aboutsummaryrefslogtreecommitdiff
path: root/sources/samples
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2017-01-20 00:55:51 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2017-01-20 00:55:51 +0100
commitc63493c899de714b05b0521bb38aab60d19030ef (patch)
treefcb2b261afa0f3c2bd6b48929b64724c71192bae /sources/samples
downloadludivine-ckeditor-component-c63493c899de714b05b0521bb38aab60d19030ef.tar.gz
ludivine-ckeditor-component-c63493c899de714b05b0521bb38aab60d19030ef.tar.zst
ludivine-ckeditor-component-c63493c899de714b05b0521bb38aab60d19030ef.zip
Validation initiale4.6.2.1
Diffstat (limited to 'sources/samples')
-rw-r--r--sources/samples/css/samples.css1632
-rw-r--r--sources/samples/img/github-top.pngbin0 -> 383 bytes
-rw-r--r--sources/samples/img/header-bg.pngbin0 -> 13086 bytes
-rw-r--r--sources/samples/img/header-separator.pngbin0 -> 123 bytes
-rw-r--r--sources/samples/img/logo.pngbin0 -> 5891 bytes
-rw-r--r--sources/samples/img/navigation-tip.pngbin0 -> 12029 bytes
-rw-r--r--sources/samples/index.html128
-rw-r--r--sources/samples/js/sample.js54
-rw-r--r--sources/samples/js/sf.js673
-rw-r--r--sources/samples/old/ajax.html85
-rw-r--r--sources/samples/old/api.html210
-rw-r--r--sources/samples/old/appendto.html59
-rw-r--r--sources/samples/old/assets/inlineall/logo.pngbin0 -> 4283 bytes
-rw-r--r--sources/samples/old/assets/outputxhtml/outputxhtml.css204
-rw-r--r--sources/samples/old/assets/posteddata.php59
-rw-r--r--sources/samples/old/assets/sample.jpgbin0 -> 14449 bytes
-rw-r--r--sources/samples/old/assets/uilanguages/languages.js92
-rw-r--r--sources/samples/old/datafiltering.html508
-rw-r--r--sources/samples/old/divreplace.html144
-rw-r--r--sources/samples/old/index.html111
-rw-r--r--sources/samples/old/inlineall.html314
-rw-r--r--sources/samples/old/inlinebycode.html124
-rw-r--r--sources/samples/old/inlinetextarea.html113
-rw-r--r--sources/samples/old/jquery.html103
-rw-r--r--sources/samples/old/readonly.html76
-rw-r--r--sources/samples/old/replacebyclass.html60
-rw-r--r--sources/samples/old/replacebycode.html59
-rw-r--r--sources/samples/old/sample.css357
-rw-r--r--sources/samples/old/sample.js51
-rw-r--r--sources/samples/old/sample_posteddata.php16
-rw-r--r--sources/samples/old/tabindex.html78
-rw-r--r--sources/samples/old/uicolor.html72
-rw-r--r--sources/samples/old/uilanguages.html122
-rw-r--r--sources/samples/old/xhtmlstyle.html234
-rw-r--r--sources/samples/toolbarconfigurator/bender.js52
-rw-r--r--sources/samples/toolbarconfigurator/css/fontello.css55
-rw-r--r--sources/samples/toolbarconfigurator/font/LICENSE.txt12
-rw-r--r--sources/samples/toolbarconfigurator/font/config.json28
-rw-r--r--sources/samples/toolbarconfigurator/font/fontello.eotbin0 -> 4988 bytes
-rw-r--r--sources/samples/toolbarconfigurator/font/fontello.svg14
-rw-r--r--sources/samples/toolbarconfigurator/font/fontello.ttfbin0 -> 4820 bytes
-rw-r--r--sources/samples/toolbarconfigurator/font/fontello.woffbin0 -> 2904 bytes
-rw-r--r--sources/samples/toolbarconfigurator/index.html446
-rw-r--r--sources/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js566
-rw-r--r--sources/samples/toolbarconfigurator/js/fulltoolbareditor.js365
-rw-r--r--sources/samples/toolbarconfigurator/js/toolbarmodifier.js1366
-rw-r--r--sources/samples/toolbarconfigurator/js/toolbartextmodifier.js623
-rw-r--r--sources/samples/toolbarconfigurator/less/base.less38
-rw-r--r--sources/samples/toolbarconfigurator/less/toolbarmodifier.less508
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/LICENSE19
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/README.md12
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/codemirror.css325
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/codemirror.js8738
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/javascript.js701
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/neo.css36
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/show-hint.css38
-rw-r--r--sources/samples/toolbarconfigurator/lib/codemirror/show-hint.js392
-rw-r--r--sources/samples/toolbarconfigurator/package.json12
-rw-r--r--sources/samples/toolbarconfigurator/tests/one.js9
59 files changed, 20093 insertions, 0 deletions
diff --git a/sources/samples/css/samples.css b/sources/samples/css/samples.css
new file mode 100644
index 0000000..455b152
--- /dev/null
+++ b/sources/samples/css/samples.css
@@ -0,0 +1,1632 @@
1/**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5@media (max-width: 900px) {
6 .global-is-mobile-hidden {
7 display: none !important;
8 }
9}
10article,
11aside,
12details,
13figcaption,
14figure,
15footer,
16header,
17hgroup,
18main,
19menu,
20nav,
21section {
22 display: block;
23}
24body,
25html {
26 margin: 0;
27 padding: 0;
28 font: 16px / 1.8 Arial, 'Helvetica Neue', Helvetica, sans-serif;
29 font-weight: 300;
30 color: #575757;
31}
32.grid-width-10 {
33 width: 10%;
34}
35.grid-width-20 {
36 width: 20%;
37}
38.grid-width-30 {
39 width: 30%;
40}
41.grid-width-40 {
42 width: 40%;
43}
44.grid-width-50 {
45 width: 50%;
46}
47.grid-width-60 {
48 width: 60%;
49}
50.grid-width-70 {
51 width: 70%;
52}
53.grid-width-80 {
54 width: 80%;
55}
56.grid-width-90 {
57 width: 90%;
58}
59.grid-width-100 {
60 width: 100%;
61}
62@media (max-width: 900px) {
63 .grid-width-10,
64 .grid-width-20,
65 .grid-width-30,
66 .grid-width-40,
67 .grid-width-50,
68 .grid-width-60,
69 .grid-width-70,
70 .grid-width-80,
71 .grid-width-90,
72 .grid-width-100 {
73 width: 100%;
74 }
75}
76*[class*="grid-width"] {
77 -webkit-box-sizing: border-box;
78 -moz-box-sizing: border-box;
79 box-sizing: border-box;
80 padding-left: 4%;
81 padding-right: 4%;
82 float: left;
83}
84*[class*="grid-width"]:after,
85.grid-container:after,
86*[class*="grid-width"]:before,
87.grid-container:before {
88 content: '';
89 display: block;
90 overflow: hidden;
91 visibility: hidden;
92 font-size: 0;
93 line-height: 0;
94 width: 0;
95 height: 0;
96}
97*[class*="grid-width"]:after,
98.grid-container:after {
99 clear: both;
100}
101.grid-container {
102 -webkit-box-sizing: border-box;
103 -moz-box-sizing: border-box;
104 box-sizing: border-box;
105 margin-left: auto;
106 margin-right: auto;
107}
108.grid-container-nested *[class*="grid-width"]:first-child {
109 padding-left: 0;
110}
111.grid-container-nested *[class*="grid-width"]:last-child {
112 padding-right: 0;
113}
114@media (max-width: 900px) {
115 .grid-container-nested *[class*="grid-width"]:first-child {
116 padding-left: 4%;
117 }
118 .grid-container-nested *[class*="grid-width"]:last-child {
119 padding-right: 4%;
120 }
121}
122.header-a {
123 min-height: 140px;
124 overflow: hidden;
125}
126.header-a .header-a-logo {
127 margin: 40px 0 0;
128}
129@media (max-width: 900px) {
130 .header-a .header-a-logo {
131 text-align: center;
132 }
133}
134.header-a .header-a-logo img {
135 border: transparent;
136}
137.navigation-a {
138 height: 30px;
139 background: #3D3D3D;
140 position: absolute;
141 left: 0;
142 right: 0;
143 top: 0;
144 padding: 0;
145 overflow: hidden;
146}
147@media (max-width: 900px) {
148 .navigation-a {
149 text-align: center;
150 }
151}
152.navigation-a ul {
153 list-style: none;
154 margin: 0;
155 overflow: hidden;
156}
157.navigation-a ul li,
158.navigation-a ul li a {
159 display: inline-block;
160}
161@media (max-width: 900px) {
162 .navigation-a ul {
163 width: auto;
164 text-overflow: ellipsis;
165 white-space: nowrap;
166 display: inline-block;
167 float: none;
168 }
169 .navigation-a ul:before,
170 .navigation-a ul:after {
171 display: none;
172 }
173}
174.navigation-a ul.navigation-a-left {
175 text-align: left;
176}
177@media (max-width: 900px) {
178 .navigation-a ul.navigation-a-left {
179 padding-right: 0;
180 }
181}
182.navigation-a ul.navigation-a-right {
183 text-align: right;
184}
185@media (max-width: 900px) {
186 .navigation-a ul.navigation-a-right {
187 padding-left: 23px;
188 }
189}
190.navigation-a ul li + li {
191 margin-left: 23px;
192}
193.navigation-a ul li a {
194 font-size: 10px;
195 font-size: 0.625rem;
196 line-height: 18px;
197 line-height: 1.13rem;
198 line-height: 30px;
199 float: left;
200 color: #ddd;
201 font-weight: bold;
202 text-decoration: none;
203 text-transform: uppercase;
204}
205.navigation-a ul li a:hover {
206 cursor: pointer;
207 color: #fff;
208}
209.icon-navigation-a-github:before,
210.icon-navigation-a-github:after {
211 background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAAXNSR0IArs4c6QAAAa9JREFUOBGNlM8rRGEUht0pDGosjKYZpUSIkuwsiCaxUEqK2VOUBcrWv2BjxUJho6wsLLDzY2fhD5iR5NeOcJvIjOfM3O927m3mmlPPnPec835nZprvjlVVJvL5fCOjMWiDCLzCLVxZlpUj/x8saYV9+IZS8UJzFWoCt2GYgk+oJG4wJUouZDANv5VsUZ47dNSzkEYHfIDEHixDWgoiB/rTHlPPwBNInPmXHRb7hdeUDFG10AN1Th1Fd5mD6BMwMVnoUyVA3t3EkjkQlDFfmwPkc7NsQTXf0bGgJWaGb16dk18+EmLYawzkC+6Q3KdK4kiZqtGdskx/kmdlCJS86RuGrDLFZJmtGi1KB0q+VhOGsDLZsiyjGsOY4qoOkrO+YUauwCDoOKWo9xk9JfM+MPdSzqZdA8UlyDO3AvKLPsIG9LsmBHUKduEHdCy6PrpJZyKXdwKMOemaissOHJ9O9xTeh57GluMYIsehWy8STW/d8ZhkI0b9PjFasA1fsAOb0KCN1PLXYyKLGNdzj2YYArnZDyDRrA3Ua4UuDzd5QM/KaoxhmAO5Om5Qt8OI2/CJP6MVa1dvltQ5AAAAAElFTkSuQmCC");
212}
213.navigation-b {
214 text-align: right;
215 margin: 52px 0 0;
216 overflow: visible;
217}
218@media (max-width: 900px) {
219 .navigation-b {
220 text-align: center;
221 margin-top: 20px;
222 padding: 0;
223 }
224}
225.navigation-b ul {
226 padding: 0;
227 list-style: none;
228 margin: 0;
229 overflow: visible;
230}
231.navigation-b ul li,
232.navigation-b ul li a {
233 display: inline-block;
234}
235@media (max-width: 900px) {
236 .navigation-b ul {
237 display: table;
238 width: 100%;
239 padding-bottom: 1.5em;
240 }
241}
242@media (max-width: 900px) {
243 .navigation-b ul li {
244 display: table-row;
245 }
246}
247.navigation-b ul li + li {
248 margin-left: 20px;
249}
250@media (max-width: 900px) {
251 .navigation-b ul li + li {
252 margin-left: 0;
253 }
254}
255.navigation-b ul li a {
256 -webkit-box-sizing: border-box;
257 -moz-box-sizing: border-box;
258 box-sizing: border-box;
259 text-transform: uppercase;
260 text-decoration: none;
261 outline: none;
262}
263@media (max-width: 900px) {
264 .navigation-b ul li a {
265 width: 100%;
266 -webkit-border-radius: 0;
267 -webkit-background-clip: padding-box;
268 -moz-border-radius: 0;
269 -moz-background-clip: padding;
270 border-radius: 0;
271 background-clip: padding-box;
272 }
273}
274.footer-a {
275 font-size: 13px;
276 font-size: 0.8125rem;
277 line-height: 23.4px;
278 line-height: 1.46rem;
279 padding-top: 2.25em;
280 padding-bottom: 2.25em;
281 overflow: hidden;
282 color: #8a8a8a;
283}
284.footer-a a {
285 color: #27C0D8;
286 text-decoration: none;
287 border-bottom: 1px dotted #27C0D8;
288}
289.footer-a a:hover {
290 color: #23adc2;
291}
292.footer-a p {
293 margin: 0;
294 display: inline-block;
295 text-align: center;
296}
297.content {
298 font-size: 14px;
299 font-size: 0.875rem;
300 line-height: 25.2px;
301 line-height: 1.57rem;
302 overflow: hidden;
303 padding-top: 1.5em;
304 padding-bottom: 1.5em;
305}
306.content p {
307 margin: 0.75em 0;
308}
309.content ul,
310.content ol,
311.content pre,
312.content blockquote,
313.content textarea:not([class^="cke"]),
314.content .cke {
315 margin: 1.875em 0;
316}
317.content code,
318.content kbd {
319 -webkit-border-radius: 3px;
320 -webkit-background-clip: padding-box;
321 -moz-border-radius: 3px;
322 -moz-background-clip: padding;
323 border-radius: 3px;
324 background-clip: padding-box;
325 padding: 3px 4px;
326}
327.content pre,
328.content code,
329.content kbd,
330.content blockquote {
331 background: #f5f5f5;
332}
333.content blockquote,
334.content pre {
335 background: none;
336 border-left: 4px solid #27C0D8;
337 padding: 1.5em 2.25em;
338}
339.content p a,
340.content ul a,
341.content ol a,
342.content blockquote a,
343.content h1 a,
344.content h2 a,
345.content h3 a,
346.content h4 a,
347.content h5 a {
348 color: #27C0D8;
349 text-decoration: none;
350 border-bottom: 1px dotted #27C0D8;
351}
352.content p a:hover,
353.content ul a:hover,
354.content ol a:hover,
355.content blockquote a:hover,
356.content h1 a:hover,
357.content h2 a:hover,
358.content h3 a:hover,
359.content h4 a:hover,
360.content h5 a:hover {
361 color: #23adc2;
362}
363.content h1,
364.content h2,
365.content h3,
366.content h4,
367.content h5 {
368 color: #000;
369 font-weight: 100;
370}
371.content h1 code,
372.content h2 code,
373.content h3 code,
374.content h4 code,
375.content h5 code,
376.content h1 kbd,
377.content h2 kbd,
378.content h3 kbd,
379.content h4 kbd,
380.content h5 kbd {
381 font-size: inherit;
382}
383.content h1 a.content-heading-anchor,
384.content h2 a.content-heading-anchor,
385.content h3 a.content-heading-anchor,
386.content h4 a.content-heading-anchor,
387.content h5 a.content-heading-anchor {
388 font-weight: 100;
389 vertical-align: middle;
390 opacity: 0;
391 border: 0;
392}
393.content h1:hover a.content-heading-anchor,
394.content h2:hover a.content-heading-anchor,
395.content h3:hover a.content-heading-anchor,
396.content h4:hover a.content-heading-anchor,
397.content h5:hover a.content-heading-anchor {
398 opacity: 1;
399}
400.content h1:target a,
401.content h2:target a,
402.content h3:target a,
403.content h4:target a,
404.content h5:target a {
405 -webkit-animation: targetLinkOpacity 0.5s linear alternate;
406 -moz-animation: targetLinkOpacity 0.5s linear alternate;
407 -o-animation: targetLinkOpacity 0.5s linear alternate;
408 animation: targetLinkOpacity 0.5s linear alternate;
409 opacity: 1;
410}
411.content input,
412.content select,
413.content textarea:not([class^="cke"]) {
414 -webkit-border-radius: 3px;
415 -webkit-background-clip: padding-box;
416 -moz-border-radius: 3px;
417 -moz-background-clip: padding;
418 border-radius: 3px;
419 background-clip: padding-box;
420 -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.08);
421 -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.08);
422 box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.08);
423 font: inherit;
424 color: inherit;
425 border: 1px solid #D9D9D9;
426 padding: .2em .5em;
427}
428.content input:focus,
429.content select:focus,
430.content textarea:not([class^="cke"]):focus {
431 border-color: #66afe9;
432 outline: 0;
433 -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.08), 0 0 8px #93c6ef;
434 -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.08), 0 0 8px #93c6ef;
435 box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.08), 0 0 8px #93c6ef;
436}
437.content abbr {
438 border-bottom: 1px dotted #666;
439 cursor: pointer;
440}
441.content blockquote {
442 font-style: italic;
443 font-family: Georgia, Times, "Times New Roman", serif;
444 font-size: 16px;
445 font-size: 1rem;
446 line-height: 28.8px;
447 line-height: 1.8rem;
448}
449.content em {
450 font-style: italic;
451}
452.content h1 {
453 font-size: 36px;
454 font-size: 2.25rem;
455 line-height: 64.8px;
456 line-height: 4.05rem;
457 margin: 1.125em 0 0;
458}
459.content h2 {
460 font-size: 27.2px;
461 font-size: 1.7rem;
462 line-height: 48.96px;
463 line-height: 3.06rem;
464 margin: 0.9em 0 0;
465}
466.content h3 {
467 font-size: 24px;
468 font-size: 1.5rem;
469 line-height: 43.2px;
470 line-height: 2.7rem;
471 font-weight: 500;
472 margin: 0.75em 0 0;
473}
474.content h4 {
475 font-size: 19.2px;
476 font-size: 1.2rem;
477 line-height: 34.56px;
478 line-height: 2.16rem;
479 font-weight: 500;
480 margin: 0.75em 0 0;
481}
482.content h5 {
483 font-size: 17.6px;
484 font-size: 1.1rem;
485 line-height: 31.68px;
486 line-height: 1.98rem;
487 font-weight: 500;
488 margin: 0.75em 0 0;
489}
490.content hr {
491 border: 0;
492 border-top: 4px solid #D9D9D9;
493 margin: 1.5em 0;
494}
495.content input[type="text"] {
496 height: 1.8em;
497 line-height: 1.8em;
498}
499.content input[type="button"] {
500 -webkit-appearance: button;
501 -moz-appearance: button;
502 appearance: button;
503}
504.content kbd {
505 font-size: 12px;
506 font-size: 0.75rem;
507 line-height: 21.6px;
508 line-height: 1.35rem;
509 font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
510 padding: 2px 6px;
511 -webkit-box-shadow: 0 0 4px #fff inset, 0 2px 0 #D9D9D9;
512 -moz-box-shadow: 0 0 4px #fff inset, 0 2px 0 #D9D9D9;
513 box-shadow: 0 0 4px #fff inset, 0 2px 0 #D9D9D9;
514}
515.content p img {
516 vertical-align: middle;
517}
518.content p pre {
519 padding: 1.5em;
520}
521.content pre {
522 padding: 0;
523 border: 0;
524 tab-size: 4;
525 -o-tab-size: 4;
526 -moz-tab-size: 4;
527}
528.content pre,
529.content code {
530 font-size: 11.89px;
531 font-size: 0.743rem;
532 line-height: 21.4px;
533 line-height: 1.34rem;
534 font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
535}
536.content pre a,
537.content code a {
538 border: 0;
539}
540.content pre code {
541 padding: 0.75em;
542 display: block;
543}
544.content strong {
545 color: #000;
546}
547.content ul ul,
548.content ol ul,
549.content ul ol,
550.content ol ol {
551 margin: 0.75em 0;
552}
553.content ul li,
554.content ol li {
555 font-size: 14px;
556 font-size: 0.875rem;
557 line-height: 30.24px;
558 line-height: 1.89rem;
559}
560.content textarea:not([class^="cke"]) {
561 width: 100%;
562}
563.content div.todo {
564 border: 2px dotted #444;
565 padding: 10px;
566 margin: 60px 0 10px 0;
567 /* Remove me some day */
568}
569.content div.todo:before {
570 content: "TODO";
571 font-weight: bold;
572}
573body a.button-a,
574body button.button-a,
575body input.button-a {
576 -webkit-border-radius: 3px;
577 -webkit-background-clip: padding-box;
578 -moz-border-radius: 3px;
579 -moz-background-clip: padding;
580 border-radius: 3px;
581 background-clip: padding-box;
582 font-size: 14px;
583 font-size: 0.875rem;
584 line-height: 25.2px;
585 line-height: 1.57rem;
586 height: 36px;
587 line-height: 36px;
588 padding: 0 1.1em;
589 font-weight: 700;
590 color: #3e3e3e;
591 white-space: nowrap;
592 text-decoration: none;
593 display: inline-block;
594 cursor: pointer;
595 border: 0;
596 vertical-align: middle;
597 margin: 1px 0;
598 background: transparent;
599}
600body a.button-a.icon-pos-left,
601body button.button-a.icon-pos-left,
602body input.button-a.icon-pos-left {
603 padding-left: .8em;
604}
605body a.button-a.icon-pos-right,
606body button.button-a.icon-pos-right,
607body input.button-a.icon-pos-right {
608 padding-right: .8em;
609}
610body a.button-a.button-a-no-text,
611body button.button-a.button-a-no-text,
612body input.button-a.button-a-no-text {
613 -webkit-border-radius: 100px;
614 -webkit-background-clip: padding-box;
615 -moz-border-radius: 100px;
616 -moz-background-clip: padding;
617 border-radius: 100px;
618 background-clip: padding-box;
619 width: 36px;
620 padding: 0;
621 text-indent: -999px;
622 overflow: hidden;
623 position: relative;
624 text-align: center;
625}
626body a.button-a.button-a-no-text:before,
627body button.button-a.button-a-no-text:before,
628body input.button-a.button-a-no-text:before {
629 position: absolute;
630 left: 50%;
631 top: 50%;
632 margin: -9px 0 0 -9px;
633}
634@media (max-width: 900px) {
635 body a.button-a.button-a-mobile-collapsed,
636 body button.button-a.button-a-mobile-collapsed,
637 body input.button-a.button-a-mobile-collapsed {
638 -webkit-border-radius: 100px;
639 -webkit-background-clip: padding-box;
640 -moz-border-radius: 100px;
641 -moz-background-clip: padding;
642 border-radius: 100px;
643 background-clip: padding-box;
644 width: 36px;
645 padding: 0;
646 text-indent: -999px;
647 overflow: hidden;
648 position: relative;
649 text-align: center;
650 }
651 body a.button-a.button-a-mobile-collapsed:before,
652 body button.button-a.button-a-mobile-collapsed:before,
653 body input.button-a.button-a-mobile-collapsed:before {
654 position: absolute;
655 left: 50%;
656 top: 50%;
657 margin: -9px 0 0 -9px;
658 }
659 body a.button-a.button-a-mobile-collapsed:before,
660 body button.button-a.button-a-mobile-collapsed:before,
661 body input.button-a.button-a-mobile-collapsed:before {
662 position: absolute;
663 left: 50%;
664 top: 50%;
665 margin: -9px 0 0 -9px;
666 }
667}
668body a.button-a:active,
669body button.button-a:active,
670body input.button-a:active,
671body a.button-a:hover,
672body button.button-a:hover,
673body input.button-a:hover {
674 color: #fff;
675 background: #23adc2;
676}
677body a.button-a:focus,
678body button.button-a:focus,
679body input.button-a:focus {
680 border-color: #66afe9;
681 outline: 0;
682 -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px #93c6ef;
683 -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px #93c6ef;
684 box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px #93c6ef;
685}
686body a.button-a-soft,
687body button.button-a-soft,
688body input.button-a-soft {
689 background: #e7e7e7;
690}
691body a.button-a-soft:active,
692body button.button-a-soft:active,
693body input.button-a-soft:active,
694body a.button-a-soft:hover,
695body button.button-a-soft:hover,
696body input.button-a-soft:hover {
697 color: #3e3e3e;
698 background: #cecece;
699}
700body a.button-a-background,
701body button.button-a-background,
702body input.button-a-background,
703body a.navigation-b ul li a:hover,
704body button.navigation-b ul li a:hover,
705body input.navigation-b ul li a:hover {
706 color: #fff;
707 background: #27C0D8;
708}
709body a.button-a-background:active,
710body button.button-a-background:active,
711body input.button-a-background:active,
712body a.button-a-background:hover,
713body button.button-a-background:hover,
714body input.button-a-background:hover,
715body a.navigation-b ul li a:hover:active,
716body button.navigation-b ul li a:hover:active,
717body input.navigation-b ul li a:hover:active,
718body a.navigation-b ul li a:hover:hover,
719body button.navigation-b ul li a:hover:hover,
720body input.navigation-b ul li a:hover:hover {
721 color: #fff;
722 background: #23adc2;
723}
724.balloon-a {
725 font-size: 12px;
726 font-size: 0.75rem;
727 line-height: 21.6px;
728 line-height: 1.35rem;
729 -webkit-border-radius: 3px;
730 -webkit-background-clip: padding-box;
731 -moz-border-radius: 3px;
732 -moz-background-clip: padding;
733 border-radius: 3px;
734 background-clip: padding-box;
735 border-bottom: 3px solid #d4d4d4;
736 background: #ebebeb;
737 display: inline-block;
738 white-space: nowrap;
739 padding: .4em 1.2em .2em;
740 font-weight: 700;
741 position: relative;
742 z-index: 1000;
743 text-transform: none;
744 color: #575757;
745}
746.balloon-a:hover {
747 color: #575757;
748}
749.balloon-a:before {
750 content: '';
751 width: 0;
752 height: 0;
753 border-style: solid;
754 position: absolute;
755}
756.balloon-a-ne:before,
757.balloon-a-nw:before {
758 top: -13px;
759 border-width: 0 9px 15.6px 9px;
760 border-color: transparent transparent #ebebeb transparent;
761}
762.balloon-a-se:before,
763.balloon-a-sw:before {
764 bottom: -13px;
765 border-width: 15.6px 9px 0 9px;
766 border-color: #ebebeb transparent transparent transparent;
767}
768.balloon-a-nw:before,
769.balloon-a-sw:before {
770 left: 20px;
771}
772.balloon-a-ne:before,
773.balloon-a-se:before {
774 right: 20px;
775}
776.icon-pos-left:before,
777.icon-pos-right:after {
778 content: '';
779 display: inline-block;
780 width: 18px;
781 height: 18px;
782 vertical-align: middle;
783 background-repeat: no-repeat;
784}
785.icon-pos-left:before {
786 margin-right: 10px;
787}
788.icon-pos-right:after {
789 margin-left: 10px;
790}
791.icon-download:before,
792.icon-download:after {
793 background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAQFJREFUOBGtVDESgjAQBGfobHwE/AIa/AN/8EEWfMWGZ+gDaG2ws8BdyY13SRgGcGducre3WQ5NSJIIxnGsES3ijhhcMCdXR7ZYCqIc0SGWQE1ud7sKjRLxXHJQfWpLYwaCk6wxET/u+U2GIngd8yRViINau28bBH/YAGqvSQPhRNQHqBqj3FY0NKq27TW7qhSTDaCOhkaRAj7Hmm8S4V+c6C+gUa+crsizuWmoc70MKbWCnqPy2GvcUJxE4a/sIajRaGkU+/sf4IuISQGePR/T/QMbHEhwPLVnMWPuOCwGnWg41dwVeaN3ccHch70idIRi/6WV0WC2/zMiZm661R+2DxyEdjTuST3mAAAAAElFTkSuQmCC");
794}
795.icon-question-mark:before,
796.icon-question-mark:after {
797 background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAUhJREFUOBGllLFOAkEQhjk0WthT2JFA7Czsqc7OxFLewEeAZ/AVbO0tTLTSBKhstTBUNkYLEoVAbD2//zILe5e9uwCT/JnZmX/+m83ebq0WsCRJYnANxmBhUKxcHGjJpiC1wQBUmTjtbLetKHTAT5WCVxe3kxEjoUmKRL6pvYEZyJt6VpOxCG3nmfyx+yJxBM7BFPg2SDlkTv2sxZqi4YnUvfgswI9FuHAkzz9EUTTRmqYeTifXsvoj/s9i57oi6ljz9kviFdyBCbgHe+rCn4C8jVXQ18rshuKOiTSIXwLkRZWQTurARJrE7wERpea7kD7BkcgB+yB3CFGlPmgqCNiXhEagSGif2qU1Ln8FW/tupK3pXhXZrWNDuCoikY/rHPMT5KFr2MAPTSM90rIrUjJIeq1WV0RTwN7+0rrtILb9M+LEbLq1H7Z/Ea3+RvBddl0AAAAASUVORK5CYII=");
798}
799.icon-close:before,
800.icon-close:after {
801 background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAUlJREFUOBGllDFuwzAMRe3Cd+jYKUCzd/XkDtm9dsoVOuUqBnqBoodwgBwiW8ZsXTIWqPu+Iia0LMAoTOBbJEV+UZTkosjIMAwN6MARXCKky9dkUsYuglagB3OimNU4O1pM1OB7jsHNK7YekeFQJZ5kj/0LcnLA+RMnlHOvDMNv5wO7BFuQkn3hq0ALjKwPVeF4BSaqpLRy0T1ZIHFz75bE2BR8dBImqmBrwRplg09QmR/9GZyBSadAHauXCZkRROKURLlHEemepJIlIyhHotzLg1/N6erTxtmmvqA8muHGIbc1rTBqrEuwnqWnGbbmmz0hwaHtvM2QhWbrXZnosvnTWWPrdCY9w7cDJtf3h9VHjy5Zq9UZ08beyJh7Aicg6W/VYvgnIjJdNn9PMIOITJWcgnV9VvcnEitY/mitNFZZ/hsxsljdv39sfybRQ4R/kU0MAAAAAElFTkSuQmCC");
802}
803.ie8 .switch > * {
804 vertical-align: middle;
805}
806.ie8 .switch input[type="radio"] {
807 margin: 0 0.25em;
808 display: inline-block;
809}
810.ie8 .switch label {
811 margin-left: 0 !important;
812 margin-right: 0 !important;
813}
814.ie8 .switch label[data-for="1"] {
815 float: left;
816}
817.ie8 .switch label[data-for="2"] {
818 float: right;
819}
820.ie8 .switch .switch-inner {
821 display: none;
822}
823.switch {
824 font-size: 14px;
825 font-size: 0.875rem;
826 line-height: 25.2px;
827 line-height: 1.57rem;
828 font-weight: bold;
829 background-color: #27C0D8;
830 overflow: hidden;
831 display: inline-block;
832 padding: 0.75em 0.25em;
833 color: #fff;
834 -webkit-border-radius: 3px;
835 -webkit-background-clip: padding-box;
836 -moz-border-radius: 3px;
837 -moz-background-clip: padding;
838 border-radius: 3px;
839 background-clip: padding-box;
840 position: relative;
841}
842.switch input[type="radio"] {
843 display: none;
844}
845.switch label {
846 position: relative;
847 z-index: 2;
848 float: left;
849 cursor: pointer;
850 padding: 0 0.75em;
851}
852.switch label:hover {
853 text-decoration: underline;
854}
855.switch .switch-inner {
856 float: left;
857 background-color: #FFF;
858 height: 1.5em;
859 width: 4.125em;
860 padding: 2px;
861 margin: 0 0.25em;
862 -webkit-border-radius: 5.5px;
863 -webkit-background-clip: padding-box;
864 -moz-border-radius: 5.5px;
865 -moz-background-clip: padding;
866 border-radius: 5.5px;
867 background-clip: padding-box;
868}
869.switch .switch-inner .handler {
870 overflow: hidden;
871 position: relative;
872 display: block;
873 height: 1.5em;
874 width: 1.5em;
875 background: #25b4cb;
876 -webkit-border-radius: 4.5px;
877 -webkit-background-clip: padding-box;
878 -moz-border-radius: 4.5px;
879 -moz-background-clip: padding;
880 border-radius: 4.5px;
881 background-clip: padding-box;
882}
883.switch .switch-inner .handler:before {
884 content: '';
885 display: block;
886 position: absolute;
887 top: 0;
888 right: 0;
889 bottom: 3px;
890 left: 0;
891 background-color: #34c4da;
892 -webkit-border-bottom-left-radius: 4.5px;
893 -moz-border-radius-bottomleft: 4.5px;
894 border-bottom-left-radius: 4.5px;
895 -webkit-border-bottom-right-radius: 4.5px;
896 -webkit-background-clip: padding-box;
897 -moz-border-radius-bottomright: 4.5px;
898 -moz-background-clip: padding;
899 border-bottom-right-radius: 4.5px;
900 background-clip: padding-box;
901}
902.switch:hover .switch-inner .handler:before {
903 background: #45c9dd;
904}
905.switch input[data-num="2"]:checked ~ .switch-inner > .handler {
906 margin-left: auto;
907}
908.switch input[data-num="2"]:checked ~ label[data-for="1"] {
909 padding-right: 5.125em;
910 margin-right: -4.375em;
911}
912.switch input[data-num="1"]:checked ~ label[data-for="2"] {
913 padding-left: 5.125em;
914 margin-left: -4.375em;
915}
916.toggler {
917 -webkit-user-select: none;
918 -moz-user-select: none;
919 -ms-user-select: none;
920 user-select: none;
921}
922.toggler label {
923 cursor: pointer;
924}
925.toggler [data-collapse] {
926 display: inherit;
927}
928.toggler [data-expand] {
929 display: none;
930}
931.toggler.collapsed [data-collapse] {
932 display: none;
933}
934.toggler.collapsed [data-expand] {
935 display: inherit;
936}
937.toggler-container {
938 overflow: hidden;
939}
940.toggler-container.collapsed {
941 height: 0;
942}
943.icon-toggler-expanded:before,
944.icon-toggler-collapsed:before,
945.icon-toggler-expanded:after,
946.icon-toggler-collapsed:after {
947 background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAByCAYAAABeOoENAAAAAXNSR0IArs4c6QAAAbxJREFUaAXtmT1KBEEQhRdFQdBEMfQEBoaGopl3MfECXsFERLyBh/AUIuwJDEUQM//eB11Dz1A1uzotGFTBY2rr58306+kNpmazP7Z98V8Kj8JrAT4xcgttXRVXwofwFYAcNdS6RuJegOBTuBUOhc0CfGLkqKHWJeMuFDwJJ0Jk5Kihlp6esW4embuNkVgTNdTS09MMEbkDj76sUUsPvZ2xIwTRATsQuBuxGsTIYdSSo7cztpggwprdyKlJ8ImZUUuM3s48ol1lXwQjwydm5hINl2bF53KMCL82d2mR2GvqnBfg1+aKPbb9p+oGtYXbT1GTFxKiZkfEyHgy7x0y0clR454zSGpDMzaA3fzV30hNln4qkAqkAqlAKpAKpAKpQCqQCqQCqUAqkAqkAqlAKpAKpAKpQCrw3xWY/GGcz++TP9U3Gx40GWdEAxabXA33NBywRCOfdzFcCztDJv12Rz7REMpmIc9qPBNWK0J3COWNxegxIrs+KHZcyHpjsZUSXPaypcLtseJFS3tT84WwUZG4S4vEZkl3wl5FYK4rdrT9R9Y1uIbbT12TFxKiZkfEyCYfWojMJv+NGNGPr99GI9DP7P9TCgAAAABJRU5ErkJggg==");
948}
949.icon-toggler-expanded.icon-light:before,
950.icon-toggler-collapsed.icon-light:before,
951.icon-toggler-expanded.icon-light:after,
952.icon-toggler-collapsed.icon-light:after {
953 background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAByCAYAAABeOoENAAAAAXNSR0IArs4c6QAAAcVJREFUaAXtmT9KA1EQxhMlASE2SkpPkCJlStHOu3gGwRPYBAm5gYfwFBKwtrARRAh26vr7ljfx7TrLChtBcAa+zOSbPy/7vcTC6fV+04qimIArsALrBMXiJq1nUzQEc/AOmkw51QzdgUqAWyD7AEswA6MExeKUk6n2+zBInSJ7BKfuaZDKpRpcMa/UQUgTfWSd1jjEmlSTatXzpRlvJKJsacVtXrVlB72bWgjdiGwmEj8FOq1u4qapRprJVvkgXbFsZCTxomSqL4ssr0uQrY3TJ/AGjeFfVJlM8diaiCuDdlLiIfmNcP1+/wnu0hoVJ84oq7XeUhNXbE4dgPuEgU2Qh3PFbrx+Gs6E2hD/+tMJ3b+QadB2fiLZsG4/2poG3f6M5MMiDgVCgVAgFAgFQoFQIBQIBUKBUCAUCAVCgVAgFAgFQoFQIBQIBf66AiwLuv1jnAH/Zb/Go5abq/qdwvsLFhJNK583ctfg0Bnmrnwq+zVrYoDZM8E52M1yP9uvqcGmZP6O+CTl3LWYHdTm9yk4aCzilLZHe6XmAuzZEGL30ZrEpr64AUc2wDycK7a7X6P42BpzD+9fv4pIxn4tWznnwm0r/gQpiG1tFshTowAAAABJRU5ErkJggg==");
954}
955.icon-toggler-expanded:before,
956.icon-toggler-expanded:after {
957 background-position: top left;
958}
959.icon-toggler-collapsed:before,
960.icon-toggler-collapsed:after {
961 background-position: bottom left;
962}
963.modal {
964 padding: 20px;
965 border-radius: 3px;
966 background-color: white;
967 max-width: 700px;
968 -webkit-box-sizing: border-box;
969 -moz-box-sizing: border-box;
970 box-sizing: border-box;
971 width: 80% !important;
972 top: 50% !important;
973 -webkit-transform: translate(-50%, -50%) !important;
974 -moz-transform: translate(-50%, -50%) !important;
975 -o-transform: translate(-50%, -50%) !important;
976 -ms-transform: translate(-50%, -50%) !important;
977 transform: translate(-50%, -50%) !important;
978}
979.modal-close {
980 -webkit-border-radius: 100px;
981 -webkit-background-clip: padding-box;
982 -moz-border-radius: 100px;
983 -moz-background-clip: padding;
984 border-radius: 100px;
985 background-clip: padding-box;
986 cursor: pointer;
987 height: 18px;
988 width: 18px;
989 position: absolute;
990 top: 10px;
991 right: 10px;
992 font-size: 17px;
993 text-align: center;
994 line-height: 19px;
995 background: #cccccc;
996}
997main .grid-container,
998header .grid-container,
999.navigation-a > div,
1000footer > div {
1001 max-width: 968px;
1002}
1003.header-a {
1004 margin-top: 30px;
1005}
1006.footer-a {
1007 border-top: 1px solid #D9D9D9;
1008}
1009.adjoined-top {
1010 background-color: #27C0D8;
1011 color: #fff;
1012}
1013.adjoined-top .content h1,
1014.adjoined-top .content h2,
1015.adjoined-top .content h3,
1016.adjoined-top .content h4,
1017.adjoined-top .content h5 {
1018 color: #fff;
1019}
1020.adjoined-top .content p {
1021 font-size: 18px;
1022 font-size: 1.125rem;
1023 line-height: 32.4px;
1024 line-height: 2.02rem;
1025 font-weight: 100;
1026}
1027.adjoined-top .content p a {
1028 text-decoration: none;
1029 border-bottom: 1px dotted #fff;
1030 color: inherit;
1031}
1032.adjoined-top .content p a:hover {
1033 color: #e6e6e6;
1034}
1035.adjoined-top .content button {
1036 color: #fff;
1037}
1038.adjoined-top .content strong {
1039 color: #fff;
1040}
1041.adjoined-top .content code {
1042 font-size: inherit;
1043 color: #27C0D8;
1044}
1045.adjoined-bottom {
1046 position: relative;
1047}
1048.adjoined-bottom:before {
1049 z-index: -1;
1050 content: '';
1051 background: #27C0D8;
1052 position: absolute;
1053 top: 0;
1054 left: 0;
1055 right: 0;
1056 height: 50%;
1057}
1058main .grid-container,
1059header .grid-container,
1060.navigation-a > div,
1061footer > div {
1062 max-width: 1052px;
1063}
1064main .grid-container.freed-width {
1065 max-width: none;
1066}
1067.switch {
1068 background: #25b4cb;
1069 float: right;
1070 overflow: visible;
1071}
1072.switch .balloon-a {
1073 position: absolute;
1074 top: -40px;
1075 right: 50%;
1076 margin-right: -15px;
1077 background: #FFEFC1;
1078 border-bottom-color: #DCDCA4;
1079}
1080.switch .balloon-a:before {
1081 border-color: #FFEFC1 transparent transparent transparent;
1082}
1083#toolbar .editors-container {
1084 overflow: hidden;
1085 height: 0;
1086 transition: height 200ms;
1087}
1088#toolbar .editors-container.active {
1089 height: auto;
1090}
1091#main #editor {
1092 background: #FFF;
1093 padding: 2% 4%;
1094 border: dashed 5px #27C0D8;
1095}
1096#main .adjoined-top:before {
1097 height: 335px;
1098}
1099#toolbar .adjoined-top:before {
1100 height: 219px;
1101}
1102#toolbar .adjoined-top .grid-container-nested {
1103 height: 147px;
1104}
1105.content .grid-switch-magic {
1106 margin: 3.5em 0 0;
1107}
1108#info-box {
1109 padding-bottom: 0;
1110}
1111#info-box > div {
1112 width: 100%;
1113 text-align: right;
1114}
1115#info-box > div .toggler {
1116 padding-right: 0;
1117}
1118#info-box > div .toggler:hover {
1119 background: transparent;
1120 color: #000;
1121}
1122#info-box > div .toggler:hover > label {
1123 text-decoration: underline;
1124}
1125#info-box > div h2 {
1126 float: left;
1127 margin-top: 0;
1128}
1129#info-box > div#instructions-container {
1130 text-align: left;
1131}
1132#toolbarModifierWrapper {
1133 overflow: hidden;
1134 height: 0;
1135 opacity: 0;
1136 transition: height 200ms;
1137}
1138#toolbarModifierWrapper.active {
1139 height: auto;
1140 opacity: 1;
1141}
1142header {
1143 overflow: visible;
1144}
1145header div.grid-container {
1146 overflow: visible;
1147}
1148header .navigation-b {
1149 overflow: visible;
1150}
1151header .navigation-b ul {
1152 overflow: visible;
1153}
1154header .navigation-b a {
1155 position: relative;
1156}
1157header .balloon-a {
1158 position: absolute;
1159 top: 48px;
1160 left: 50%;
1161 margin-left: -35px;
1162}
1163@media (max-width: 1140px) {
1164 header .balloon-a {
1165 left: auto;
1166 margin-left: auto;
1167 right: 50%;
1168 margin-right: -35px;
1169 }
1170 header .balloon-a:before {
1171 left: auto;
1172 right: 22px;
1173 }
1174}
1175@media (max-width: 900px) {
1176 header .balloon-a {
1177 display: none;
1178 }
1179}
1180#toolbar .cke_toolbar {
1181 pointer-events: none;
1182 -webkit-user-select: none;
1183 -moz-user-select: none;
1184 -ms-user-select: none;
1185 user-select: none;
1186 cursor: default;
1187}
1188.some-toolbar-active .cke_toolbar {
1189 zoom: 1;
1190 filter: alpha(opacity=50);
1191 -webkit-opacity: 0.5;
1192 -moz-opacity: 0.5;
1193 opacity: 0.5;
1194}
1195.cke_toolbar.active {
1196 position: relative;
1197 zoom: 1;
1198 filter: alpha(opacity=100);
1199 -webkit-opacity: 1;
1200 -moz-opacity: 1;
1201 opacity: 1;
1202}
1203.cke_toolbar.active:after {
1204 content: '';
1205 display: block;
1206 position: absolute;
1207 top: 0;
1208 right: 6px;
1209 bottom: 5px;
1210 left: 0;
1211 -webkit-border-radius: 5px;
1212 -webkit-background-clip: padding-box;
1213 -moz-border-radius: 5px;
1214 -moz-background-clip: padding;
1215 border-radius: 5px;
1216 background-clip: padding-box;
1217 -webkit-box-shadow: 0px 0px 15px 3px #fff4b0;
1218 -moz-box-shadow: 0px 0px 15px 3px #fff4b0;
1219 box-shadow: 0px 0px 15px 3px #fff4b0;
1220}
1221.cke_toolbar.active .cke_toolgroup {
1222 -webkit-box-shadow: none;
1223 -moz-box-shadow: none;
1224 box-shadow: none;
1225 border-color: #e3c300;
1226}
1227.cke_toolbar.active .cke_combo,
1228.cke_toolbar.active .cke_toolgroup {
1229 position: relative;
1230 z-index: 2;
1231}
1232.cke_toolbar.active .cke_combo_button {
1233 -webkit-box-shadow: none;
1234 -moz-box-shadow: none;
1235 box-shadow: none;
1236}
1237.unselectable {
1238 -webkit-user-select: none;
1239 -moz-user-select: none;
1240 -ms-user-select: none;
1241 user-select: none;
1242}
1243.toolbar {
1244 padding: 5px 0;
1245 margin-bottom: 2.4em;
1246 overflow: hidden;
1247 background: #fff;
1248}
1249.toolbar button.button-a.cke_button {
1250 cursor: pointer;
1251 display: inline-block;
1252 padding: 4px 6px;
1253 outline: 0;
1254 border: 1px solid #a6a6a6;
1255}
1256.toolbar button.button-a.hidden {
1257 display: none;
1258}
1259.toolbar button.button-a.left {
1260 float: left;
1261 margin-right: 8px;
1262}
1263.toolbar button.button-a.right {
1264 float: right;
1265 margin-left: 8px;
1266}
1267.toolbar button.button-a .highlight {
1268 color: #ffefc1;
1269}
1270.configContainer.hidden,
1271.toolbarModifier.hidden,
1272.toolbarModifier-hints.hidden {
1273 display: none;
1274}
1275.toolbarModifier :focus,
1276.toolbar button:focus,
1277.configContainer textarea.configCode:focus {
1278 outline: none;
1279}
1280div.toolbarModifier {
1281 padding: 0;
1282 overflow: hidden;
1283 width: 100%;
1284 position: relative;
1285 display: table;
1286 border-collapse: collapse;
1287}
1288div.toolbarModifier ::-moz-focus-inner {
1289 border: 0;
1290}
1291div.toolbarModifier .empty {
1292 display: none;
1293}
1294div.toolbarModifier.empty-visible .empty {
1295 display: table-row;
1296 zoom: 1;
1297 filter: alpha(opacity=60);
1298 -webkit-opacity: 0.6;
1299 -moz-opacity: 0.6;
1300 opacity: 0.6;
1301}
1302div.toolbarModifier .empty > p {
1303 line-height: 31px;
1304}
1305div.toolbarModifier > ul {
1306 padding: 0;
1307 margin: 0;
1308 border-top: 1px solid #ccc;
1309 width: 100%;
1310}
1311div.toolbarModifier > ul[data-type="table-header"] {
1312 display: table-header-group;
1313}
1314div.toolbarModifier > ul[data-type="table-body"] {
1315 display: table-row-group;
1316}
1317div.toolbarModifier > ul p {
1318 padding: 0;
1319 margin: 0;
1320}
1321div.toolbarModifier > ul > li {
1322 display: table-row;
1323}
1324div.toolbarModifier > ul > li[data-type="header"] {
1325 font-weight: bold;
1326 user-select: none;
1327 cursor: default;
1328}
1329div.toolbarModifier > ul > li[data-type="group"],
1330div.toolbarModifier > ul > li[data-type="separator"] {
1331 border-bottom: 1px solid #ccc;
1332}
1333div.toolbarModifier > ul > li[data-type="subgroup"] {
1334 border-top: 1px solid #eee;
1335}
1336div.toolbarModifier > ul > li[data-type="subgroup"]:first-child {
1337 border-top: none;
1338}
1339div.toolbarModifier > ul > li[data-type="group"].active,
1340div.toolbarModifier > ul > li[data-type="group"]:hover,
1341div.toolbarModifier > ul > li[data-type="separator"].active,
1342div.toolbarModifier > ul > li[data-type="separator"]:hover {
1343 overflow: hidden;
1344 z-index: 2;
1345}
1346div.toolbarModifier > ul > li[data-type="group"].active,
1347div.toolbarModifier > ul > li[data-type="separator"].active,
1348div.toolbarModifier > ul > li[data-type="group"].active:hover,
1349div.toolbarModifier > ul > li[data-type="separator"].active:hover {
1350 background: #f0fafb;
1351}
1352div.toolbarModifier > ul > li[data-type="group"]:hover,
1353div.toolbarModifier > ul > li[data-type="separator"]:hover {
1354 background: #fffbe3;
1355}
1356div.toolbarModifier > ul > li[data-type="separator"] {
1357 background: #f5f5f5;
1358}
1359div.toolbarModifier > ul > li[data-type="separator"]:after {
1360 content: '';
1361 width: 100%;
1362}
1363div.toolbarModifier > ul > li[data-type="separator"] > p {
1364 padding: 2px 5px;
1365}
1366div.toolbarModifier > ul > li > p,
1367div.toolbarModifier > ul > li > ul {
1368 display: table-cell;
1369 vertical-align: middle;
1370}
1371div.toolbarModifier > ul > li p {
1372 padding-left: 5px;
1373 min-width: 200px;
1374}
1375div.toolbarModifier > ul > li p span {
1376 white-space: nowrap;
1377 cursor: default;
1378}
1379div.toolbarModifier > ul > li p span button {
1380 font-size: 12.666px;
1381 margin-right: 5px;
1382 cursor: pointer;
1383 background: #fff;
1384 -webkit-border-radius: 5px;
1385 -webkit-background-clip: padding-box;
1386 -moz-border-radius: 5px;
1387 -moz-background-clip: padding;
1388 border-radius: 5px;
1389 background-clip: padding-box;
1390 border: 1px solid #bbb;
1391 padding: 0 7px;
1392 line-height: 12px;
1393 height: 20px;
1394}
1395div.toolbarModifier > ul > li p span button:not(.disabled):hover,
1396div.toolbarModifier > ul > li p span button:not(.disabled):focus {
1397 color: #fff;
1398 background-color: #454545;
1399 border-color: transparent;
1400}
1401div.toolbarModifier > ul > li p span button.move.disabled {
1402 cursor: default;
1403 zoom: 1;
1404 filter: alpha(opacity=20);
1405 -webkit-opacity: 0.2;
1406 -moz-opacity: 0.2;
1407 opacity: 0.2;
1408}
1409div.toolbarModifier > ul > li ul {
1410 border-collapse: collapse;
1411 padding: 0;
1412 width: 100%;
1413}
1414div.toolbarModifier > ul > li ul li {
1415 display: table-row;
1416 list-style-type: none;
1417 line-height: 1;
1418}
1419div.toolbarModifier > ul > li ul li[data-type="subgroup"] {
1420 border-top: 1px solid #ddd;
1421}
1422div.toolbarModifier > ul > li ul li[data-type="subgroup"]:first-child {
1423 border-top: 0;
1424}
1425div.toolbarModifier > ul > li ul li[data-type="subgroup"] [data-type="button"] {
1426 -webkit-border-radius: 3px;
1427 -webkit-background-clip: padding-box;
1428 -moz-border-radius: 3px;
1429 -moz-background-clip: padding;
1430 border-radius: 3px;
1431 background-clip: padding-box;
1432 padding: 0 2px;
1433}
1434div.toolbarModifier > ul > li ul li[data-type="subgroup"] [data-type="button"]:focus {
1435 background: rgba(0, 0, 0, 0.04);
1436}
1437div.toolbarModifier > ul > li ul li[data-type="subgroup"] [data-type="button"] input {
1438 vertical-align: middle;
1439}
1440div.toolbarModifier > ul > li ul li > p,
1441div.toolbarModifier > ul > li ul li > ul {
1442 display: table-cell;
1443 vertical-align: middle;
1444}
1445div.toolbarModifier > ul > li ul li ul {
1446 padding: 0;
1447}
1448div.toolbarModifier > ul > li ul li ul li {
1449 padding: 0;
1450 display: inline-block;
1451 cursor: pointer;
1452 margin: 2px 5px 2px 0;
1453}
1454div.toolbarModifier > ul > li ul li ul li .cke_combo_text {
1455 cursor: pointer;
1456 white-space: nowrap;
1457}
1458div.toolbarModifier > ul > li ul li ul li .cke_toolgroup,
1459div.toolbarModifier > ul > li ul li ul li .cke_combo_button {
1460 cursor: pointer;
1461 margin: 0;
1462 vertical-align: middle;
1463 border: 1px solid #ddd;
1464 font-size: 11.41px;
1465 font-size: 0.713rem;
1466 line-height: 20.54px;
1467 line-height: 1.28rem;
1468}
1469div.toolbarModifier > .codemirror-wrapper {
1470 overflow-y: auto;
1471}
1472div.toolbarModifier-hints {
1473 float: right;
1474 width: 350px;
1475 min-width: 150px;
1476 overflow-y: auto;
1477 margin-left: 1.5em;
1478}
1479div.toolbarModifier-hints h3 {
1480 font-size: 18.08px;
1481 font-size: 1.13rem;
1482 line-height: 32.54px;
1483 line-height: 2.03rem;
1484 padding: 0.36em 1.5em;
1485 background: #f5f5f5;
1486 border-bottom: 1px solid #ddd;
1487 margin-top: 0;
1488 margin-bottom: 1.2em;
1489}
1490div.toolbarModifier-hints dl {
1491 margin-bottom: 1.2em;
1492 overflow: hidden;
1493}
1494div.toolbarModifier-hints dl .list-header {
1495 font-weight: bold;
1496 border: 0;
1497 padding-bottom: 0.6em;
1498}
1499div.toolbarModifier-hints dl > p {
1500 text-align: center;
1501}
1502div.toolbarModifier-hints dl dt {
1503 float: left;
1504 width: 9em;
1505 clear: both;
1506 text-align: right;
1507 border-top: 1px solid #ddd;
1508 padding-left: 1.5em;
1509 padding-right: .1em;
1510 -webkit-box-sizing: border-box;
1511 -moz-box-sizing: border-box;
1512 box-sizing: border-box;
1513}
1514div.toolbarModifier-hints dl dt code {
1515 background: none;
1516 border: none;
1517 vertical-align: middle;
1518}
1519div.toolbarModifier-hints dl dd {
1520 margin-left: 10em;
1521 clear: right;
1522 padding-right: 1.5em;
1523}
1524div.toolbarModifier-hints dl dd code {
1525 line-height: 2.2em;
1526}
1527div.toolbarModifier-hints dl dd:after {
1528 content: '\00a0';
1529 display: block;
1530 clear: left;
1531 float: right;
1532 height: 0;
1533 width: 0;
1534}
1535.toolbarModifier-hints,
1536.configContainer textarea.configCode,
1537.CodeMirror {
1538 -webkit-border-radius: 3px;
1539 -webkit-background-clip: padding-box;
1540 -moz-border-radius: 3px;
1541 -moz-background-clip: padding;
1542 border-radius: 3px;
1543 background-clip: padding-box;
1544 border: 1px solid #ccc;
1545 font-size: 13.01px;
1546 font-size: 0.813rem;
1547 line-height: 23.42px;
1548 line-height: 1.46rem;
1549}
1550.configContainer textarea.configCode,
1551.CodeMirror pre,
1552.CodeMirror-linenumber {
1553 font-size: 13.01px;
1554 font-size: 0.813rem;
1555 line-height: 23.42px;
1556 line-height: 1.46rem;
1557 font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
1558}
1559.CodeMirror pre {
1560 border: none;
1561 padding: 0;
1562 margin: 0;
1563}
1564.configContainer textarea.configCode {
1565 -webkit-box-sizing: border-box;
1566 -moz-box-sizing: border-box;
1567 box-sizing: border-box;
1568 color: #575757;
1569 padding: 10px;
1570 width: 100%;
1571 min-height: 500px;
1572 margin: 0;
1573 resize: none;
1574 outline: none;
1575 -moz-tab-size: 4;
1576 tab-size: 4;
1577 white-space: pre;
1578 word-wrap: normal;
1579 overflow: auto;
1580}
1581.CodeMirror-hints.toolbar-modifier {
1582 padding: 0;
1583 color: #575757;
1584 font-size: 14px;
1585 font-size: 0.875rem;
1586 line-height: 25.2px;
1587 line-height: 1.57rem;
1588 font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
1589}
1590.CodeMirror-hints.toolbar-modifier .CodeMirror-hint-active {
1591 color: #575757;
1592 background: #f0fafb;
1593}
1594.CodeMirror-hints.toolbar-modifier > li:hover {
1595 background: #fffbe3;
1596}
1597/* Text modifier */
1598#toolbarModifierWrapper {
1599 margin-bottom: 1.2em;
1600}
1601#toolbarModifierWrapper .invalid .CodeMirror {
1602 background: #fff8f8;
1603 border-color: red;
1604}
1605#toolbarModifierWrapper .CodeMirror {
1606 height: auto;
1607 padding: 0 0.6em;
1608}
1609.staticContainer {
1610 position: fixed;
1611 top: 0;
1612 width: 100%;
1613 z-index: 10;
1614}
1615.staticContainer > .grid-container {
1616 max-width: 1052px;
1617}
1618.staticContainer > .grid-container .inner {
1619 background: #fff;
1620}
1621.staticContainer > .grid-container .inner .toolbar {
1622 margin-bottom: 0;
1623}
1624#help {
1625 position: relative;
1626 top: -15px;
1627 left: -5px;
1628}
1629#help-content {
1630 display: none;
1631}
1632/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL2dsb2JhbC9nbG9iYWwubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL2NvcmUvY29yZS5sZXNzIiwiLi4vLi4vbm9kZV9tb2R1bGVzL2Nrc291cmNlLXNhbXBsZXMtZnJhbWV3b3JrL2NvbXBvbmVudHMvZ3JpZC9ncmlkLmxlc3MiLCIuLi8uLi9ub2RlX21vZHVsZXMvY2tzb3VyY2Utc2FtcGxlcy1mcmFtZXdvcmsvbm9kZV9tb2R1bGVzL2xlc3NoYXQvYnVpbGQvbGVzc2hhdC5sZXNzIiwiLi4vLi4vbm9kZV9tb2R1bGVzL2Nrc291cmNlLXNhbXBsZXMtZnJhbWV3b3JrL2NvbXBvbmVudHMvaGVhZGVyLWEvaGVhZGVyLWEubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL25hdmlnYXRpb24tYS9uYXZpZ2F0aW9uLWEubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL25hdmlnYXRpb24tYi9uYXZpZ2F0aW9uLWIubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL2Zvb3Rlci1hL2Zvb3Rlci1hLmxlc3MiLCIuLi8uLi9ub2RlX21vZHVsZXMvY2tzb3VyY2Utc2FtcGxlcy1mcmFtZXdvcmsvY29tcG9uZW50cy9jb250ZW50L2NvbnRlbnQubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL2J1dHRvbi1hL2J1dHRvbi1hLmxlc3MiLCIuLi8uLi9ub2RlX21vZHVsZXMvY2tzb3VyY2Utc2FtcGxlcy1mcmFtZXdvcmsvY29tcG9uZW50cy9iYWxsb29uLWEvYmFsbG9vbi1hLmxlc3MiLCIuLi8uLi9ub2RlX21vZHVsZXMvY2tzb3VyY2Utc2FtcGxlcy1mcmFtZXdvcmsvY29tcG9uZW50cy9pY29uL2ljb24ubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL3N3aXRjaC9zd2l0Y2gubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL3RvZ2dsZXIvdG9nZ2xlci5sZXNzIiwiLi4vLi4vbm9kZV9tb2R1bGVzL2Nrc291cmNlLXNhbXBsZXMtZnJhbWV3b3JrL2NvbXBvbmVudHMvbW9kYWwvbW9kYWwubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL2Jhc2ljc2FtcGxlL2NvcmUubGVzcyIsIi4uLy4uL25vZGVfbW9kdWxlcy9ja3NvdXJjZS1zYW1wbGVzLWZyYW1ld29yay9jb21wb25lbnRzL2Jhc2ljc2FtcGxlL2Fkam9pbmVkLmxlc3MiLCIuLi8uLi9zYW1wbGVzL2xlc3MvY3VzdG9tLmxlc3MiLCIuLi8uLi9zYW1wbGVzL3Rvb2xiYXJjb25maWd1cmF0b3IvbGVzcy90b29sYmFybW9kaWZpZXIubGVzcyIsIi4uLy4uL3NhbXBsZXMvdG9vbGJhcmNvbmZpZ3VyYXRvci9sZXNzL2Jhc2UubGVzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBc0RBLFFBSGlDO0VBeUNoQztJQUNDLHdCQUFBOzs7QUMxRkY7QUFBUztBQUFPO0FBQVM7QUFBWTtBQUFRO0FBQVE7QUFBUTtBQUFRO0FBQU07QUFBTTtBQUFLO0VBQ3JGLGNBQUE7O0FBR0Q7QUFBTTtFQUNMLFNBQUE7RUFDQSxVQUFBO0VBQ0Esd0JETitCLHVDQ00vQjtFQUNBLGdCQUFBO0VBQ0EsY0FBQTs7QUNIQSxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsVUFBQTs7QUFERCxZQUFZO0VBQ1gsV0FBQTs7QUY0Q0YsUUFIaUM7RUVqQ2hDO0VBS0MsWUFBWTtFQUFaLFlBQVk7RUFBWixZQUFZO0VBQVosWUFBWTtFQUFaLFlBQVk7RUFBWixZQUFZO0VBQVosWUFBWTtFQUFaLFlBQVk7RUFBWixZQUFZO0lBSlosV0FBQTs7O0FBYUYsQ0FBQztFQ3FSQyw4QkFBQTtFQUNBLDJCQUFBO0VBQ0Esc0JBQUE7RURyUkQsZ0JBQUE7RUFDQSxpQkFBQTtFQUNBLFdBQUE7O0FBSUEsQ0FEQSxxQkFDQztBQUFELGVBQUM7QUFBUSxDQURULHFCQUNVO0FBQUQsZUFBQztFQUNULFNBQVMsRUFBVDtFQUNBLGNBQUE7RUFDQSxnQkFBQTtFQUNBLGtCQUFBO0VBQ0EsWUFBQTtFQUNBLGNBQUE7RUFDQSxRQUFBO0VBQ0EsU0FBQTs7QUFLRCxDQURBLHFCQUNDO0FBQUQsZUFBQztFQUNBLFdBQUE7O0FBSUY7RUMyUEUsOEJBQUE7RUFDQSwyQkFBQTtFQUNBLHNCQUFBO0VEM1BELGlCQUFBO0VBQ0Esa0JBQUE7O0FBS0Msc0JBREQsRUFBQyxxQkFDQztFQUNBLGVBQUE7O0FBR0Qsc0JBTEQsRUFBQyxxQkFLQztFQUNBLGdCQUFBOztBRmpCSCxRQUhpQztFRTBCOUIsc0JBREQsRUFBQyxxQkFDQztJQUNBLGdCQUFBOztFQUdELHNCQUxELEVBQUMscUJBS0M7SUFDQSxpQkFBQTs7O0FFN0VKO0VBQ0MsaUJBQUE7RUFHQSxnQkFBQTs7QUFKRCxTQU1DO0VBQ0MsZ0JBQUE7O0FKMENGLFFBSGlDO0VBNkNqQyxTSXJGQztJQUlFLGtCQUFBOzs7QUFWSCxTQU1DLGVBT0M7RUFDQyxtQkFBQTs7QUNWSDtFQUNDLFlBQUE7RUFDQSxtQkFBQTtFQUNBLGtCQUFBO0VBQ0EsT0FBQTtFQUNBLFFBQUE7RUFDQSxNQUFBO0VBQ0EsVUFBQTtFQUNBLGdCQUFBOztBTHFDRCxRQUhpQztFQTZDakM7SUs1RUUsa0JBQUE7OztBQVhGLGFBY0M7RUFDQyxnQkFBQTtFQUNBLFNBQUE7RUFDQSxnQkFBQTs7QUFqQkYsYUFjQyxHQUtDO0FBbkJGLGFBY0MsR0FLSyxHQUFHO0VBQ04scUJBQUE7O0FMeUJILFFBSGlDO0VBNkNqQyxhS3pFQztJQVVFLFdBQUE7SUFDQSx1QkFBQTtJQUNBLG1CQUFBO0lBQ0EscUJBQUE7SUFDQSxXQUFBOztFQUVBLGFBaEJGLEdBZ0JHO0VBQVMsYUFoQlosR0FnQmE7SUFDVixhQUFBOzs7QUFLRCxhQXRCRixHQXFCRSxhQUNDO0VBQ0EsZ0JBQUE7O0FMUUosUUFIaUM7RUE2Q2pDLGFLekVDLEdBcUJFLGFBQ0M7SUFJQyxnQkFBQTs7O0FBSUYsYUE5QkYsR0FxQkUsYUFTQztFQUNBLGlCQUFBOztBTEFKLFFBSGlDO0VBNkNqQyxhS3pFQyxHQXFCRSxhQVNDO0lBSUMsa0JBQUE7OztBQU1GLGFBeENGLEdBdUNDLEdBQ0c7RUFDRCxpQkFBQTs7QUF2REosYUFjQyxHQXVDQyxHQUtDO0VMeENGLGVBQUE7RUFDQSxtQkFBQTtFQUNBLGlCQUFBO0VBQ0Esb0JBQUE7RUt1Q0csaUJBQUE7RUFDQSxXQUFBO0VBQ0EsV0FBQTtFQUNBLGlCQUFBO0VBQ0EscUJBQUE7RUFDQSx5QkFBQTs7QUFFQSxhQXJESCxHQXVDQyxHQUtDLEVBU0U7RUFDQSxlQUFBO0VBQ0EsV0FBQTs7QUFRSix5QkFBQztBQUFTLHlCQUFDO0VBQ1Ysc0JBQWtCLHFyQkFBbEI7O0FDcEZGO0VBQ0MsaUJBQUE7RUFDQSxnQkFBQTtFQUNBLGlCQUFBOztBTmdERCxRQUhpQztFQTZDakM7SU12RkUsa0JBQUE7SUFDQSxnQkFBQTtJQUdBLFVBQUE7OztBQVZGLGFBYUM7RUFDQyxVQUFBO0VBQ0EsZ0JBQUE7RUFDQSxTQUFBO0VBQ0EsaUJBQUE7O0FBakJGLGFBYUMsR0FNQztBQW5CRixhQWFDLEdBTUssR0FBRztFQUNOLHFCQUFBOztBTitCSCxRQUhpQztFQTZDakMsYU1oRkM7SUFXRSxjQUFBO0lBQ0EsV0FBQTtJQUNBLHFCQUFBOzs7QU55QkgsUUFIaUM7RUE2Q2pDLGFNaEZDLEdBZ0JDO0lBRUUsa0JBQUE7OztBQUdELGFBckJGLEdBZ0JDLEdBS0c7RUFDRCxpQkFBQTs7QU5nQkosUUFIaUM7RUE2Q2pDLGFNaEZDLEdBZ0JDLEdBS0c7SUFJQSxjQUFBOzs7QUF0Q0wsYUFhQyxHQWdCQyxHQWFDO0VId1FELDhCQUFBO0VBQ0EsMkJBQUE7RUFDQSxzQkFBQTtFR3hRRSx5QkFBQTtFQUNBLHFCQUFBO0VBQ0EsYUFBQTs7QU5LSixRQUhpQztFQTZDakMsYU1oRkMsR0FnQkMsR0FhQztJQU9FLFdBQUE7SUhxT0gsd0JBQUE7SUFBaUMsb0NBQUE7SUFDakMscUJBQUE7SUFBOEIsNkJBQUE7SUFDOUIsZ0JBQUE7SUFBeUIsNEJBQUE7OztBSXhSM0I7RVB3QkMsZUFBQTtFQUNBLG9CQUFBO0VBQ0EsbUJBQUE7RUFDQSxvQkFBQTtFT3hCQSxtQkFBQTtFQUNBLHNCQUFBO0VBQ0EsZ0JBQUE7RUFDQSxjQUFBOztBQU5ELFNQNEVDO0VBQ0MsY0FBQTtFQUNBLHFCQUFBO0VBRUEsaUNBQUE7O0FBRUEsU0FORCxFQU1FO0VBQ0EsY0FBQTs7QU9uRkgsU0FRQztFQUNDLFNBQUE7RUFDQSxxQkFBQTtFQUNBLGtCQUFBOztBQ1hGO0VSd0JDLGVBQUE7RUFDQSxtQkFBQTtFQUNBLG1CQUFBO0VBQ0Esb0JBQUE7RVF6QkEsZ0JBQUE7RUFDQSxrQkFBQTtFQUNBLHFCQUFBOztBQUpELFFBU0M7RUFDQyxnQkFBQTs7QUFWRixRQWFDO0FBYkQsUUFhSztBQWJMLFFBYVM7QUFiVCxRQWFjO0FBYmQsUUFhMEIsU0FBUSxJQUFJO0FBYnRDLFFBYXdEO0VBQ3RELGlCQUFBOztBQWRGLFFBaUJDO0FBakJELFFBaUJPO0VMcVFMLDBCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHVCQUFBO0VBQThCLDZCQUFBO0VBQzlCLGtCQUFBO0VBQXlCLDRCQUFBO0VLclF6QixnQkFBQTs7QUFuQkYsUUFzQkM7QUF0QkQsUUFzQk07QUF0Qk4sUUFzQlk7QUF0QlosUUFzQmlCO0VBQ2YsbUJBQUE7O0FBdkJGLFFBMEJDO0FBMUJELFFBMEJhO0VBQ1gsZ0JBQUE7RUFDQSw4QkFBQTtFQUNBLHFCQUFBOztBQTdCRixRQW9DQyxFUndDQTtBUTVFRCxRQW9DSSxHUndDSDtBUTVFRCxRQW9DUSxHUndDUDtBUTVFRCxRQW9DWSxXUndDWDtBUTVFRCxRQW9Dd0IsR1J3Q3ZCO0FRNUVELFFBb0M0QixHUndDM0I7QVE1RUQsUUFvQ2dDLEdSd0MvQjtBUTVFRCxRQW9Db0MsR1J3Q25DO0FRNUVELFFBb0N3QyxHUndDdkM7RUFDQyxjQUFBO0VBQ0EscUJBQUE7RUFFQSxpQ0FBQTs7QUFFQSxRUTlDRCxFUndDQSxFQU1FO0FBQUQsUVE5Q0UsR1J3Q0gsRUFNRTtBQUFELFFROUNNLEdSd0NQLEVBTUU7QUFBRCxRUTlDVSxXUndDWCxFQU1FO0FBQUQsUVE5Q3NCLEdSd0N2QixFQU1FO0FBQUQsUVE5QzBCLEdSd0MzQixFQU1FO0FBQUQsUVE5QzhCLEdSd0MvQixFQU1FO0FBQUQsUVE5Q2tDLEdSd0NuQyxFQU1FO0FBQUQsUVE5Q3NDLEdSd0N2QyxFQU1FO0VBQ0EsY0FBQTs7QVFuRkgsUUF3Q0M7QUF4Q0QsUUF3Q0s7QUF4Q0wsUUF3Q1M7QUF4Q1QsUUF3Q2E7QUF4Q2IsUUF3Q2lCO0VBQ2YsV0FBQTtFQUNBLGdCQUFBOztBQTFDRixRQXdDQyxHQUtDO0FBN0NGLFFBd0NLLEdBS0g7QUE3Q0YsUUF3Q1MsR0FLUDtBQTdDRixRQXdDYSxHQUtYO0FBN0NGLFFBd0NpQixHQUtmO0FBN0NGLFFBd0NDLEdBS087QUE3Q1IsUUF3Q0ssR0FLRztBQTdDUixRQXdDUyxHQUtEO0FBN0NSLFFBd0NhLEdBS0w7QUE3Q1IsUUF3Q2lCLEdBS1Q7RUFDTCxrQkFBQTs7QUE5Q0gsUUF3Q0MsR0FVQyxFQUFDO0FBbERILFFBd0NLLEdBVUgsRUFBQztBQWxESCxRQXdDUyxHQVVQLEVBQUM7QUFsREgsUUF3Q2EsR0FVWCxFQUFDO0FBbERILFFBd0NpQixHQVVmLEVBQUM7RUFDQSxnQkFBQTtFQUNBLHNCQUFBO0VBQ0EsVUFBQTtFQUNBLFNBQUE7O0FBR0QsUUFqQkQsR0FpQkUsTUFDQSxFQUFDO0FBREYsUUFqQkcsR0FpQkYsTUFDQSxFQUFDO0FBREYsUUFqQk8sR0FpQk4sTUFDQSxFQUFDO0FBREYsUUFqQlcsR0FpQlYsTUFDQSxFQUFDO0FBREYsUUFqQmUsR0FpQmQsTUFDQSxFQUFDO0VBQ0EsVUFBQTs7QUFJRixRQXZCRCxHQXVCRSxPQUNBO0FBREQsUUF2QkcsR0F1QkYsT0FDQTtBQURELFFBdkJPLEdBdUJOLE9BQ0E7QUFERCxRQXZCVyxHQXVCVixPQUNBO0FBREQsUUF2QmUsR0F1QmQsT0FDQTtFTDhERCwwREFBQTtFQUNBLHVEQUFBO0VBQ0EscURBQUE7RUFDQSxrREFBQTtFSy9ERSxVQUFBOztBQWxFSixRQXVFQztBQXZFRCxRQXVFUTtBQXZFUixRQXVFZ0IsU0FBUSxJQUFJO0VMK00xQiwwQkFBQTtFQUFpQyxvQ0FBQTtFQUNqQyx1QkFBQTtFQUE4Qiw2QkFBQTtFQUM5QixrQkFBQTtFQUF5Qiw0QkFBQTtFQW1CekIsdURBQUE7RUFDQSxvREFBQTtFQUNBLCtDQUFBO0VLbE9BLGFBQUE7RUFDQSxjQUFBO0VBRUEseUJBQUE7RUFDQSxrQkFBQTs7QUFFQSxRQVZELE1BVUU7QUFBRCxRQVZNLE9BVUw7QUFBRCxRQVZjLFNBQVEsSUFBSSxnQkFVekI7RUFDQSxxQkFBQTtFQUNBLFVBQUE7RUx3TkQsd0VBQUE7RUFDQSxxRUFBQTtFQUNBLGdFQUFBOztBSzdTRixRQThGQztFQUNDLDhCQUFBO0VBQ0EsZUFBQTs7QUFoR0YsUUFtR0M7RUFDQyxrQkFBQTtFQUNBLDZCUm5HMkMsd0JRbUczQztFUjdFRCxlQUFBO0VBQ0EsZUFBQTtFQUNBLG1CQUFBO0VBQ0EsbUJBQUE7O0FRM0JELFFBeUdDO0VBQ0Msa0JBQUE7O0FBMUdGLFFBNkdDO0VSckZBLGVBQUE7RUFDQSxrQkFBQTtFQUNBLG1CQUFBO0VBQ0Esb0JBQUE7RVFvRkMsbUJBQUE7O0FBL0dGLFFBa0hDO0VSMUZBLGlCQUFBO0VBQ0EsaUJBQUE7RUFDQSxvQkFBQTtFQUNBLG9CQUFBO0VReUZDLGlCQUFBOztBQXBIRixRQXVIQztFUi9GQSxlQUFBO0VBQ0EsaUJBQUE7RUFDQSxtQkFBQTtFQUNBLG1CQUFBO0VROEZDLGdCQUFBO0VBQ0Esa0JBQUE7O0FBMUhGLFFBNkhDO0VSckdBLGlCQUFBO0VBQ0EsaUJBQUE7RUFDQSxvQkFBQTtFQUNBLG9CQUFBO0VRb0dDLGdCQUFBO0VBQ0Esa0JBQUE7O0FBaElGLFFBbUlDO0VSM0dBLGlCQUFBO0VBQ0EsaUJBQUE7RUFDQSxvQkFBQTtFQUNBLG9CQUFBO0VRMEdDLGdCQUFBO0VBQ0Esa0JBQUE7O0FBdElGLFFBeUlDO0VBQ0MsU0FBQTtFQUNBLDZCQUFBO0VBQ0EsZUFBQTs7QUFJQSxRQURELE1BQ0U7RUFDQSxhQUFBO0VBQ0Esa0JBQUE7O0FBR0QsUUFORCxNQU1FO0VMaURELDBCQUFBO0VBQ0EsdUJBQUE7RUFDQSxrQkFBQTs7QUt4TUYsUUE0SkM7RVJwSUEsZUFBQTtFQUNBLGtCQUFBO0VBQ0EsbUJBQUE7RUFDQSxvQkFBQTtFUW1JQyxvQlI3SjhCLHVDUTZKOUI7RUFDQSxnQkFBQTtFTDRJQSx1REFBQTtFQUNBLG9EQUFBO0VBQ0EsK0NBQUE7O0FLN1NGLFFBdUtDLEVBQ0M7RUFDQyxzQkFBQTs7QUF6S0gsUUF1S0MsRUFLQztFQUNDLGNBQUE7O0FBN0tILFFBaUxDO0VBQ0MsVUFBQTtFQUNBLFNBQUE7RUFFQSxXQUFBO0VBQ0EsY0FBQTtFQUNBLGdCQUFBOztBQXZMRixRQTBMQztBQTFMRCxRQTBMTTtFUmxLTCxrQkFBQTtFQUNBLG1CQUFBO0VBQ0EsbUJBQUE7RUFDQSxvQkFBQTtFUWtLQyxnSkFBQTs7QUE3TEYsUUEwTEMsSUFLQztBQS9MRixRQTBMTSxLQUtKO0VBQ0MsU0FBQTs7QUFoTUgsUUFxTUMsSUFBSTtFQUNILGVBQUE7RUFDQSxjQUFBOztBQXZNRixRQTBNQztFQUNDLFdBQUE7O0FBM01GLFFBOE1DLEdBRUM7QUFoTkYsUUE4TUssR0FFSDtBQWhORixRQThNQyxHQUVLO0FBaE5OLFFBOE1LLEdBRUM7RUFDSCxnQkFBQTs7QUFqTkgsUUE4TUMsR0FNQztBQXBORixRQThNSyxHQU1IO0VSNUxELGVBQUE7RUFDQSxtQkFBQTtFQUNBLG9CQUFBO0VBQ0Esb0JBQUE7O0FRM0JELFFBME5DLFNBQVEsSUFBSTtFQUNYLFdBQUE7O0FBM05GLFFBOE5DLElBQUc7RUFDRix1QkFBQTtFQUNBLGFBQUE7RUFDQSxxQkFBQTs7O0FBR0EsUUFORCxJQUFHLEtBTUQ7RUFDQSxTQUFTLE1BQVQ7RUFDQSxpQkFBQTs7QUNqT0QsSUFERCxFQUNFO0FBQUQsSUFERSxPQUNEO0FBQUQsSUFEVSxNQUNUO0VOaVJELDBCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHVCQUFBO0VBQThCLDZCQUFBO0VBQzlCLGtCQUFBO0VBQXlCLDRCQUFBO0VIaFExQixlQUFBO0VBQ0EsbUJBQUE7RUFDQSxtQkFBQTtFQUNBLG9CQUFBO0VTbkJFLFlBQUE7RUFDQSxpQkFBQTtFQUNBLGdCQUFBO0VBQ0EsZ0JBQUE7RUFDQSxjQUFBO0VBQ0EsbUJBQUE7RUFDQSxxQkFBQTtFQUNBLHFCQUFBO0VBQ0EsZUFBQTtFQUNBLFNBQUE7RUFDQSxzQkFBQTtFQUlBLGFBQUE7RUFHQSx1QkFBQTs7QUFFQSxJQXZCRixFQUNFLFNBc0JDO0FBQUQsSUF2QkMsT0FDRCxTQXNCQztBQUFELElBdkJTLE1BQ1QsU0FzQkM7RUFDQSxrQkFBQTs7QUFHRCxJQTNCRixFQUNFLFNBMEJDO0FBQUQsSUEzQkMsT0FDRCxTQTBCQztBQUFELElBM0JTLE1BQ1QsU0EwQkM7RUFDQSxtQkFBQTs7QUFvQkQsSUFoREYsRUFDRSxTQStDQztBQUFELElBaERDLE9BQ0QsU0ErQ0M7QUFBRCxJQWhEUyxNQUNULFNBK0NDO0VOa09GLDRCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHlCQUFBO0VBQThCLDZCQUFBO0VBQzlCLG9CQUFBO0VBQXlCLDRCQUFBO0VNblB2QixXQUFBO0VBQ0EsVUFBQTtFQUNBLG1CQUFBO0VBQ0EsZ0JBQUE7RUFDQSxrQkFBQTtFQUNBLGtCQUFBOztBQUVBLElBeENILEVBQ0UsU0ErQ0MsaUJBUkM7QUFBRCxJQXhDQSxPQUNELFNBK0NDLGlCQVJDO0FBQUQsSUF4Q1EsTUFDVCxTQStDQyxpQkFSQztFQUNBLGtCQUFBO0VBQ0EsU0FBQTtFQUNBLFFBQUE7RUFDQSxxQkFBQTs7QVRHTCxRQUhpQztFQTZDakMsSVN6RkMsRUFDRSxTQW1EQztFVHFDSixJU3pGSSxPQUNELFNBbURDO0VUcUNKLElTekZZLE1BQ1QsU0FtREM7SU44TkYsNEJBQUE7SUFBaUMsb0NBQUE7SUFDakMseUJBQUE7SUFBOEIsNkJBQUE7SUFDOUIsb0JBQUE7SUFBeUIsNEJBQUE7SU1uUHZCLFdBQUE7SUFDQSxVQUFBO0lBQ0EsbUJBQUE7SUFDQSxnQkFBQTtJQUNBLGtCQUFBO0lBQ0Esa0JBQUE7O0VBRUEsSUF4Q0gsRUFDRSxTQW1EQywwQkFaQztFQUFELElBeENBLE9BQ0QsU0FtREMsMEJBWkM7RUFBRCxJQXhDUSxNQUNULFNBbURDLDBCQVpDO0lBQ0Esa0JBQUE7SUFDQSxTQUFBO0lBQ0EsUUFBQTtJQUNBLHFCQUFBOztFQUpELElBeENILEVBQ0UsU0FtREMsMEJBWkM7RUFBRCxJQXhDQSxPQUNELFNBbURDLDBCQVpDO0VBQUQsSUF4Q1EsTUFDVCxTQW1EQywwQkFaQztJQUNBLGtCQUFBO0lBQ0EsU0FBQTtJQUNBLFFBQUE7SUFDQSxxQkFBQTs7O0FBY0YsSUExREYsRUFDRSxTQXlEQztBQUFELElBMURDLE9BQ0QsU0F5REM7QUFBRCxJQTFEUyxNQUNULFNBeURDO0FBQ0QsSUEzREYsRUFDRSxTQTBEQztBQUFELElBM0RDLE9BQ0QsU0EwREM7QUFBRCxJQTNEUyxNQUNULFNBMERDO0VBQ0EsV0FBQTtFQUNBLG1CQUFBOztBQUdELElBaEVGLEVBQ0UsU0ErREM7QUFBRCxJQWhFQyxPQUNELFNBK0RDO0FBQUQsSUFoRVMsTUFDVCxTQStEQztFQUNBLHFCQUFBO0VBQ0EsVUFBQTtFTnFPRix5RUFBQTtFQUNBLHNFQUFBO0VBQ0EsaUVBQUE7O0FNNU5BLElBN0VELEVBNkVFO0FBQUQsSUE3RUUsT0E2RUQ7QUFBRCxJQTdFVSxNQTZFVDtFQUNBLG1CQUFBOztBQUVBLElBaEZGLEVBNkVFLGNBR0M7QUFBRCxJQWhGQyxPQTZFRCxjQUdDO0FBQUQsSUFoRlMsTUE2RVQsY0FHQztBQUNELElBakZGLEVBNkVFLGNBSUM7QUFBRCxJQWpGQyxPQTZFRCxjQUlDO0FBQUQsSUFqRlMsTUE2RVQsY0FJQztFQUNBLGNBQUE7RUFDQSxtQkFBQTs7QUFJRixJQXZGRCxFQXVGRTtBQUFELElBdkZFLE9BdUZEO0FBQUQsSUF2RlUsTUF1RlQ7QUFBRCxJQXZGRCxFSGlERyxhQXhDSCxHQWdCQyxHQWFDLEVBV0U7QUdzQ0gsSUF2RkUsT0hpREEsYUF4Q0gsR0FnQkMsR0FhQyxFQVdFO0FHc0NILElBdkZVLE1IaURSLGFBeENILEdBZ0JDLEdBYUMsRUFXRTtFR3VDRixXQUFBO0VBQ0EsbUJBQUE7O0FBRUEsSUEzRkYsRUF1RkUsb0JBSUM7QUFBRCxJQTNGQyxPQXVGRCxvQkFJQztBQUFELElBM0ZTLE1BdUZULG9CQUlDO0FBQ0QsSUE1RkYsRUF1RkUsb0JBS0M7QUFBRCxJQTVGQyxPQXVGRCxvQkFLQztBQUFELElBNUZTLE1BdUZULG9CQUtDO0FBREQsSUEzRkYsRUhpREcsYUF4Q0gsR0FnQkMsR0FhQyxFQVdFLE1HMENEO0FBQUQsSUEzRkMsT0hpREEsYUF4Q0gsR0FnQkMsR0FhQyxFQVdFLE1HMENEO0FBQUQsSUEzRlMsTUhpRFIsYUF4Q0gsR0FnQkMsR0FhQyxFQVdFLE1HMENEO0FBQ0QsSUE1RkYsRUhpREcsYUF4Q0gsR0FnQkMsR0FhQyxFQVdFLE1HMkNEO0FBQUQsSUE1RkMsT0hpREEsYUF4Q0gsR0FnQkMsR0FhQyxFQVdFLE1HMkNEO0FBQUQsSUE1RlMsTUhpRFIsYUF4Q0gsR0FnQkMsR0FhQyxFQVdFLE1HMkNEO0VBQ0EsV0FBQTtFQUNBLG1CQUFBOztBQ2hHSjtFVnNCQyxlQUFBO0VBQ0Esa0JBQUE7RUFDQSxtQkFBQTtFQUNBLG9CQUFBO0VHMlBDLDBCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHVCQUFBO0VBQThCLDZCQUFBO0VBQzlCLGtCQUFBO0VBQXlCLDRCQUFBO0VPblIxQixnQ0FBQTtFQUVBLG1CQUFBO0VBQ0EscUJBQUE7RUFDQSxtQkFBQTtFQUNBLHdCQUFBO0VBQ0EsZ0JBQUE7RUFDQSxrQkFBQTtFQUNBLGFBQUE7RUFDQSxvQkFBQTtFQUNBLGNBQUE7O0FBRUEsVUFBQztFQUNBLGNBQUE7O0FBR0QsVUFBQztFQUNBLFNBQVMsRUFBVDtFQUNBLFFBQUE7RUFDQSxTQUFBO0VBQ0EsbUJBQUE7RUFDQSxrQkFBQTs7QUFNRCxhQUFDO0FBQUQsYUFBQztFQUNBLFVBQUE7RUFDQSw4QkFBQTtFQUNBLHlEQUFBOztBQU1ELGFBQUM7QUFBRCxhQUFDO0VBQ0EsYUFBQTtFQUNBLDhCQUFBO0VBQ0EseURBQUE7O0FBTUQsYUFBQztBQUFELGFBQUM7RUFDQSxVQUFBOztBQU1ELGFBQUM7QUFBRCxhQUFDO0VBQ0EsV0FBQTs7QUN2REYsY0FBYztBQUNkLGVBQWU7RUFDZCxTQUFTLEVBQVQ7RUFDQSxxQkFBQTtFQUNBLFdBQUE7RUFDQSxZQUFBO0VBQ0Esc0JBQUE7RUFDQSw0QkFBQTs7QUFHRCxjQUFjO0VBQ2Isa0JBQUE7O0FBR0QsZUFBZTtFQUNkLGlCQUFBOztBQUlBLGNBQUM7QUFBUyxjQUFDO0VBQ1Ysc0JBQWtCLDZjQUFsQjs7QUFLRCxtQkFBQztBQUFTLG1CQUFDO0VBQ1Ysc0JBQWtCLDZpQkFBbEI7O0FBS0QsV0FBQztBQUFTLFdBQUM7RUFDVixzQkFBa0IsNmlCQUFsQjs7QUM1QkYsSUFBSyxRQUVKO0VBQ0Msc0JBQUE7O0FBSEYsSUFBSyxRQU1KLE1BQUs7RUFDSixnQkFBQTtFQUNBLHFCQUFBOztBQVJGLElBQUssUUFXSjtFQUNDLHlCQUFBO0VBQ0EsMEJBQUE7O0FBRUEsSUFmRyxRQVdKLE1BSUU7RUFDQSxXQUFBOztBQUdELElBbkJHLFFBV0osTUFRRTtFQUNBLFlBQUE7O0FBcEJILElBQUssUUF3Qko7RUFDQyxhQUFBOztBQUlGO0VaWkMsZUFBQTtFQUNBLG1CQUFBO0VBQ0EsbUJBQUE7RUFDQSxvQkFBQTtFWVdBLGlCQUFBO0VBQ0EseUJBQUE7RUFDQSxnQkFBQTtFQUNBLHFCQUFBO0VBQ0Esc0JBQUE7RUFDQSxXQUFBO0VUMk9DLDBCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHVCQUFBO0VBQThCLDZCQUFBO0VBQzlCLGtCQUFBO0VBQXlCLDRCQUFBO0VTM08xQixrQkFBQTs7QUFURCxPQVdDLE1BQUs7RUFDSixhQUFBOztBQVpGLE9BZUM7RUFDQyxrQkFBQTtFQUNBLFVBQUE7RUFDQSxXQUFBO0VBQ0EsZUFBQTtFQUNBLGlCQUFBOztBQUVBLE9BUEQsTUFPRTtFQUNBLDBCQUFBOztBQXZCSCxPQTJCQztFQUNDLFdBQUE7RUFDQSxzQkFBQTtFQUNBLGFBQUE7RUFDQSxjQUFBO0VBQ0EsWUFBQTtFQUNBLGdCQUFBO0VUaU5BLDRCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHlCQUFBO0VBQThCLDZCQUFBO0VBQzlCLG9CQUFBO0VBQXlCLDRCQUFBOztBU3BQM0IsT0EyQkMsY0FTQztFQUNDLGdCQUFBO0VBQ0Esa0JBQUE7RUFDQSxjQUFBO0VBQ0EsYUFBQTtFQUNBLFlBQUE7RUFDQSxtQkFBQTtFVHdNRCw0QkFBQTtFQUFpQyxvQ0FBQTtFQUNqQyx5QkFBQTtFQUE4Qiw2QkFBQTtFQUM5QixvQkFBQTtFQUF5Qiw0QkFBQTs7QVN2TXhCLE9BbEJGLGNBU0MsU0FTRTtFQUNBLFNBQVMsRUFBVDtFQUNBLGNBQUE7RUFDQSxrQkFBQTtFQUNBLE1BQUE7RUFDQSxRQUFBO0VBQ0EsV0FBQTtFQUNBLE9BQUE7RUFFQSx5QkFBQTtFVHNLRix3Q0FBQTtFQUNBLG9DQUFBO0VBQ0EsZ0NBQUE7RUFLQSx5Q0FBQTtFQUE4QyxvQ0FBQTtFQUM5QyxxQ0FBQTtFQUEwQyw2QkFBQTtFQUMxQyxpQ0FBQTtFQUFzQyw0QkFBQTs7QVN2S3ZDLE9BQUMsTUFDQSxjQUFjLFNBQVE7RUFDckIsbUJBQUE7O0FBaEVILE9Bb0VDLE1BQUssY0FBZ0IsUUFFcEIsZ0JBQWdCO0VBQ2YsaUJBQUE7O0FBdkVILE9Bb0VDLE1BQUssY0FBZ0IsUUFTcEIsUUFBTztFQUNOLHNCQUFBO0VBQ0Esc0JBQUE7O0FBL0VILE9BbUZDLE1BQUssY0FBZ0IsUUFBUyxRQUFPO0VBQ3BDLHFCQUFBO0VBQ0EscUJBQUE7O0FDekhGO0VWazNCRSx5QkFBQTtFQUNBLHNCQUFBO0VBQ0EscUJBQUE7RUFDQSxpQkFBQTs7QVVyM0JGLFFBR0M7RUFDQyxlQUFBOztBQUpGLFFBTUM7RUFDQyxnQkFBQTs7QUFQRixRQVVDO0VBQ0MsYUFBQTs7QUFHRCxRQUFDLFVBQ0E7RUFDQyxhQUFBOztBQUZGLFFBQUMsVUFLQTtFQUNDLGdCQUFBOztBQUtIO0VBQ0MsZ0JBQUE7O0FBRUEsa0JBQUM7RUFDQSxTQUFBOztBQU1ELHNCQUFDO0FBQUQsdUJBQUM7QUFBUyxzQkFBQztBQUFELHVCQUFDO0VBQ1Ysc0JBQWtCLHlzQkFBbEI7O0FBSUEsc0JBREEsV0FDQztBQUFELHVCQURBLFdBQ0M7QUFBUyxzQkFEVixXQUNXO0FBQUQsdUJBRFYsV0FDVztFQUNWLHNCQUFrQixxdEJBQWxCOztBQU1GLHNCQUFDO0FBQ0Qsc0JBQUM7RUFDQSw2QkFBQTs7QUFLRCx1QkFBQztBQUNELHVCQUFDO0VBQ0EsZ0NBQUE7O0FDdERGO0VBQ0MsYUFBQTtFQUNBLGtCQUFBO0VBQ0EsdUJBQUE7RUFDQSxnQkFBQTtFWDRTQyw4QkFBQTtFQUNBLDJCQUFBO0VBQ0Esc0JBQUE7RVd6U0QscUJBQUE7RUFDQSxtQkFBQTtFWGd2QkMsd0NBQUE7RUFDQSxxQ0FBQTtFQUNBLG1DQUFBO0VBQ0Esb0NBQUE7RUFDQSxnQ0FBQTs7QVdqdkJELE1BQUM7RVh1UUEsNEJBQUE7RUFBaUMsb0NBQUE7RUFDakMseUJBQUE7RUFBOEIsNkJBQUE7RUFDOUIsb0JBQUE7RUFBeUIsNEJBQUE7RVd2UXpCLGVBQUE7RUFDQSxZQUFBO0VBQ0EsV0FBQTtFQUNBLGtCQUFBO0VBQ0EsU0FBQTtFQUNBLFdBQUE7RUFDQSxlQUFBO0VBQ0Esa0JBQUE7RUFDQSxpQkFBQTtFQUNBLG1CQUFBOztBQ3pCRixJQUFLO0FBQ0wsTUFBTztBQUNQLGFBQWM7QUFDZCxNQUFPO0VBQ04sZ0JBQUE7O0FBSUQ7RUFDQyxnQkFBQTs7QUFHRDtFQUNDLDZCQUFBOztBQ1hBLFNBQUM7RUFDQSx5QkFBQTtFQUNBLFdBQUE7O0FBRkQsU0FBQyxJQUlBLFNBQ0M7QUFMRixTQUFDLElBSUEsU0FDSztBQUxOLFNBQUMsSUFJQSxTQUNTO0FBTFYsU0FBQyxJQUlBLFNBQ2E7QUFMZCxTQUFDLElBSUEsU0FDaUI7RUFDZixXQUFBOztBQU5ILFNBQUMsSUFJQSxTQUtDO0VoQllGLGVBQUE7RUFDQSxtQkFBQTtFQUNBLG1CQUFBO0VBQ0Esb0JBQUE7RWdCYkcsZ0JBQUE7O0FBWEgsU0FBQyxJQUlBLFNBS0MsRUFJQztFQUNDLHFCQUFBO0VBQ0EsOEJBQUE7RUFDQSxjQUFBOztBQUVBLFNBbEJILElBSUEsU0FLQyxFQUlDLEVBS0U7RUFDQSxjQUFBOztBQW5CTCxTQUFDLElBSUEsU0FvQkM7RUFDQyxXQUFBOztBQXpCSCxTQUFDLElBSUEsU0F3QkM7RUFDQyxXQUFBOztBQTdCSCxTQUFDLElBSUEsU0E0QkM7RUFDQyxrQkFBQTtFQUNBLGNBQUE7O0FBS0gsU0FBQztFQUNBLGtCQUFBOztBQUVBLFNBSEEsT0FHQztFQUNBLFdBQUE7RUFDQSxTQUFTLEVBQVQ7RUFDQSxtQkFBQTtFQUNBLGtCQUFBO0VBQ0EsTUFBQTtFQUNBLE9BQUE7RUFDQSxRQUFBO0VBQ0EsV0FBQTs7QUN4REgsSUFBSztBQUNMLE1BQU87QUFDUCxhQUFjO0FBQ2QsTUFBTztFQUNOLGlCQUFBOztBQUdELElBQUssZ0JBQWU7RUFDbkIsZUFBQTs7QUFHRDtFQUNDLG1CQUFBO0VBQ0EsWUFBQTtFQUNBLGlCQUFBOztBQUhELE9BTUM7RUFFQyxrQkFBQTtFQUNBLFVBQUE7RUFDQSxVQUFBO0VBQ0EsbUJBQUE7RUFHQSxtQkFBQTtFQUNBLDRCQUFBOztBQUVBLE9BWEQsV0FXRTtFQUNBLHlEQUFBOztBQUtILFFBQVM7RUFDUixnQkFBQTtFQUNBLFNBQUE7RUFDQSx3QkFBQTs7QUFFQSxRQUxRLG1CQUtQO0VBQ0EsWUFBQTs7QUFLRixLQUFNO0VBQ0wsZ0JBQUE7RUFDQSxjQUFBO0VBQ0EsMEJBQUE7O0FBR0QsS0FBTSxjQUFhO0VBQ2xCLGFBQUE7O0FBSUEsUUFEUSxjQUNQO0VBQ0EsYUFBQTs7QUFGRixRQUFTLGNBS1I7RUFDQyxhQUFBOztBQUlGLFFBQ0M7RUFDQyxpQkFBQTs7QUFJRjtFQUNDLGlCQUFBOztBQURELFNBR0M7RUFDQyxXQUFBO0VBQ0EsaUJBQUE7O0FBTEYsU0FHQyxNQUlDO0VBQ0MsZ0JBQUE7O0FBRUEsU0FQRixNQUlDLFNBR0U7RUFDQSx1QkFBQTtFQUNBLFdBQUE7O0FBRkQsU0FQRixNQUlDLFNBR0UsTUFJQTtFQUNDLDBCQUFBOztBQWZMLFNBR0MsTUFpQkM7RUFDQyxXQUFBO0VBQ0EsYUFBQTs7QUFHRCxTQXRCRCxNQXNCRTtFQUNBLGdCQUFBOztBQUtIO0VBQ0MsZ0JBQUE7RUFDQSxTQUFBO0VBQ0EsVUFBQTtFQUNBLHdCQUFBOztBQUVBLHVCQUFDO0VBQ0EsWUFBQTtFQUNBLFVBQUE7O0FBS0Y7RUFDQyxpQkFBQTs7QUFERCxNQUdDLElBQUc7RUFDRixpQkFBQTs7QUFKRixNQU9DO0VBQ0MsaUJBQUE7O0FBUkYsTUFPQyxjQUdDO0VBQ0MsaUJBQUE7O0FBWEgsTUFPQyxjQU9DO0VBRUMsa0JBQUE7O0FBaEJILE1Bb0JDO0VBQ0Msa0JBQUE7RUFDQSxTQUFBO0VBRUEsU0FBQTtFQUNBLGtCQUFBOztBakJ0RkYsUUFIaUM7RUE2Q2pDLE1pQnVDQztJQVVFLFVBQUE7SUFDQSxpQkFBQTtJQUVBLFVBQUE7SUFDQSxtQkFBQTs7RUFFQSxNQWhCRixXQWdCRztJQUNBLFVBQUE7SUFDQSxXQUFBOzs7QWpCbkdKLFFBSGlDO0VBNkNqQyxNaUJ1Q0M7SUF3QkUsYUFBQTs7O0FDOUlILFFBQVM7RUFDUixvQkFBQTtFZm0yQkMseUJBQUE7RUFDQSxzQkFBQTtFQUNBLHFCQUFBO0VBQ0EsaUJBQUE7RWVwMkJELGVBQUE7O0FBSUQsb0JBQXFCO0VmNmVsQixPQUFBO0VBQVMseUJBQUE7RUFDVixvQkFBQTtFQUNBLGlCQUFBO0VBQ0EsWUFBQTs7QWU1ZUYsWUFBWTtFQUNYLGtCQUFBO0Vmd2VFLE9BQUE7RUFBUywwQkFBQTtFQUNWLGtCQUFBO0VBQ0EsZUFBQTtFQUNBLFVBQUE7O0FldGVELFlBTlcsT0FNVjtFQUNBLFNBQVMsRUFBVDtFQUNBLGNBQUE7RUFDQSxrQkFBQTtFQUNBLE1BQUE7RUFDQSxVQUFBO0VBQ0EsV0FBQTtFQUNBLE9BQUE7RWZnUEEsMEJBQUE7RUFBaUMsb0NBQUE7RUFDakMsdUJBQUE7RUFBOEIsNkJBQUE7RUFDOUIsa0JBQUE7RUFBeUIsNEJBQUE7RUFtQnpCLDRDQUFBO0VBQ0EseUNBQUE7RUFDQSxvQ0FBQTs7QWVwUkYsWUFBWSxPQWtCWDtFZmdRQyx3QkFBQTtFQUNBLHFCQUFBO0VBQ0EsZ0JBQUE7RWVoUUEscUJBQUE7O0FBcEJGLFlBQVksT0F1Qlg7QUF2QkQsWUFBWSxPQXdCWDtFQUNDLGtCQUFBO0VBQ0EsVUFBQTs7QUExQkYsWUFBWSxPQTZCWDtFZnFQQyx3QkFBQTtFQUNBLHFCQUFBO0VBQ0EsZ0JBQUE7O0FlbFBGO0VmdXpCRSx5QkFBQTtFQUNBLHNCQUFBO0VBQ0EscUJBQUE7RUFDQSxpQkFBQTs7QWV2ekJGO0VBQ0MsY0FBQTtFQUNBLG9CQUFBO0VBQ0EsZ0JBQUE7RUFDQSxnQkFBQTs7QUFHQyxRQURELE9BQU0sU0FDSjtFQUNBLGVBQUE7RUFFQSxxQkFBQTtFQUNBLGdCQUFBO0VBQ0EsVUFBQTtFQUNBLHlCQUFBOztBQUdELFFBVkQsT0FBTSxTQVVKO0VBQ0EsYUFBQTs7QUFHRCxRQWRELE9BQU0sU0FjSjtFQUNBLFdBQUE7RUFDQSxpQkFBQTs7QUFHRCxRQW5CRCxPQUFNLFNBbUJKO0VBQ0EsWUFBQTtFQUNBLGdCQUFBOztBQTNCSCxRQU1DLE9BQU0sU0F3Qkw7RUFDQyxjQUFBOztBQU1ILGdCQUFnQjtBQUNoQixnQkFBZ0I7QUFDaEIsc0JBQXNCO0VBQ3JCLGFBQUE7O0FBR0QsZ0JBQWlCO0FBQ2pCLFFBQVMsT0FBTTtBQUNmLGdCQUFpQixTQUFRLFdBQVc7RUFDbkMsYUFBQTs7QUFHRCxHQUFHO0VBQ0YsVUFBQTtFQUNBLGdCQUFBO0VBQ0EsV0FBQTtFQUNBLGtCQUFBO0VBQ0EsY0FBQTtFQUNBLHlCQUFBOztBQU5ELEdBQUcsZ0JBUUY7RUFDQyxTQUFBOztBQVRGLEdBQUcsZ0JBWUY7RUFDQyxhQUFBOztBQUdELEdBaEJFLGdCQWdCRCxjQUFlO0VBQ2Ysa0JBQUE7RWZrWUMsT0FBQTtFQUFTLHlCQUFBO0VBQ1Ysb0JBQUE7RUFDQSxpQkFBQTtFQUNBLFlBQUE7O0FldFpGLEdBQUcsZ0JBdUJGLE9BQU87RUFDTixpQkFBQTs7QUFJRCxHQTVCRSxnQkE0QkE7RUFDRCxVQUFBO0VBQ0EsU0FBQTtFQUNBLDBCQUFBO0VBQ0EsV0FBQTs7QUFFQSxHQWxDQyxnQkE0QkEsS0FNQTtFQUNBLDJCQUFBOztBQUdELEdBdENDLGdCQTRCQSxLQVVBO0VBQ0Esd0JBQUE7O0FBWEYsR0E1QkUsZ0JBNEJBLEtBZUQ7RUFDQyxVQUFBO0VBQ0EsU0FBQTs7QUFJRCxHQWpEQyxnQkE0QkEsS0FxQkM7RUFDRCxrQkFBQTs7QUFFQSxHQXBEQSxnQkE0QkEsS0FxQkMsS0FHQTtFQUNBLGlCQUFBO0VBQ0EsaUJBQUE7RUFDQSxlQUFBOztBQUdELEdBMURBLGdCQTRCQSxLQXFCQyxLQVNBO0FBQ0QsR0EzREEsZ0JBNEJBLEtBcUJDLEtBVUE7RUFDQSw2QkFBQTs7QUFHRCxHQS9EQSxnQkE0QkEsS0FxQkMsS0FjQTtFQUNBLDBCQUFBOztBQUVBLEdBbEVELGdCQTRCQSxLQXFCQyxLQWNBLHNCQUdDO0VBQ0EsZ0JBQUE7O0FBSUYsR0F2RUEsZ0JBNEJBLEtBcUJDLEtBc0JBLG1CQUFtQjtBQUNwQixHQXhFQSxnQkE0QkEsS0FxQkMsS0F1QkEsbUJBQW1CO0FBQ3BCLEdBekVBLGdCQTRCQSxLQXFCQyxLQXdCQSx1QkFBdUI7QUFDeEIsR0ExRUEsZ0JBNEJBLEtBcUJDLEtBeUJBLHVCQUF1QjtFQUN2QixnQkFBQTtFQUNBLFVBQUE7O0FBR0QsR0EvRUEsZ0JBNEJBLEtBcUJDLEtBOEJBLG1CQUFtQjtBQUNwQixHQWhGQSxnQkE0QkEsS0FxQkMsS0ErQkEsdUJBQXVCO0FBQ3hCLEdBakZBLGdCQTRCQSxLQXFCQyxLQWdDQSxtQkFBbUIsT0FBTztBQUMzQixHQWxGQSxnQkE0QkEsS0FxQkMsS0FpQ0EsdUJBQXVCLE9BQU87RUFDOUIsbUJBQUE7O0FBR0QsR0F0RkEsZ0JBNEJBLEtBcUJDLEtBcUNBLG1CQUFtQjtBQUNwQixHQXZGQSxnQkE0QkEsS0FxQkMsS0FzQ0EsdUJBQXVCO0VBQ3ZCLG1CQUFBOztBQUdELEdBM0ZBLGdCQTRCQSxLQXFCQyxLQTBDQTtFQU1BLG1CQUFBOztBQUxBLEdBNUZELGdCQTRCQSxLQXFCQyxLQTBDQSx1QkFDQztFQUNBLFNBQVMsRUFBVDtFQUNBLFdBQUE7O0FBS0QsR0FuR0QsZ0JBNEJBLEtBcUJDLEtBMENBLHVCQVFFO0VBQ0QsZ0JBQUE7O0FBSUYsR0F4R0EsZ0JBNEJBLEtBcUJDLEtBdURDO0FBQUssR0F4R1AsZ0JBNEJBLEtBcUJDLEtBdURRO0VBQ1IsbUJBQUE7RUFDQSxzQkFBQTs7QUF6REYsR0FqREMsZ0JBNEJBLEtBcUJDLEtBNkREO0VBQ0MsaUJBQUE7RUFDQSxnQkFBQTs7QUEvREYsR0FqREMsZ0JBNEJBLEtBcUJDLEtBNkRELEVBSUM7RUFDQyxtQkFBQTtFQUNBLGVBQUE7O0FBbkVILEdBakRDLGdCQTRCQSxLQXFCQyxLQTZERCxFQUlDLEtBSUM7RUFDQyxtQkFBQTtFQUNBLGlCQUFBO0VBQ0EsZUFBQTtFQUNBLGdCQUFBO0VmNkNKLDBCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHVCQUFBO0VBQThCLDZCQUFBO0VBQzlCLGtCQUFBO0VBQXlCLDRCQUFBO0VlN0NyQixzQkFBQTtFQUNBLGNBQUE7RUFDQSxpQkFBQTtFQUNBLFlBQUE7O0FBR0MsR0FsSUosZ0JBNEJBLEtBcUJDLEtBNkRELEVBSUMsS0FJQyxPQVdFLElBQUksV0FDSDtBQUNELEdBbklKLGdCQTRCQSxLQXFCQyxLQTZERCxFQUlDLEtBSUMsT0FXRSxJQUFJLFdBRUg7RUFDQSxXQUFBO0VBQ0EseUJBQUE7RUFDQSx5QkFBQTs7QUFJRixHQTFJSCxnQkE0QkEsS0FxQkMsS0E2REQsRUFJQyxLQUlDLE9Bb0JFLEtBQUs7RUFDTCxlQUFBO0Vmd1FKLE9BQUE7RUFBUyx5QkFBQTtFQUNWLG9CQUFBO0VBQ0EsaUJBQUE7RUFDQSxZQUFBOztBZXJXQSxHQWpEQyxnQkE0QkEsS0FxQkMsS0FrR0Q7RUFDQyx5QkFBQTtFQUNBLFVBQUE7RUFDQSxXQUFBOztBQXJHRixHQWpEQyxnQkE0QkEsS0FxQkMsS0FrR0QsR0FNQztFQUNDLGtCQUFBO0VBQ0EscUJBQUE7RUFHQSxjQUFBOztBQUVBLEdBaEtGLGdCQTRCQSxLQXFCQyxLQWtHRCxHQU1DLEdBT0U7RUFDQSwwQkFBQTs7QUFFQSxHQW5LSCxnQkE0QkEsS0FxQkMsS0FrR0QsR0FNQyxHQU9FLHNCQUdDO0VBQ0EsYUFBQTs7QUFKRixHQWhLRixnQkE0QkEsS0FxQkMsS0FrR0QsR0FNQyxHQU9FLHNCQU9BO0VmQUosMEJBQUE7RUFBaUMsb0NBQUE7RUFDakMsdUJBQUE7RUFBOEIsNkJBQUE7RUFDOUIsa0JBQUE7RUFBeUIsNEJBQUE7RWVBcEIsY0FBQTs7QUFFQSxHQTNLSixnQkE0QkEsS0FxQkMsS0FrR0QsR0FNQyxHQU9FLHNCQU9BLHFCQUlFO0VBQ0EsK0JBQUE7O0FBWkgsR0FoS0YsZ0JBNEJBLEtBcUJDLEtBa0dELEdBTUMsR0FPRSxzQkFPQSxxQkFRQztFQUNDLHNCQUFBOztBQUtILEdBckxGLGdCQTRCQSxLQXFCQyxLQWtHRCxHQU1DLEdBNEJHO0FBQUssR0FyTFQsZ0JBNEJBLEtBcUJDLEtBa0dELEdBTUMsR0E0QlU7RUFDUixtQkFBQTtFQUNBLHNCQUFBOztBQXRJSixHQWpEQyxnQkE0QkEsS0FxQkMsS0FrR0QsR0FNQyxHQWtDQztFQUNDLFVBQUE7O0FBM0lKLEdBakRDLGdCQTRCQSxLQXFCQyxLQWtHRCxHQU1DLEdBa0NDLEdBSUM7RUFDQyxVQUFBO0VBQ0EscUJBQUE7RUFDQSxlQUFBO0VBQ0EscUJBQUE7O0FBbEpMLEdBakRDLGdCQTRCQSxLQXFCQyxLQWtHRCxHQU1DLEdBa0NDLEdBSUMsR0FPQztFQUNDLGVBQUE7RUFDQSxtQkFBQTs7QUF2Sk4sR0FqREMsZ0JBNEJBLEtBcUJDLEtBa0dELEdBTUMsR0FrQ0MsR0FJQyxHQVlDO0FBMUpMLEdBakRDLGdCQTRCQSxLQXFCQyxLQWtHRCxHQU1DLEdBa0NDLEdBSUMsR0FhQztFQUNDLGVBQUE7RUFDQSxTQUFBO0VBQ0Esc0JBQUE7RUFDQSxzQkFBQTtFQ2xTUCxrQkFBQTtFQUNBLG1CQUFBO0VBRUEsb0JBQUE7RUFDQSxvQkFBQTs7QUR3U0EsR0ExTkUsZ0JBME5BO0VBQ0QsZ0JBQUE7O0FBSUQsR0EvTkUsZ0JBK05EO0VBQ0EsWUFBQTtFQUNBLFlBQUE7RUFDQSxnQkFBQTtFQUNBLGdCQUFBO0VBQ0Esa0JBQUE7O0FBTEQsR0EvTkUsZ0JBK05ELE1BT0E7RUN4VEQsa0JBQUE7RUFDQSxrQkFBQTtFQUVBLG9CQUFBO0VBQ0Esb0JBQUE7RURzVEUscUJBQUE7RUFDQSxtQkFBQTtFQUNBLDZCQUFBO0VBQ0EsYUFBQTtFQUNBLG9CQUFBOztBQWJGLEdBL05FLGdCQStORCxNQWdCQTtFQUVDLG9CQUFBO0VBQ0EsZ0JBQUE7O0FBbkJGLEdBL05FLGdCQStORCxNQWdCQSxHQUtDO0VBQ0MsaUJBQUE7RUFDQSxTQUFBO0VBQ0EscUJBQUE7O0FBR0QsR0ExUEEsZ0JBK05ELE1BZ0JBLEdBV0c7RUFDRCxrQkFBQTs7QUE1QkgsR0EvTkUsZ0JBK05ELE1BZ0JBLEdBZUM7RUFDQyxXQUFBO0VBQ0EsVUFBQTtFQUNBLFdBQUE7RUFDQSxpQkFBQTtFQUNBLDBCQUFBO0VBQ0EsbUJBQUE7RUFDQSxtQkFBQTtFZmxFRiw4QkFBQTtFQUNBLDJCQUFBO0VBQ0Esc0JBQUE7O0FlMEJELEdBL05FLGdCQStORCxNQWdCQSxHQWVDLEdBVUM7RUFDQyxnQkFBQTtFQUNBLFlBQUE7RUFDQSxzQkFBQTs7QUE1Q0osR0EvTkUsZ0JBK05ELE1BZ0JBLEdBZ0NDO0VBQ0MsaUJBQUE7RUFDQSxZQUFBO0VBQ0Esb0JBQUE7O0FBbkRILEdBL05FLGdCQStORCxNQWdCQSxHQWdDQyxHQUtDO0VBQ0Msa0JBQUE7O0FBR0QsR0F4UkQsZ0JBK05ELE1BZ0JBLEdBZ0NDLEdBU0U7RUFDQSxTQUFTLE9BQVQ7RUFDQSxjQUFBO0VBQ0EsV0FBQTtFQUNBLFlBQUE7RUFDQSxTQUFBO0VBQ0EsUUFBQTs7QUFPTDtBQUNBLGdCQUFpQixTQUFRO0FBQ3pCO0VmaElFLDBCQUFBO0VBQWlDLG9DQUFBO0VBQ2pDLHVCQUFBO0VBQThCLDZCQUFBO0VBQzlCLGtCQUFBO0VBQXlCLDRCQUFBO0VlZ0kxQixzQkFBQTtFQzNYQSxrQkFBQTtFQUNBLG1CQUFBO0VBRUEsb0JBQUE7RUFDQSxvQkFBQTs7QUQyWEQsZ0JBQWlCLFNBQVE7QUFDekIsV0FBWTtBQUNaO0VDallDLGtCQUFBO0VBQ0EsbUJBQUE7RUFFQSxvQkFBQTtFQUNBLG9CQUFBO0VEK1hBLGdKQUFBOztBQUdELFdBQVk7RUFDWCxZQUFBO0VBQ0EsVUFBQTtFQUNBLFNBQUE7O0FBR0QsZ0JBQWlCLFNBQVE7RWZ2SHZCLDhCQUFBO0VBQ0EsMkJBQUE7RUFDQSxzQkFBQTtFZXVIRCxjQUFBO0VBQ0EsYUFBQTtFQUNBLFdBQUE7RUFDQSxpQkFBQTtFQUNBLFNBQUE7RUFDQSxZQUFBO0VBQ0EsYUFBQTtFQUNBLGdCQUFBO0VBQ0EsV0FBQTtFQUNBLGdCQUFBO0VBQ0EsaUJBQUE7RUFDQSxjQUFBOztBQUdELGlCQUFpQjtFQUNoQixVQUFBO0VBQ0EsY0FBQTtFQzlaQSxlQUFBO0VBQ0EsbUJBQUE7RUFFQSxtQkFBQTtFQUNBLG9CQUFBO0VEa2FBLGdKQUFBOztBQVZELGlCQUFpQixpQkFJaEI7RUFDQyxjQUFBO0VBQ0EsbUJBQUE7O0FBTUQsaUJBWmdCLGlCQVlkLEtBQUk7RUFDTCxtQkFBQTs7O0FBS0Y7RUFDQyxvQkFBQTs7QUFERCx1QkFHQyxTQUFTO0VBQ1IsbUJBQUE7RUFDQSxpQkFBQTs7QUFMRix1QkFRQztFQUVDLFlBQUE7RUFHQSxnQkFBQTs7QUFJRjtFQUNDLGVBQUE7RUFDQSxNQUFBO0VBQ0EsV0FBQTtFQUNBLFdBQUE7O0FBSkQsZ0JBTUM7RUFDQyxpQkFBQTs7QUFQRixnQkFNQyxrQkFHQztFQUNDLGdCQUFBOztBQVZILGdCQU1DLGtCQUdDLE9BR0M7RUFDQyxnQkFBQTs7QUFPSjtFQUNDLGtCQUFBO0VBQ0EsVUFBQTtFQUNBLFVBQUE7O0FBRUEsS0FBQztFQUNBLGFBQUEifQ== */
diff --git a/sources/samples/img/github-top.png b/sources/samples/img/github-top.png
new file mode 100644
index 0000000..7b9cbb1
--- /dev/null
+++ b/sources/samples/img/github-top.png
Binary files differ
diff --git a/sources/samples/img/header-bg.png b/sources/samples/img/header-bg.png
new file mode 100644
index 0000000..a14166a
--- /dev/null
+++ b/sources/samples/img/header-bg.png
Binary files differ
diff --git a/sources/samples/img/header-separator.png b/sources/samples/img/header-separator.png
new file mode 100644
index 0000000..8c4fb9b
--- /dev/null
+++ b/sources/samples/img/header-separator.png
Binary files differ
diff --git a/sources/samples/img/logo.png b/sources/samples/img/logo.png
new file mode 100644
index 0000000..96d86e2
--- /dev/null
+++ b/sources/samples/img/logo.png
Binary files differ
diff --git a/sources/samples/img/navigation-tip.png b/sources/samples/img/navigation-tip.png
new file mode 100644
index 0000000..2286114
--- /dev/null
+++ b/sources/samples/img/navigation-tip.png
Binary files differ
diff --git a/sources/samples/index.html b/sources/samples/index.html
new file mode 100644
index 0000000..3d274cf
--- /dev/null
+++ b/sources/samples/index.html
@@ -0,0 +1,128 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>CKEditor Sample</title>
10 <script src="../ckeditor.js"></script>
11 <script src="js/sample.js"></script>
12 <link rel="stylesheet" href="css/samples.css">
13 <link rel="stylesheet" href="toolbarconfigurator/lib/codemirror/neo.css">
14</head>
15<body id="main">
16
17<nav class="navigation-a">
18 <div class="grid-container">
19 <ul class="navigation-a-left grid-width-70">
20 <li><a href="http://ckeditor.com">Project Homepage</a></li>
21 <li><a href="http://dev.ckeditor.com/">I found a bug</a></li>
22 <li><a href="http://github.com/ckeditor/ckeditor-dev" class="icon-pos-right icon-navigation-a-github">Fork CKEditor on GitHub</a></li>
23 </ul>
24 <ul class="navigation-a-right grid-width-30">
25 <li><a href="http://ckeditor.com/blog-list">CKEditor Blog</a></li>
26 </ul>
27 </div>
28</nav>
29
30<header class="header-a">
31 <div class="grid-container">
32 <h1 class="header-a-logo grid-width-30">
33 <a href="index.html"><img src="img/logo.png" alt="CKEditor Sample"></a>
34 </h1>
35
36 <nav class="navigation-b grid-width-70">
37 <ul>
38 <li><a href="index.html" class="button-a button-a-background">Start</a></li>
39 <li><a href="toolbarconfigurator/index.html" class="button-a">Toolbar configurator <span class="balloon-a balloon-a-nw">Edit your toolbar now!</span></a></li>
40 </ul>
41 </nav>
42 </div>
43</header>
44
45<main>
46 <div class="adjoined-top">
47 <div class="grid-container">
48 <div class="content grid-width-100">
49 <h1>Congratulations!</h1>
50 <p>
51 If you can see CKEditor below, it means that the installation succeeded.
52 You can now try out your new editor version, see its features, and when you are ready to move on, check some of the <a href="#sample-customize">most useful resources</a> recommended below.
53 </p>
54 </div>
55 </div>
56 </div>
57 <div class="adjoined-bottom">
58 <div class="grid-container">
59 <div class="grid-width-100">
60 <div id="editor">
61 <h1>Hello world!</h1>
62 <p>I'm an instance of <a href="http://ckeditor.com">CKEditor</a>.</p>
63 </div>
64 </div>
65 </div>
66 </div>
67
68 <div class="grid-container">
69 <div class="content grid-width-100">
70 <section id="sample-customize">
71 <h2>Customize Your Editor</h2>
72 <p>Modular build and <a href="http://docs.ckeditor.com/#!/guide/dev_configuration">numerous configuration options</a> give you nearly endless possibilities to customize CKEditor. Replace the content of your <code><a href="../config.js">config.js</a></code> file with the following code and refresh this page (<strong>remember to clear the browser cache</strong>)!</p>
73 <pre class="cm-s-neo CodeMirror"><code><span style="padding-right: 0.1px;"><span class="cm-variable">CKEDITOR</span>.<span class="cm-property">editorConfig</span> <span class="cm-operator">=</span> <span class="cm-keyword">function</span>( <span class="cm-def">config</span> ) {</span>
74<span style="padding-right: 0.1px;"><span class="cm-tab"> </span><span class="cm-variable-2">config</span>.<span class="cm-property">language</span> <span class="cm-operator">=</span> <span class="cm-string">'es'</span>;</span>
75<span style="padding-right: 0.1px;"><span class="cm-tab"> </span><span class="cm-variable-2">config</span>.<span class="cm-property">uiColor</span> <span class="cm-operator">=</span> <span class="cm-string">'#F7B42C'</span>;</span>
76<span style="padding-right: 0.1px;"><span class="cm-tab"> </span><span class="cm-variable-2">config</span>.<span class="cm-property">height</span> <span class="cm-operator">=</span> <span class="cm-number">300</span>;</span>
77<span style="padding-right: 0.1px;"><span class="cm-tab"> </span><span class="cm-variable-2">config</span>.<span class="cm-property">toolbarCanCollapse</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>;</span>
78<span style="padding-right: 0.1px;">};</span></code></pre>
79 </section>
80
81 <section>
82 <h2>Toolbar Configuration</h2>
83 <p>If you want to reorder toolbar buttons or remove some of them, check <a href="toolbarconfigurator/index.html">this handy tool</a>!</p>
84 </section>
85
86 <section>
87 <h2>More Samples!</h2>
88 <p>Visit the <a href="http://sdk.ckeditor.com">CKEditor SDK</a> for a huge collection of samples showcasing editor features, with source code readily available to copy and use in your own implementation.</p>
89 </section>
90
91 <section>
92 <h2>Developer's Guide</h2>
93 <p>The most important resource for all developers working with CKEditor, integrating it with their websites and applications, and customizing to their needs. You can start from here:</p>
94 <ul>
95 <li><a href="http://docs.ckeditor.com/#!/guide/dev_installation">Getting Started</a> &ndash; Explains most crucial editor concepts and practices as well as the installation process and integration with your website.</li>
96 <li><a href="http://docs.ckeditor.com/#!/guide/dev_advanced_installation">Advanced Installation Concepts</a> &ndash; Describes how to upgrade, install additional components (plugins, skins), or create a custom build.</li>
97 </ul>
98 <p>When you have the basics sorted out, feel free to browse some more advanced sections like:</p>
99 <ul>
100 <li><a href="http://docs.ckeditor.com/#!/guide/dev_features">Functionality Overview</a> &ndash; Descriptions and samples of various editor features.</li>
101 <li><a href="http://docs.ckeditor.com/#!/guide/plugin_sdk_intro">Plugin SDK</a>, <a href="http://docs.ckeditor.com/#!/guide/widget_sdk_intro">Widget SDK</a>, and <a href="http://docs.ckeditor.com/#!/guide/skin_sdk_intro">Skin SDK</a> &ndash; Useful when you want to create your own editor components.</li>
102 </ul>
103 </section>
104
105 <section>
106 <h2>CKEditor JavaScript API</h2>
107 <p>CKEditor boasts a rich <a href="http://docs.ckeditor.com/#!/api">JavaScript API</a> that you can use to adjust the editor to your needs and integrate it with your website or application.</p>
108 </section>
109 </div>
110 </div>
111</main>
112
113<footer class="footer-a grid-container">
114 <div class="grid-container">
115 <p class="grid-width-100">
116 CKEditor &ndash; The text editor for the Internet &ndash; <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
117 </p>
118 <p class="grid-width-100" id="copy">
119 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> &ndash; Frederico Knabben. All rights reserved.
120 </p>
121 </div>
122</footer>
123<script>
124 initSample();
125</script>
126
127</body>
128</html>
diff --git a/sources/samples/js/sample.js b/sources/samples/js/sample.js
new file mode 100644
index 0000000..70cf8d6
--- /dev/null
+++ b/sources/samples/js/sample.js
@@ -0,0 +1,54 @@
1/**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6/* exported initSample */
7
8if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
9 CKEDITOR.tools.enableHtml5Elements( document );
10
11// The trick to keep the editor in the sample quite small
12// unless user specified own height.
13CKEDITOR.config.height = 150;
14CKEDITOR.config.width = 'auto';
15
16var initSample = ( function() {
17 var wysiwygareaAvailable = isWysiwygareaAvailable(),
18 isBBCodeBuiltIn = !!CKEDITOR.plugins.get( 'bbcode' );
19
20 return function() {
21 var editorElement = CKEDITOR.document.getById( 'editor' );
22
23 // :(((
24 if ( isBBCodeBuiltIn ) {
25 editorElement.setHtml(
26 'Hello world!\n\n' +
27 'I\'m an instance of [url=http://ckeditor.com]CKEditor[/url].'
28 );
29 }
30
31 // Depending on the wysiwygare plugin availability initialize classic or inline editor.
32 if ( wysiwygareaAvailable ) {
33 CKEDITOR.replace( 'editor' );
34 } else {
35 editorElement.setAttribute( 'contenteditable', 'true' );
36 CKEDITOR.inline( 'editor' );
37
38 // TODO we can consider displaying some info box that
39 // without wysiwygarea the classic editor may not work.
40 }
41 };
42
43 function isWysiwygareaAvailable() {
44 // If in development mode, then the wysiwygarea must be available.
45 // Split REV into two strings so builder does not replace it :D.
46 if ( CKEDITOR.revision == ( '%RE' + 'V%' ) ) {
47 return true;
48 }
49
50 return !!CKEDITOR.plugins.get( 'wysiwygarea' );
51 }
52} )();
53
54// %LEAVE_UNMINIFIED% %REMOVE_LINE%
diff --git a/sources/samples/js/sf.js b/sources/samples/js/sf.js
new file mode 100644
index 0000000..d42dbd8
--- /dev/null
+++ b/sources/samples/js/sf.js
@@ -0,0 +1,673 @@
1/**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5/* exported SF */
6
7'use strict';
8
9var SF = ( function() {
10 var SF = {};
11
12 SF.attachListener = function( elem, evtName, callback ) {
13 if ( elem.addEventListener ) {
14 elem.addEventListener( evtName, callback, false );
15 } else if ( elem.attachEvent ) {
16 elem.attachEvent( 'on' + evtName , function() {
17 callback.apply( elem, arguments );
18 } );
19 } else {
20 throw new Error( 'Could not attach event.' );
21 }
22 };
23
24 SF.indexOf = ( function() {
25 var indexOf = Array.prototype.indexOf;
26
27 if ( indexOf === 'function' ) {
28 return function( arr, elem ) {
29 return indexOf.call( arr, elem );
30 };
31 } else {
32 return function( arr, elem ) {
33 var max = arr.length;
34
35 for ( var i = 0; i < max; i++ ) {
36 if ( arr[ i ] === elem ) {
37 return i;
38 }
39 }
40
41 return -1;
42 };
43 }
44
45 }() );
46
47 SF.accept = function( node, visitor ) {
48 var children;
49
50 // Handling node as a node and array
51 if ( node.children ) {
52 children = node.children;
53
54 visitor( node );
55 } else if ( typeof node.length === 'number' ) {
56 children = node;
57 }
58
59 var i = children ? ( children.length || 0 ) : 0;
60 while ( i-- ) {
61 SF.accept( children[ i ], visitor );
62 }
63 };
64
65 SF.getByClass = ( function( ) {
66 var getByClass = document.getElementsByClassName;
67 if ( typeof getByClass === 'function' ) {
68 return function( root, className ) {
69 if ( typeof root === 'string' ) {
70 className = root;
71 root = document;
72 }
73
74 return getByClass.call( root, className );
75 };
76 }
77
78 return function( root, className ) {
79 if ( typeof root === 'string' ) {
80 className = root;
81 root = document.getElementsByTagName( 'html' )[ 0 ];
82 }
83 var results = [];
84
85 SF.accept( root, function( elem ) {
86 if ( SF.classList.contains( elem, className ) ) {
87 results.push( elem );
88 }
89 } );
90
91 return results;
92 };
93 }() );
94
95 SF.classList = {};
96
97 SF.classList.add = function( elem, className ) {
98 var classes = parseClasses( elem );
99 classes.push( className );
100
101 elem.attributes.setNamedItem( createClassAttr( classes ) );
102 };
103
104 SF.classList.remove = function( elem, className ) {
105 var classes = parseClasses( elem, className ),
106 foundAt = SF.indexOf( classes, className );
107
108 if ( foundAt === -1 ) {
109 return;
110 }
111
112 classes.splice( foundAt, 1 );
113 elem.attributes.setNamedItem( createClassAttr( classes ) );
114 };
115
116 SF.classList.contains = function( elem, className ) {
117 return findIndex( elem, className ) !== -1;
118 };
119
120 SF.classList.toggle = function( elem, className ) {
121 this.contains( elem, className ) ? this.remove( elem, className ) : this.add( elem, className );
122 };
123
124 function findIndex( elem, className ) {
125 return SF.indexOf( parseClasses( elem ), className );
126 }
127
128 function parseClasses( elem ) {
129 var classAttr = elem.attributes ? elem.attributes.getNamedItem( 'class' ) : null;
130
131 return classAttr ? classAttr.value.split( ' ' ) : [];
132 }
133
134 function createClassAttr( classesArray ) {
135 var attr = document.createAttribute( 'class' );
136
137 attr.value = classesArray.join( ' ' );
138
139 return attr;
140 }
141
142 return SF;
143}() );
144
145/* global SF, picoModal */
146
147'use strict';
148
149( function() {
150 // Purges all styles in passed object.
151 function purgeStyles( styles ) {
152 for ( var i in styles ) {
153 delete styles[ i ];
154 }
155 }
156
157 SF.modal = function( config ) {
158 // Modal should use the same style set as the rest of the page (.content component).
159 config.modalClass = 'modal content';
160 config.closeClass = 'modal-close';
161
162 // Purge all pre-defined pico styles. Use the lessfile instead.
163 config.modalStyles = purgeStyles;
164
165 // Close button styles are customized via lessfile.
166 config.closeStyles = purgeStyles;
167
168 var userDefinedAfterCreate = config.afterCreate,
169 userDefinedAfterClose = config.afterClose;
170
171 // Close modal on ESC key.
172 function onKeyDown( event ) {
173 if ( event.keyCode == 27 ) {
174 modal.close();
175 }
176 }
177
178 // Use afterCreate as a config option rather than function chain.
179 config.afterCreate = function( modal ) {
180 userDefinedAfterCreate && userDefinedAfterCreate( modal );
181
182 window.addEventListener( 'keydown', onKeyDown );
183 };
184
185 // Use afterClose as a config option rather than function chain.
186 config.afterClose = function( modal ) {
187 userDefinedAfterClose && userDefinedAfterClose( modal );
188
189 window.removeEventListener( 'keydown', onKeyDown );
190 };
191
192 var modal = new picoModal( config )
193 .afterCreate( config.afterCreate )
194 .afterClose( config.afterClose );
195
196 return modal;
197 };
198} )();
199'use strict';
200
201( function() {
202 // All .tree-a elements in DOM.
203 var expanders = SF.getByClass( 'toggler' );
204
205 var i = expanders.length;
206 while ( i-- ) {
207 var expander = expanders[ i ];
208
209 SF.attachListener( expander, 'click', function() {
210 var containsIcon = SF.classList.contains( this, 'icon-toggler-expanded' ) || SF.classList.contains( this, 'icon-toggler-collapsed' ),
211 related = document.getElementById( this.getAttribute( 'data-for' ) );
212
213 SF.classList.toggle( this, 'collapsed' );
214
215 if ( SF.classList.contains( this, 'collapsed' ) ) {
216 SF.classList.add( related, 'collapsed' );
217 if ( containsIcon ) {
218 SF.classList.remove( this, 'icon-toggler-expanded' );
219 SF.classList.add( this, 'icon-toggler-collapsed' );
220 }
221 } else {
222 SF.classList.remove( related, 'collapsed' );
223 if ( containsIcon ) {
224 SF.classList.remove( this, 'icon-toggler-collapsed' );
225 SF.classList.add( this, 'icon-toggler-expanded' );
226 }
227 }
228 } );
229 }
230} )();
231/* global SF */
232
233'use strict';
234
235( function() {
236 // All .tree-a elements in DOM.
237 var trees = SF.getByClass( 'tree-a' );
238
239 for ( var i = trees.length; i--; ) {
240 var tree = trees[ i ];
241
242 SF.attachListener( tree, 'click', function( evt ) {
243 var target = evt.target || evt.srcElement;
244
245 // Collapse or expand item groups.
246 if ( target.nodeName === 'H2' && !SF.classList.contains( target, 'tree-a-no-sub' ) ) {
247 SF.classList.toggle( target, 'tree-a-active' );
248 }
249 } );
250 }
251} )();
252// jshint ignore:start
253// jscs:disable
254/**
255 * Permission is hereby granted, free of charge, to any person obtaining a copy
256 * of this software and associated documentation files (the "Software"), to deal
257 * in the Software without restriction, including without limitation the rights
258 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
259 * copies of the Software, and to permit persons to whom the Software is
260 * furnished to do so, subject to the following conditions:
261 *
262 * The above copyright notice and this permission notice shall be included in
263 * all copies or substantial portions of the Software.
264 *
265 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
266 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
267 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
268 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
269 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
270 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
271 * SOFTWARE.
272 */
273
274/**
275 * A self-contained modal library
276 */
277(function(window, document) {
278 "use strict";
279
280 /** Returns whether a value is a dom node */
281 function isNode(value) {
282 if ( typeof Node === "object" ) {
283 return value instanceof Node;
284 }
285 else {
286 return value &&
287 typeof value === "object" &&
288 typeof value.nodeType === "number";
289 }
290 }
291
292 /** Returns whether a value is a string */
293 function isString(value) {
294 return typeof value === "string";
295 }
296
297 /**
298 * Generates observable objects that can be watched and triggered
299 */
300 function observable() {
301 var callbacks = [];
302 return {
303 watch: callbacks.push.bind(callbacks),
304 trigger: function( modal ) {
305
306 var unprevented = true;
307 var event = {
308 preventDefault: function preventDefault () {
309 unprevented = false;
310 }
311 };
312
313 for (var i = 0; i < callbacks.length; i++) {
314 callbacks[i](modal, event);
315 }
316
317 return unprevented;
318 }
319 };
320 }
321
322
323 /**
324 * A small interface for creating and managing a dom element
325 */
326 function Elem( elem ) {
327 this.elem = elem;
328 }
329
330 /**
331 * Creates a new div
332 */
333 Elem.div = function ( parent ) {
334 var elem = document.createElement('div');
335 (parent || document.body).appendChild(elem);
336 return new Elem(elem);
337 };
338
339 Elem.prototype = {
340
341 /** Creates a child of this node */
342 child: function () {
343 return Elem.div(this.elem);
344 },
345
346 /** Applies a set of styles to an element */
347 stylize: function(styles) {
348 styles = styles || {};
349
350 if ( typeof styles.opacity !== "undefined" ) {
351 styles.filter =
352 "alpha(opacity=" + (styles.opacity * 100) + ")";
353 }
354
355 for (var prop in styles) {
356 if (styles.hasOwnProperty(prop)) {
357 this.elem.style[prop] = styles[prop];
358 }
359 }
360
361 return this;
362 },
363
364 /** Adds a class name */
365 clazz: function (clazz) {
366 this.elem.className += " " + clazz;
367 return this;
368 },
369
370 /** Sets the HTML */
371 html: function (content) {
372 if ( isNode(content) ) {
373 this.elem.appendChild( content );
374 }
375 else {
376 this.elem.innerHTML = content;
377 }
378 return this;
379 },
380
381 /** Adds a click handler to this element */
382 onClick: function(callback) {
383 this.elem.addEventListener('click', callback);
384 return this;
385 },
386
387 /** Removes this element from the DOM */
388 destroy: function() {
389 document.body.removeChild(this.elem);
390 },
391
392 /** Hides this element */
393 hide: function() {
394 this.elem.style.display = "none";
395 },
396
397 /** Shows this element */
398 show: function() {
399 this.elem.style.display = "block";
400 },
401
402 /** Sets an attribute on this element */
403 attr: function ( name, value ) {
404 this.elem.setAttribute(name, value);
405 return this;
406 },
407
408 /** Executes a callback on all the ancestors of an element */
409 anyAncestor: function ( predicate ) {
410 var elem = this.elem;
411 while ( elem ) {
412 if ( predicate( new Elem(elem) ) ) {
413 return true;
414 }
415 else {
416 elem = elem.parentNode;
417 }
418 }
419 return false;
420 }
421 };
422
423
424 /** Generates the grey-out effect */
425 function buildOverlay( getOption, close ) {
426 return Elem.div()
427 .clazz("pico-overlay")
428 .clazz( getOption("overlayClass", "") )
429 .stylize({
430 display: "block",
431 position: "fixed",
432 top: "0px",
433 left: "0px",
434 height: "100%",
435 width: "100%",
436 zIndex: 10000
437 })
438 .stylize(getOption('overlayStyles', {
439 opacity: 0.5,
440 background: "#000"
441 }))
442 .onClick(function () {
443 if ( getOption('overlayClose', true) ) {
444 close();
445 }
446 });
447 }
448
449 /** Builds the content of a modal */
450 function buildModal( getOption, close ) {
451 var width = getOption('width', 'auto');
452 if ( typeof width === "number" ) {
453 width = "" + width + "px";
454 }
455
456 var elem = Elem.div()
457 .clazz("pico-content")
458 .clazz( getOption("modalClass", "") )
459 .stylize({
460 display: 'block',
461 position: 'fixed',
462 zIndex: 10001,
463 left: "50%",
464 top: "50px",
465 width: width,
466 '-ms-transform': 'translateX(-50%)',
467 '-moz-transform': 'translateX(-50%)',
468 '-webkit-transform': 'translateX(-50%)',
469 '-o-transform': 'translateX(-50%)',
470 'transform': 'translateX(-50%)'
471 })
472 .stylize(getOption('modalStyles', {
473 backgroundColor: "white",
474 padding: "20px",
475 borderRadius: "5px"
476 }))
477 .html( getOption('content') )
478 .attr("role", "dialog")
479 .onClick(function (event) {
480 var isCloseClick = new Elem(event.target)
481 .anyAncestor(function (elem) {
482 return /\bpico-close\b/.test(elem.elem.className);
483 });
484 if ( isCloseClick ) {
485 close();
486 }
487 });
488
489 return elem;
490 }
491
492 /** Builds the close button */
493 function buildClose ( elem, getOption ) {
494 if ( getOption('closeButton', true) ) {
495 return elem.child()
496 .html( getOption('closeHtml', "&#xD7;") )
497 .clazz("pico-close")
498 .clazz( getOption("closeClass") )
499 .stylize( getOption('closeStyles', {
500 borderRadius: "2px",
501 cursor: "pointer",
502 height: "15px",
503 width: "15px",
504 position: "absolute",
505 top: "5px",
506 right: "5px",
507 fontSize: "16px",
508 textAlign: "center",
509 lineHeight: "15px",
510 background: "#CCC"
511 }) );
512 }
513 }
514
515 /** Builds a method that calls a method and returns an element */
516 function buildElemAccessor( builder ) {
517 return function () {
518 return builder().elem;
519 };
520 }
521
522
523 /**
524 * Displays a modal
525 */
526 function picoModal(options) {
527
528 if ( isString(options) || isNode(options) ) {
529 options = { content: options };
530 }
531
532 var afterCreateEvent = observable();
533 var beforeShowEvent = observable();
534 var afterShowEvent = observable();
535 var beforeCloseEvent = observable();
536 var afterCloseEvent = observable();
537
538 /**
539 * Returns a named option if it has been explicitly defined. Otherwise,
540 * it returns the given default value
541 */
542 function getOption ( opt, defaultValue ) {
543 var value = options[opt];
544 if ( typeof value === "function" ) {
545 value = value( defaultValue );
546 }
547 return value === undefined ? defaultValue : value;
548 }
549
550 /** Hides this modal */
551 function forceClose () {
552 shadowElem().hide();
553 modalElem().hide();
554 afterCloseEvent.trigger(iface);
555 }
556
557 /** Gracefully hides this modal */
558 function close () {
559 if ( beforeCloseEvent.trigger(iface) ) {
560 forceClose();
561 }
562 }
563
564 /** Wraps a method so it returns the modal interface */
565 function returnIface ( callback ) {
566 return function () {
567 callback.apply(this, arguments);
568 return iface;
569 };
570 }
571
572
573 // The constructed dom nodes
574 var built;
575
576 /** Builds a method that calls a method and returns an element */
577 function build ( name ) {
578 if ( !built ) {
579 var modal = buildModal(getOption, close);
580 built = {
581 modal: modal,
582 overlay: buildOverlay(getOption, close),
583 close: buildClose(modal, getOption)
584 };
585 afterCreateEvent.trigger(iface);
586 }
587 return built[name];
588 }
589
590 var modalElem = build.bind(window, 'modal');
591 var shadowElem = build.bind(window, 'overlay');
592 var closeElem = build.bind(window, 'close');
593
594
595 var iface = {
596
597 /** Returns the wrapping modal element */
598 modalElem: buildElemAccessor(modalElem),
599
600 /** Returns the close button element */
601 closeElem: buildElemAccessor(closeElem),
602
603 /** Returns the overlay element */
604 overlayElem: buildElemAccessor(shadowElem),
605
606 /** Shows this modal */
607 show: function () {
608 if ( beforeShowEvent.trigger(iface) ) {
609 shadowElem().show();
610 closeElem();
611 modalElem().show();
612 afterShowEvent.trigger(iface);
613 }
614 return this;
615 },
616
617 /** Hides this modal */
618 close: returnIface(close),
619
620 /**
621 * Force closes this modal. This will not call beforeClose
622 * events and will just immediately hide the modal
623 */
624 forceClose: returnIface(forceClose),
625
626 /** Destroys this modal */
627 destroy: function () {
628 modalElem = modalElem().destroy();
629 shadowElem = shadowElem().destroy();
630 closeElem = undefined;
631 },
632
633 /**
634 * Updates the options for this modal. This will only let you
635 * change options that are re-evaluted regularly, such as
636 * `overlayClose`.
637 */
638 options: function ( opts ) {
639 options = opts;
640 },
641
642 /** Executes after the DOM nodes are created */
643 afterCreate: returnIface(afterCreateEvent.watch),
644
645 /** Executes a callback before this modal is closed */
646 beforeShow: returnIface(beforeShowEvent.watch),
647
648 /** Executes a callback after this modal is shown */
649 afterShow: returnIface(afterShowEvent.watch),
650
651 /** Executes a callback before this modal is closed */
652 beforeClose: returnIface(beforeCloseEvent.watch),
653
654 /** Executes a callback after this modal is closed */
655 afterClose: returnIface(afterCloseEvent.watch)
656 };
657
658 return iface;
659 }
660
661 if ( typeof window.define === "function" && window.define.amd ) {
662 window.define(function () {
663 return picoModal;
664 });
665 }
666 else {
667 window.picoModal = picoModal;
668 }
669
670}(window, document));
671
672// jscs:enable
673// jshint ignore:end
diff --git a/sources/samples/old/ajax.html b/sources/samples/old/ajax.html
new file mode 100644
index 0000000..852a086
--- /dev/null
+++ b/sources/samples/old/ajax.html
@@ -0,0 +1,85 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Ajax &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link rel="stylesheet" href="sample.css">
12 <script>
13
14 var editor, html = '';
15
16 function createEditor() {
17 if ( editor )
18 return;
19
20 // Create a new editor inside the <div id="editor">, setting its value to html
21 var config = {};
22 editor = CKEDITOR.appendTo( 'editor', config, html );
23 }
24
25 function removeEditor() {
26 if ( !editor )
27 return;
28
29 // Retrieve the editor contents. In an Ajax application, this data would be
30 // sent to the server or used in any other way.
31 document.getElementById( 'editorcontents' ).innerHTML = html = editor.getData();
32 document.getElementById( 'contents' ).style.display = '';
33
34 // Destroy the editor.
35 editor.destroy();
36 editor = null;
37 }
38
39 </script>
40</head>
41<body>
42 <h1 class="samples">
43 <a href="index.html">CKEditor Samples</a> &raquo; Create and Destroy Editor Instances for Ajax Applications
44 </h1>
45 <div class="warning deprecated">
46 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/saveajax.html">brand new version in CKEditor SDK</a>.
47 </div>
48 <div class="description">
49 <p>
50 This sample shows how to create and destroy CKEditor instances on the fly. After the removal of CKEditor the content created inside the editing
51 area will be displayed in a <code>&lt;div&gt;</code> element.
52 </p>
53 <p>
54 For details of how to create this setup check the source code of this sample page
55 for JavaScript code responsible for the creation and destruction of a CKEditor instance.
56 </p>
57 </div>
58 <p>Click the buttons to create and remove a CKEditor instance.</p>
59 <p>
60 <input onclick="createEditor();" type="button" value="Create Editor">
61 <input onclick="removeEditor();" type="button" value="Remove Editor">
62 </p>
63 <!-- This div will hold the editor. -->
64 <div id="editor">
65 </div>
66 <div id="contents" style="display: none">
67 <p>
68 Edited Contents:
69 </p>
70 <!-- This div will be used to display the editor contents. -->
71 <div id="editorcontents">
72 </div>
73 </div>
74 <div id="footer">
75 <hr>
76 <p>
77 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
78 </p>
79 <p id="copy">
80 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
81 Knabben. All rights reserved.
82 </p>
83 </div>
84</body>
85</html>
diff --git a/sources/samples/old/api.html b/sources/samples/old/api.html
new file mode 100644
index 0000000..682a719
--- /dev/null
+++ b/sources/samples/old/api.html
@@ -0,0 +1,210 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>API Usage &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link href="sample.css" rel="stylesheet">
12 <script>
13
14// The instanceReady event is fired, when an instance of CKEditor has finished
15// its initialization.
16CKEDITOR.on( 'instanceReady', function( ev ) {
17 // Show the editor name and description in the browser status bar.
18 document.getElementById( 'eMessage' ).innerHTML = 'Instance <code>' + ev.editor.name + '<\/code> loaded.';
19
20 // Show this sample buttons.
21 document.getElementById( 'eButtons' ).style.display = 'block';
22});
23
24function InsertHTML() {
25 // Get the editor instance that we want to interact with.
26 var editor = CKEDITOR.instances.editor1;
27 var value = document.getElementById( 'htmlArea' ).value;
28
29 // Check the active editing mode.
30 if ( editor.mode == 'wysiwyg' )
31 {
32 // Insert HTML code.
33 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertHtml
34 editor.insertHtml( value );
35 }
36 else
37 alert( 'You must be in WYSIWYG mode!' );
38}
39
40function InsertText() {
41 // Get the editor instance that we want to interact with.
42 var editor = CKEDITOR.instances.editor1;
43 var value = document.getElementById( 'txtArea' ).value;
44
45 // Check the active editing mode.
46 if ( editor.mode == 'wysiwyg' )
47 {
48 // Insert as plain text.
49 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertText
50 editor.insertText( value );
51 }
52 else
53 alert( 'You must be in WYSIWYG mode!' );
54}
55
56function SetContents() {
57 // Get the editor instance that we want to interact with.
58 var editor = CKEDITOR.instances.editor1;
59 var value = document.getElementById( 'htmlArea' ).value;
60
61 // Set editor contents (replace current contents).
62 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setData
63 editor.setData( value );
64}
65
66function GetContents() {
67 // Get the editor instance that you want to interact with.
68 var editor = CKEDITOR.instances.editor1;
69
70 // Get editor contents
71 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-getData
72 alert( editor.getData() );
73}
74
75function ExecuteCommand( commandName ) {
76 // Get the editor instance that we want to interact with.
77 var editor = CKEDITOR.instances.editor1;
78
79 // Check the active editing mode.
80 if ( editor.mode == 'wysiwyg' )
81 {
82 // Execute the command.
83 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-execCommand
84 editor.execCommand( commandName );
85 }
86 else
87 alert( 'You must be in WYSIWYG mode!' );
88}
89
90function CheckDirty() {
91 // Get the editor instance that we want to interact with.
92 var editor = CKEDITOR.instances.editor1;
93 // Checks whether the current editor contents present changes when compared
94 // to the contents loaded into the editor at startup
95 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-checkDirty
96 alert( editor.checkDirty() );
97}
98
99function ResetDirty() {
100 // Get the editor instance that we want to interact with.
101 var editor = CKEDITOR.instances.editor1;
102 // Resets the "dirty state" of the editor (see CheckDirty())
103 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-resetDirty
104 editor.resetDirty();
105 alert( 'The "IsDirty" status has been reset' );
106}
107
108function Focus() {
109 CKEDITOR.instances.editor1.focus();
110}
111
112function onFocus() {
113 document.getElementById( 'eMessage' ).innerHTML = '<b>' + this.name + ' is focused </b>';
114}
115
116function onBlur() {
117 document.getElementById( 'eMessage' ).innerHTML = this.name + ' lost focus';
118}
119
120 </script>
121
122</head>
123<body>
124 <h1 class="samples">
125 <a href="index.html">CKEditor Samples</a> &raquo; Using CKEditor JavaScript API
126 </h1>
127 <div class="warning deprecated">
128 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/api.html">brand new version in CKEditor SDK</a>.
129 </div>
130 <div class="description">
131 <p>
132 This sample shows how to use the
133 <a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR.editor">CKEditor JavaScript API</a>
134 to interact with the editor at runtime.
135 </p>
136 <p>
137 For details on how to create this setup check the source code of this sample page.
138 </p>
139 </div>
140
141 <!-- This <div> holds alert messages to be display in the sample page. -->
142 <div id="alerts">
143 <noscript>
144 <p>
145 <strong>CKEditor requires JavaScript to run</strong>. In a browser with no JavaScript
146 support, like yours, you should still see the contents (HTML data) and you should
147 be able to edit it normally, without a rich editor interface.
148 </p>
149 </noscript>
150 </div>
151 <form action="../../../samples/sample_posteddata.php" method="post">
152 <textarea cols="100" id="editor1" name="editor1" rows="10">&lt;p&gt;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using &lt;a href="http://ckeditor.com/"&gt;CKEditor&lt;/a&gt;.&lt;/p&gt;</textarea>
153
154 <script>
155 // Replace the <textarea id="editor1"> with an CKEditor instance.
156 CKEDITOR.replace( 'editor1', {
157 on: {
158 focus: onFocus,
159 blur: onBlur,
160
161 // Check for availability of corresponding plugins.
162 pluginsLoaded: function( evt ) {
163 var doc = CKEDITOR.document, ed = evt.editor;
164 if ( !ed.getCommand( 'bold' ) )
165 doc.getById( 'exec-bold' ).hide();
166 if ( !ed.getCommand( 'link' ) )
167 doc.getById( 'exec-link' ).hide();
168 }
169 }
170 });
171 </script>
172
173 <p id="eMessage">
174 </p>
175
176 <div id="eButtons" style="display: none">
177 <input id="exec-bold" onclick="ExecuteCommand('bold');" type="button" value="Execute &quot;bold&quot; Command">
178 <input id="exec-link" onclick="ExecuteCommand('link');" type="button" value="Execute &quot;link&quot; Command">
179 <input onclick="Focus();" type="button" value="Focus">
180 <br><br>
181 <input onclick="InsertHTML();" type="button" value="Insert HTML">
182 <input onclick="SetContents();" type="button" value="Set Editor Contents">
183 <input onclick="GetContents();" type="button" value="Get Editor Contents (HTML)">
184 <br>
185 <textarea cols="100" id="htmlArea" rows="3">&lt;h2&gt;Test&lt;/h2&gt;&lt;p&gt;This is some &lt;a href="/Test1.html"&gt;sample&lt;/a&gt; HTML code.&lt;/p&gt;</textarea>
186 <br>
187 <br>
188 <input onclick="InsertText();" type="button" value="Insert Text">
189 <br>
190 <textarea cols="100" id="txtArea" rows="3"> First line with some leading whitespaces.
191
192Second line of text preceded by two line breaks.</textarea>
193 <br>
194 <br>
195 <input onclick="CheckDirty();" type="button" value="checkDirty()">
196 <input onclick="ResetDirty();" type="button" value="resetDirty()">
197 </div>
198 </form>
199 <div id="footer">
200 <hr>
201 <p>
202 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
203 </p>
204 <p id="copy">
205 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
206 Knabben. All rights reserved.
207 </p>
208 </div>
209</body>
210</html>
diff --git a/sources/samples/old/appendto.html b/sources/samples/old/appendto.html
new file mode 100644
index 0000000..a984704
--- /dev/null
+++ b/sources/samples/old/appendto.html
@@ -0,0 +1,59 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Append To Page Element Using JavaScript Code &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link rel="stylesheet" href="sample.css">
12</head>
13<body>
14 <h1 class="samples">
15 <a href="index.html">CKEditor Samples</a> &raquo; Append To Page Element Using JavaScript Code
16 </h1>
17 <div class="warning deprecated">
18 This sample is not maintained anymore. Check out the <a href="http://sdk.ckeditor.com/">brand new samples in CKEditor SDK</a>.
19 </div>
20 <div id="section1">
21 <div class="description">
22 <p>
23 The <code><a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR-method-appendTo">CKEDITOR.appendTo()</a></code> method serves to to place editors inside existing DOM elements. Unlike <code><a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR-method-replace">CKEDITOR.replace()</a></code>,
24 a target container to be replaced is no longer necessary. A new editor
25 instance is inserted directly wherever it is desired.
26 </p>
27<pre class="samples">CKEDITOR.appendTo( '<em>container_id</em>',
28 { /* Configuration options to be used. */ }
29 'Editor content to be used.'
30);</pre>
31 </div>
32 <script>
33
34 // This call can be placed at any point after the
35 // DOM element to append CKEditor to or inside the <head><script>
36 // in a window.onload event handler.
37
38 // Append a CKEditor instance using the default configuration and the
39 // provided content to the <div> element of ID "section1".
40 CKEDITOR.appendTo( 'section1',
41 null,
42 '<p>This is some <strong>sample text</strong>. You are using <a href="http://ckeditor.com/">CKEditor</a>.</p>'
43 );
44
45 </script>
46 </div>
47 <br>
48 <div id="footer">
49 <hr>
50 <p>
51 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
52 </p>
53 <p id="copy">
54 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
55 Knabben. All rights reserved.
56 </p>
57 </div>
58</body>
59</html>
diff --git a/sources/samples/old/assets/inlineall/logo.png b/sources/samples/old/assets/inlineall/logo.png
new file mode 100644
index 0000000..b4d5979
--- /dev/null
+++ b/sources/samples/old/assets/inlineall/logo.png
Binary files differ
diff --git a/sources/samples/old/assets/outputxhtml/outputxhtml.css b/sources/samples/old/assets/outputxhtml/outputxhtml.css
new file mode 100644
index 0000000..b81e1d7
--- /dev/null
+++ b/sources/samples/old/assets/outputxhtml/outputxhtml.css
@@ -0,0 +1,204 @@
1/*
2 * Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 *
5 * Styles used by the XHTML 1.1 sample page (xhtml.html).
6 */
7
8/**
9 * Basic definitions for the editing area.
10 */
11body
12{
13 font-family: Arial, Verdana, sans-serif;
14 font-size: 80%;
15 color: #000000;
16 background-color: #ffffff;
17 padding: 5px;
18 margin: 0px;
19}
20
21/**
22 * Core styles.
23 */
24
25.Bold
26{
27 font-weight: bold;
28}
29
30.Italic
31{
32 font-style: italic;
33}
34
35.Underline
36{
37 text-decoration: underline;
38}
39
40.StrikeThrough
41{
42 text-decoration: line-through;
43}
44
45.Subscript
46{
47 vertical-align: sub;
48 font-size: smaller;
49}
50
51.Superscript
52{
53 vertical-align: super;
54 font-size: smaller;
55}
56
57/**
58 * Font faces.
59 */
60
61.FontComic
62{
63 font-family: 'Comic Sans MS';
64}
65
66.FontCourier
67{
68 font-family: 'Courier New';
69}
70
71.FontTimes
72{
73 font-family: 'Times New Roman';
74}
75
76/**
77 * Font sizes.
78 */
79
80.FontSmaller
81{
82 font-size: smaller;
83}
84
85.FontLarger
86{
87 font-size: larger;
88}
89
90.FontSmall
91{
92 font-size: 8pt;
93}
94
95.FontBig
96{
97 font-size: 14pt;
98}
99
100.FontDouble
101{
102 font-size: 200%;
103}
104
105/**
106 * Font colors.
107 */
108.FontColor1
109{
110 color: #ff9900;
111}
112
113.FontColor2
114{
115 color: #0066cc;
116}
117
118.FontColor3
119{
120 color: #ff0000;
121}
122
123.FontColor1BG
124{
125 background-color: #ff9900;
126}
127
128.FontColor2BG
129{
130 background-color: #0066cc;
131}
132
133.FontColor3BG
134{
135 background-color: #ff0000;
136}
137
138/**
139 * Indentation.
140 */
141
142.Indent1
143{
144 margin-left: 40px;
145}
146
147.Indent2
148{
149 margin-left: 80px;
150}
151
152.Indent3
153{
154 margin-left: 120px;
155}
156
157/**
158 * Alignment.
159 */
160
161.JustifyLeft
162{
163 text-align: left;
164}
165
166.JustifyRight
167{
168 text-align: right;
169}
170
171.JustifyCenter
172{
173 text-align: center;
174}
175
176.JustifyFull
177{
178 text-align: justify;
179}
180
181/**
182 * Other.
183 */
184
185code
186{
187 font-family: courier, monospace;
188 background-color: #eeeeee;
189 padding-left: 1px;
190 padding-right: 1px;
191 border: #c0c0c0 1px solid;
192}
193
194kbd
195{
196 padding: 0px 1px 0px 1px;
197 border-width: 1px 2px 2px 1px;
198 border-style: solid;
199}
200
201blockquote
202{
203 color: #808080;
204}
diff --git a/sources/samples/old/assets/posteddata.php b/sources/samples/old/assets/posteddata.php
new file mode 100644
index 0000000..ca43cc9
--- /dev/null
+++ b/sources/samples/old/assets/posteddata.php
@@ -0,0 +1,59 @@
1<!DOCTYPE html>
2<?php
3/*
4Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
5For licensing, see LICENSE.md or http://ckeditor.com/license
6*/
7?>
8<html>
9<head>
10 <meta charset="utf-8">
11 <title>Sample &mdash; CKEditor</title>
12 <link rel="stylesheet" href="sample.css">
13</head>
14<body>
15 <h1 class="samples">
16 CKEditor &mdash; Posted Data
17 </h1>
18 <table border="1" cellspacing="0" id="outputSample">
19 <colgroup><col width="120"></colgroup>
20 <thead>
21 <tr>
22 <th>Field&nbsp;Name</th>
23 <th>Value</th>
24 </tr>
25 </thead>
26<?php
27
28if (!empty($_POST))
29{
30 foreach ( $_POST as $key => $value )
31 {
32 if ( ( !is_string($value) && !is_numeric($value) ) || !is_string($key) )
33 continue;
34
35 if ( get_magic_quotes_gpc() )
36 $value = htmlspecialchars( stripslashes((string)$value) );
37 else
38 $value = htmlspecialchars( (string)$value );
39?>
40 <tr>
41 <th style="vertical-align: top"><?php echo htmlspecialchars( (string)$key ); ?></th>
42 <td><pre class="samples"><?php echo $value; ?></pre></td>
43 </tr>
44 <?php
45 }
46}
47?>
48 </table>
49 <div id="footer">
50 <hr>
51 <p>
52 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
53 </p>
54 <p id="copy">
55 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico Knabben. All rights reserved.
56 </p>
57 </div>
58</body>
59</html>
diff --git a/sources/samples/old/assets/sample.jpg b/sources/samples/old/assets/sample.jpg
new file mode 100644
index 0000000..9498271
--- /dev/null
+++ b/sources/samples/old/assets/sample.jpg
Binary files differ
diff --git a/sources/samples/old/assets/uilanguages/languages.js b/sources/samples/old/assets/uilanguages/languages.js
new file mode 100644
index 0000000..df6b2dc
--- /dev/null
+++ b/sources/samples/old/assets/uilanguages/languages.js
@@ -0,0 +1,92 @@
1/**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6/* exported CKEDITOR_LANGS */
7
8var CKEDITOR_LANGS = ( function() {
9 var langs = {
10 af: 'Afrikaans',
11 ar: 'Arabic',
12 az: 'Azerbaijani',
13 bg: 'Bulgarian',
14 bn: 'Bengali/Bangla',
15 bs: 'Bosnian',
16 ca: 'Catalan',
17 cs: 'Czech',
18 cy: 'Welsh',
19 da: 'Danish',
20 de: 'German',
21 'de-ch': 'German (Switzerland)',
22 el: 'Greek',
23 en: 'English',
24 'en-au': 'English (Australia)',
25 'en-ca': 'English (Canadian)',
26 'en-gb': 'English (United Kingdom)',
27 eo: 'Esperanto',
28 es: 'Spanish',
29 et: 'Estonian',
30 eu: 'Basque',
31 fa: 'Persian',
32 fi: 'Finnish',
33 fo: 'Faroese',
34 fr: 'French',
35 'fr-ca': 'French (Canada)',
36 gl: 'Galician',
37 gu: 'Gujarati',
38 he: 'Hebrew',
39 hi: 'Hindi',
40 hr: 'Croatian',
41 hu: 'Hungarian',
42 id: 'Indonesian',
43 is: 'Icelandic',
44 it: 'Italian',
45 ja: 'Japanese',
46 ka: 'Georgian',
47 km: 'Khmer',
48 ko: 'Korean',
49 ku: 'Kurdish',
50 lt: 'Lithuanian',
51 lv: 'Latvian',
52 mk: 'Macedonian',
53 mn: 'Mongolian',
54 ms: 'Malay',
55 nb: 'Norwegian Bokmal',
56 nl: 'Dutch',
57 no: 'Norwegian',
58 oc: 'Occitan',
59 pl: 'Polish',
60 pt: 'Portuguese (Portugal)',
61 'pt-br': 'Portuguese (Brazil)',
62 ro: 'Romanian',
63 ru: 'Russian',
64 si: 'Sinhala',
65 sk: 'Slovak',
66 sq: 'Albanian',
67 sl: 'Slovenian',
68 sr: 'Serbian (Cyrillic)',
69 'sr-latn': 'Serbian (Latin)',
70 sv: 'Swedish',
71 th: 'Thai',
72 tr: 'Turkish',
73 tt: 'Tatar',
74 ug: 'Uighur',
75 uk: 'Ukrainian',
76 vi: 'Vietnamese',
77 zh: 'Chinese Traditional',
78 'zh-cn': 'Chinese Simplified'
79 };
80
81 var langsArray = [];
82
83 for ( var code in CKEDITOR.lang.languages ) {
84 langsArray.push( { code: code, name: ( langs[ code ] || code ) } );
85 }
86
87 langsArray.sort( function( a, b ) {
88 return ( a.name < b.name ) ? -1 : 1;
89 } );
90
91 return langsArray;
92} )();
diff --git a/sources/samples/old/datafiltering.html b/sources/samples/old/datafiltering.html
new file mode 100644
index 0000000..dd78ba5
--- /dev/null
+++ b/sources/samples/old/datafiltering.html
@@ -0,0 +1,508 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Data Filtering &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link rel="stylesheet" href="sample.css">
12 <script>
13 // Remove advanced tabs for all editors.
14 CKEDITOR.config.removeDialogTabs = 'image:advanced;link:advanced;flash:advanced;creatediv:advanced;editdiv:advanced';
15 </script>
16</head>
17<body>
18 <h1 class="samples">
19 <a href="index.html">CKEditor Samples</a> &raquo; Data Filtering and Features Activation
20 </h1>
21 <div class="warning deprecated">
22 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/acf.html">brand new version in CKEditor SDK</a>.
23 </div>
24 <div class="description">
25 <p>
26 This sample page demonstrates the idea of Advanced Content Filter
27 (<abbr title="Advanced Content Filter">ACF</abbr>), a sophisticated
28 tool that takes control over what kind of data is accepted by the editor and what
29 kind of output is produced.
30 </p>
31 <h2>When and what is being filtered?</h2>
32 <p>
33 <abbr title="Advanced Content Filter">ACF</abbr> controls
34 <strong>every single source of data</strong> that comes to the editor.
35 It process both HTML that is inserted manually (i.e. pasted by the user)
36 and programmatically like:
37 </p>
38<pre class="samples">
39editor.setData( '&lt;p&gt;Hello world!&lt;/p&gt;' );
40</pre>
41 <p>
42 <abbr title="Advanced Content Filter">ACF</abbr> discards invalid,
43 useless HTML tags and attributes so the editor remains "clean" during
44 runtime. <abbr title="Advanced Content Filter">ACF</abbr> behaviour
45 can be configured and adjusted for a particular case to prevent the
46 output HTML (i.e. in CMS systems) from being polluted.
47
48 This kind of filtering is a first, client-side line of defense
49 against "<a href="http://en.wikipedia.org/wiki/Tag_soup">tag soups</a>",
50 the tool that precisely restricts which tags, attributes and styles
51 are allowed (desired). When properly configured, <abbr title="Advanced Content Filter">ACF</abbr>
52 is an easy and fast way to produce a high-quality, intentionally filtered HTML.
53 </p>
54
55 <h3>How to configure or disable ACF?</h3>
56 <p>
57 Advanced Content Filter is enabled by default, working in "automatic mode", yet
58 it provides a set of easy rules that allow adjusting filtering rules
59 and disabling the entire feature when necessary. The config property
60 responsible for this feature is <code><a class="samples"
61 href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent">config.allowedContent</a></code>.
62 </p>
63 <p>
64 By "automatic mode" is meant that loaded plugins decide which kind
65 of content is enabled and which is not. For example, if the link
66 plugin is loaded it implies that <code>&lt;a&gt;</code> tag is
67 automatically allowed. Each plugin is given a set
68 of predefined <abbr title="Advanced Content Filter">ACF</abbr> rules
69 that control the editor until <code><a class="samples"
70 href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent">
71 config.allowedContent</a></code>
72 is defined manually.
73 </p>
74 <p>
75 Let's assume our intention is to restrict the editor to accept (produce) <strong>paragraphs
76 only: no attributes, no styles, no other tags</strong>.
77 With <abbr title="Advanced Content Filter">ACF</abbr>
78 this is very simple. Basically set <code><a class="samples"
79 href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent">
80 config.allowedContent</a></code> to <code>'p'</code>:
81 </p>
82<pre class="samples">
83var editor = CKEDITOR.replace( <em>textarea_id</em>, {
84 <strong>allowedContent: 'p'</strong>
85} );
86</pre>
87 <p>
88 Now try to play with allowed content:
89 </p>
90<pre class="samples">
91// Trying to insert disallowed tag and attribute.
92editor.setData( '&lt;p <strong>style="color: red"</strong>&gt;Hello <strong>&lt;em&gt;world&lt;/em&gt;</strong>!&lt;/p&gt;' );
93alert( editor.getData() );
94
95// Filtered data is returned.
96"&lt;p&gt;Hello world!&lt;/p&gt;"
97</pre>
98 <p>
99 What happened? Since <code>config.allowedContent: 'p'</code> is set the editor assumes
100 that only plain <code>&lt;p&gt;</code> are accepted. Nothing more. This is why
101 <code>style</code> attribute and <code>&lt;em&gt;</code> tag are gone. The same
102 filtering would happen if we pasted disallowed HTML into this editor.
103 </p>
104 <p>
105 This is just a small sample of what <abbr title="Advanced Content Filter">ACF</abbr>
106 can do. To know more, please refer to the sample section below and
107 <a href="http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter">the official Advanced Content Filter guide</a>.
108 </p>
109 <p>
110 You may, of course, want CKEditor to avoid filtering of any kind.
111 To get rid of <abbr title="Advanced Content Filter">ACF</abbr>,
112 basically set <code><a class="samples"
113 href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent">
114 config.allowedContent</a></code> to <code>true</code> like this:
115 </p>
116<pre class="samples">
117CKEDITOR.replace( <em>textarea_id</em>, {
118 <strong>allowedContent: true</strong>
119} );
120</pre>
121
122 <h2>Beyond data flow: Features activation</h2>
123 <p>
124 <abbr title="Advanced Content Filter">ACF</abbr> is far more than
125 <abbr title="Input/Output">I/O</abbr> control: the entire
126 <abbr title="User Interface">UI</abbr> of the editor is adjusted to what
127 filters restrict. For example: if <code>&lt;a&gt;</code> tag is
128 <strong>disallowed</strong>
129 by <abbr title="Advanced Content Filter">ACF</abbr>,
130 then accordingly <code>link</code> command, toolbar button and link dialog
131 are also disabled. Editor is smart: it knows which features must be
132 removed from the interface to match filtering rules.
133 </p>
134 <p>
135 CKEditor can be far more specific. If <code>&lt;a&gt;</code> tag is
136 <strong>allowed</strong> by filtering rules to be used but it is restricted
137 to have only one attribute (<code>href</code>)
138 <code>config.allowedContent = 'a[!href]'</code>, then
139 "Target" tab of the link dialog is automatically disabled as <code>target</code>
140 attribute isn't included in <abbr title="Advanced Content Filter">ACF</abbr> rules
141 for <code>&lt;a&gt;</code>. This behaviour applies to dialog fields, context
142 menus and toolbar buttons.
143 </p>
144
145 <h2>Sample configurations</h2>
146 <p>
147 There are several editor instances below that present different
148 <abbr title="Advanced Content Filter">ACF</abbr> setups. <strong>All of them,
149 except the inline instance, share the same HTML content</strong> to visualize
150 how different filtering rules affect the same input data.
151 </p>
152 </div>
153
154 <div>
155 <label for="editor1">
156 Editor 1:
157 </label>
158 <div class="description">
159 <p>
160 This editor is using default configuration ("automatic mode"). It means that
161 <code><a class="samples"
162 href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent">
163 config.allowedContent</a></code> is defined by loaded plugins.
164 Each plugin extends filtering rules to make it's own associated content
165 available for the user.
166 </p>
167 </div>
168 <textarea cols="80" id="editor1" name="editor1" rows="10">
169 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
170 </textarea>
171
172 <script>
173
174 CKEDITOR.replace( 'editor1' );
175
176 </script>
177 </div>
178
179 <br>
180
181 <div>
182 <label for="editor2">
183 Editor 2:
184 </label>
185 <div class="description">
186 <p>
187 This editor is using a custom configuration for
188 <abbr title="Advanced Content Filter">ACF</abbr>:
189 </p>
190<pre class="samples">
191CKEDITOR.replace( 'editor2', {
192 allowedContent:
193 'h1 h2 h3 p blockquote strong em;' +
194 'a[!href];' +
195 'img(left,right)[!src,alt,width,height];' +
196 'table tr th td caption;' +
197 'span{!font-family};' +'
198 'span{!color};' +
199 'span(!marker);' +
200 'del ins'
201} );
202</pre>
203 <p>
204 The following rules may require additional explanation:
205 </p>
206 <ul>
207 <li>
208 <code>h1 h2 h3 p blockquote strong em</code> - These tags
209 are accepted by the editor. Any tag attributes will be discarded.
210 </li>
211 <li>
212 <code>a[!href]</code> - <code>href</code> attribute is obligatory
213 for <code>&lt;a&gt;</code> tag. Tags without this attribute
214 are disarded. No other attribute will be accepted.
215 </li>
216 <li>
217 <code>img(left,right)[!src,alt,width,height]</code> - <code>src</code>
218 attribute is obligatory for <code>&lt;img&gt;</code> tag.
219 <code>alt</code>, <code>width</code>, <code>height</code>
220 and <code>class</code> attributes are accepted but
221 <code>class</code> must be either <code>class="left"</code>
222 or <code>class="right"</code>
223 </li>
224 <li>
225 <code>table tr th td caption</code> - These tags
226 are accepted by the editor. Any tag attributes will be discarded.
227 </li>
228 <li>
229 <code>span{!font-family}</code>, <code>span{!color}</code>,
230 <code>span(!marker)</code> - <code>&lt;span&gt;</code> tags
231 will be accepted if either <code>font-family</code> or
232 <code>color</code> style is set or <code>class="marker"</code>
233 is present.
234 </li>
235 <li>
236 <code>del ins</code> - These tags
237 are accepted by the editor. Any tag attributes will be discarded.
238 </li>
239 </ul>
240 <p>
241 Please note that <strong><abbr title="User Interface">UI</abbr> of the
242 editor is different</strong>. It's a response to what happened to the filters.
243 Since <code>text-align</code> isn't allowed, the align toolbar is gone.
244 The same thing happened to subscript/superscript, strike, underline
245 (<code>&lt;u&gt;</code>, <code>&lt;sub&gt;</code>, <code>&lt;sup&gt;</code>
246 are disallowed by <code><a class="samples"
247 href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent">
248 config.allowedContent</a></code>) and many other buttons.
249 </p>
250 </div>
251 <textarea cols="80" id="editor2" name="editor2" rows="10">
252 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
253 </textarea>
254 <script>
255
256 CKEDITOR.replace( 'editor2', {
257 allowedContent:
258 'h1 h2 h3 p blockquote strong em;' +
259 'a[!href];' +
260 'img(left,right)[!src,alt,width,height];' +
261 'table tr th td caption;' +
262 'span{!font-family};' +
263 'span{!color};' +
264 'span(!marker);' +
265 'del ins'
266 } );
267
268 </script>
269 </div>
270
271 <br>
272
273 <div>
274 <label for="editor3">
275 Editor 3:
276 </label>
277 <div class="description">
278 <p>
279 This editor is using a custom configuration for
280 <abbr title="Advanced Content Filter">ACF</abbr>.
281 Note that filters can be configured as an object literal
282 as an alternative to a string-based definition.
283 </p>
284<pre class="samples">
285CKEDITOR.replace( 'editor3', {
286 allowedContent: {
287 'b i ul ol big small': true,
288 'h1 h2 h3 p blockquote li': {
289 styles: 'text-align'
290 },
291 a: { attributes: '!href,target' },
292 img: {
293 attributes: '!src,alt',
294 styles: 'width,height',
295 classes: 'left,right'
296 }
297 }
298} );
299</pre>
300 </div>
301 <textarea cols="80" id="editor3" name="editor3" rows="10">
302 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
303 </textarea>
304 <script>
305
306 CKEDITOR.replace( 'editor3', {
307 allowedContent: {
308 'b i ul ol big small': true,
309 'h1 h2 h3 p blockquote li': {
310 styles: 'text-align'
311 },
312 a: { attributes: '!href,target' },
313 img: {
314 attributes: '!src,alt',
315 styles: 'width,height',
316 classes: 'left,right'
317 }
318 }
319 } );
320
321 </script>
322 </div>
323
324 <br>
325
326 <div>
327 <label for="editor4">
328 Editor 4:
329 </label>
330 <div class="description">
331 <p>
332 This editor is using a custom set of plugins and buttons.
333 </p>
334<pre class="samples">
335CKEDITOR.replace( 'editor4', {
336 removePlugins: 'bidi,font,forms,flash,horizontalrule,iframe,justify,table,tabletools,smiley',
337 removeButtons: 'Anchor,Underline,Strike,Subscript,Superscript,Image',
338 format_tags: 'p;h1;h2;h3;pre;address'
339} );
340</pre>
341 <p>
342 As you can see, removing plugins and buttons implies filtering.
343 Several tags are not allowed in the editor because there's no
344 plugin/button that is responsible for creating and editing this
345 kind of content (for example: the image is missing because
346 of <code>removeButtons: 'Image'</code>). The conclusion is that
347 <abbr title="Advanced Content Filter">ACF</abbr> works "backwards"
348 as well: <strong>modifying <abbr title="User Interface">UI</abbr>
349 elements is changing allowed content rules</strong>.
350 </p>
351 </div>
352 <textarea cols="80" id="editor4" name="editor4" rows="10">
353 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
354 </textarea>
355 <script>
356
357 CKEDITOR.replace( 'editor4', {
358 removePlugins: 'bidi,div,font,forms,flash,horizontalrule,iframe,justify,table,tabletools,smiley',
359 removeButtons: 'Anchor,Underline,Strike,Subscript,Superscript,Image',
360 format_tags: 'p;h1;h2;h3;pre;address'
361 } );
362
363 </script>
364 </div>
365
366 <br>
367
368 <div>
369 <label for="editor5">
370 Editor 5:
371 </label>
372 <div class="description">
373 <p>
374 This editor is built on editable <code>&lt;h1&gt;</code> element.
375 <abbr title="Advanced Content Filter">ACF</abbr> takes care of
376 what can be included in <code>&lt;h1&gt;</code>. Note that there
377 are no block styles in Styles combo. Also why lists, indentation,
378 blockquote, div, form and other buttons are missing.
379 </p>
380 <p>
381 <abbr title="Advanced Content Filter">ACF</abbr> makes sure that
382 no disallowed tags will come to <code>&lt;h1&gt;</code> so the final
383 markup is valid. If the user tried to paste some invalid HTML
384 into this editor (let's say a list), it would be automatically
385 converted into plain text.
386 </p>
387 </div>
388 <h1 id="editor5" contenteditable="true">
389 <em>Apollo 11</em> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC.
390 </h1>
391 </div>
392
393 <br>
394
395 <div>
396 <label for="editor3">
397 Editor 6:
398 </label>
399 <div class="description">
400 <p>
401 This editor is using a custom configuration for <abbr title="Advanced Content Filter">ACF</abbr>.
402 It's using the <a href="http://docs.ckeditor.com/#!/guide/dev_disallowed_content" rel="noopener noreferrer" target="_blank">
403 Disallowed Content</a> property of the filter to eliminate all <code>title</code> attributes.
404 </p>
405
406<pre class="samples">
407CKEDITOR.replace( 'editor6', {
408 allowedContent: {
409 'b i ul ol big small': true,
410 'h1 h2 h3 p blockquote li': {
411 styles: 'text-align'
412 },
413 a: {attributes: '!href,target'},
414 img: {
415 attributes: '!src,alt',
416 styles: 'width,height',
417 classes: 'left,right'
418 }
419 },
420 disallowedContent: '*{title*}'
421} );
422</pre>
423 </div>
424 <textarea cols="80" id="editor6" name="editor6" rows="10">
425 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
426 </textarea>
427 <script>
428
429 CKEDITOR.replace( 'editor6', {
430 allowedContent: {
431 'b i ul ol big small': true,
432 'h1 h2 h3 p blockquote li': {
433 styles: 'text-align'
434 },
435 a: {attributes: '!href,target'},
436 img: {
437 attributes: '!src,alt',
438 styles: 'width,height',
439 classes: 'left,right'
440 }
441 },
442 disallowedContent: '*{title*}'
443 } );
444
445 </script>
446 </div>
447
448 <br>
449
450 <div>
451 <label for="editor7">
452 Editor 7:
453 </label>
454 <div class="description">
455 <p>
456 This editor is using a custom configuration for <abbr title="Advanced Content Filter">ACF</abbr>.
457 It's using the <a href="http://docs.ckeditor.com/#!/guide/dev_disallowed_content" rel="noopener noreferrer" target="_blank">
458 Disallowed Content</a> property of the filter to eliminate all <code>a</code> and <code>img</code> tags,
459 while allowing all other tags.
460 </p>
461<pre class="samples">
462CKEDITOR.replace( 'editor7', {
463 allowedContent: {
464 // Allow all content.
465 $1: {
466 elements: CKEDITOR.dtd,
467 attributes: true,
468 styles: true,
469 classes: true
470 }
471 },
472 disallowedContent: 'img a'
473} );
474</pre>
475 </div>
476 <textarea cols="80" id="editor7" name="editor7" rows="10">
477 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
478 </textarea>
479 <script>
480
481 CKEDITOR.replace( 'editor7', {
482 allowedContent: {
483 // allow all content
484 $1: {
485 elements: CKEDITOR.dtd,
486 attributes: true,
487 styles: true,
488 classes: true
489 }
490 },
491 disallowedContent: 'img a'
492 } );
493
494 </script>
495 </div>
496
497 <div id="footer">
498 <hr>
499 <p>
500 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
501 </p>
502 <p id="copy">
503 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
504 Knabben. All rights reserved.
505 </p>
506 </div>
507</body>
508</html>
diff --git a/sources/samples/old/divreplace.html b/sources/samples/old/divreplace.html
new file mode 100644
index 0000000..e882b22
--- /dev/null
+++ b/sources/samples/old/divreplace.html
@@ -0,0 +1,144 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Replace DIV &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link href="sample.css" rel="stylesheet">
12 <style>
13
14 div.editable
15 {
16 border: solid 2px transparent;
17 padding-left: 15px;
18 padding-right: 15px;
19 }
20
21 div.editable:hover
22 {
23 border-color: black;
24 }
25
26 </style>
27 <script>
28
29 // Uncomment the following code to test the "Timeout Loading Method".
30 // CKEDITOR.loadFullCoreTimeout = 5;
31
32 window.onload = function() {
33 // Listen to the double click event.
34 if ( window.addEventListener )
35 document.body.addEventListener( 'dblclick', onDoubleClick, false );
36 else if ( window.attachEvent )
37 document.body.attachEvent( 'ondblclick', onDoubleClick );
38
39 };
40
41 function onDoubleClick( ev ) {
42 // Get the element which fired the event. This is not necessarily the
43 // element to which the event has been attached.
44 var element = ev.target || ev.srcElement;
45
46 // Find out the div that holds this element.
47 var name;
48
49 do {
50 element = element.parentNode;
51 }
52 while ( element && ( name = element.nodeName.toLowerCase() ) &&
53 ( name != 'div' || element.className.indexOf( 'editable' ) == -1 ) && name != 'body' );
54
55 if ( name == 'div' && element.className.indexOf( 'editable' ) != -1 )
56 replaceDiv( element );
57 }
58
59 var editor;
60
61 function replaceDiv( div ) {
62 if ( editor )
63 editor.destroy();
64
65 editor = CKEDITOR.replace( div );
66 }
67
68 </script>
69</head>
70<body>
71 <h1 class="samples">
72 <a href="index.html">CKEditor Samples</a> &raquo; Replace DIV with CKEditor on the Fly
73 </h1>
74 <div class="warning deprecated">
75 This sample is not maintained anymore. Check out the <a href="http://sdk.ckeditor.com/">brand new samples in CKEditor SDK</a>.
76 </div>
77 <div class="description">
78 <p>
79 This sample shows how to automatically replace <code>&lt;div&gt;</code> elements
80 with a CKEditor instance on the fly, following user's doubleclick. The content
81 that was previously placed inside the <code>&lt;div&gt;</code> element will now
82 be moved into CKEditor editing area.
83 </p>
84 <p>
85 For details on how to create this setup check the source code of this sample page.
86 </p>
87 </div>
88 <p>
89 Double-click any of the following <code>&lt;div&gt;</code> elements to transform them into
90 editor instances.
91 </p>
92 <div class="editable">
93 <h3>
94 Part 1
95 </h3>
96 <p>
97 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
98 semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
99 rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
100 nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
101 eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
102 </p>
103 </div>
104 <div class="editable">
105 <h3>
106 Part 2
107 </h3>
108 <p>
109 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
110 semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
111 rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
112 nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
113 eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
114 </p>
115 <p>
116 Donec velit. Mauris massa. Vestibulum non nulla. Nam suscipit arcu nec elit. Phasellus
117 sollicitudin iaculis ante. Ut non mauris et sapien tincidunt adipiscing. Vestibulum
118 vitae leo. Suspendisse nec mi tristique nulla laoreet vulputate.
119 </p>
120 </div>
121 <div class="editable">
122 <h3>
123 Part 3
124 </h3>
125 <p>
126 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
127 semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
128 rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
129 nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
130 eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
131 </p>
132 </div>
133 <div id="footer">
134 <hr>
135 <p>
136 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
137 </p>
138 <p id="copy">
139 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
140 Knabben. All rights reserved.
141 </p>
142 </div>
143</body>
144</html>
diff --git a/sources/samples/old/index.html b/sources/samples/old/index.html
new file mode 100644
index 0000000..999d110
--- /dev/null
+++ b/sources/samples/old/index.html
@@ -0,0 +1,111 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>CKEditor Samples</title>
10 <link rel="stylesheet" href="sample.css">
11</head>
12<body>
13 <h1 class="samples">
14 CKEditor Samples
15 </h1>
16 <div class="warning deprecated">
17 These samples are not maintained anymore. Check out the <a href="http://sdk.ckeditor.com/">brand new samples in CKEditor SDK</a>.
18 </div>
19 <div class="twoColumns">
20 <div class="twoColumnsLeft">
21 <h2 class="samples">
22 Basic Samples
23 </h2>
24 <dl class="samples">
25 <dt><a class="samples" href="replacebyclass.html">Replace textarea elements by class name</a></dt>
26 <dd>Automatic replacement of all textarea elements of a given class with a CKEditor instance.</dd>
27
28 <dt><a class="samples" href="replacebycode.html">Replace textarea elements by code</a></dt>
29 <dd>Replacement of textarea elements with CKEditor instances by using a JavaScript call.</dd>
30
31 <dt><a class="samples" href="jquery.html">Create editors with jQuery</a></dt>
32 <dd>Creating standard and inline CKEditor instances with jQuery adapter.</dd>
33 </dl>
34
35 <h2 class="samples">
36 Basic Customization
37 </h2>
38 <dl class="samples">
39 <dt><a class="samples" href="uicolor.html">User Interface color</a></dt>
40 <dd>Changing CKEditor User Interface color and adding a toolbar button that lets the user set the UI color.</dd>
41
42 <dt><a class="samples" href="uilanguages.html">User Interface languages</a></dt>
43 <dd>Changing CKEditor User Interface language and adding a drop-down list that lets the user choose the UI language.</dd>
44 </dl>
45
46
47
48 <!-- PLUGINS_SAMPLES -->
49 </div>
50 <div class="twoColumnsRight">
51 <h2 class="samples">
52 Inline Editing
53 </h2>
54 <dl class="samples">
55 <dt><a class="samples" href="inlineall.html">Massive inline editor creation</a></dt>
56 <dd>Turn all elements with <code>contentEditable = true</code> attribute into inline editors.</dd>
57
58 <dt><a class="samples" href="inlinebycode.html">Convert element into an inline editor by code</a></dt>
59 <dd>Conversion of DOM elements into inline CKEditor instances by using a JavaScript call.</dd>
60
61 <dt><a class="samples" href="inlinetextarea.html">Replace textarea with inline editor</a> <span class="new">New!</span></dt>
62 <dd>A form with a textarea that is replaced by an inline editor at runtime.</dd>
63
64 <!-- INLINE_EDITING_SAMPLES -->
65 </dl>
66
67 <h2 class="samples">
68 Advanced Samples
69 </h2>
70 <dl class="samples">
71 <dt><a class="samples" href="datafiltering.html">Data filtering and features activation</a> <span class="new">New!</span></dt>
72 <dd>Data filtering and automatic features activation basing on configuration.</dd>
73
74 <dt><a class="samples" href="divreplace.html">Replace DIV elements on the fly</a></dt>
75 <dd>Transforming a <code>div</code> element into an instance of CKEditor with a mouse click.</dd>
76
77 <dt><a class="samples" href="appendto.html">Append editor instances</a></dt>
78 <dd>Appending editor instances to existing DOM elements.</dd>
79
80 <dt><a class="samples" href="ajax.html">Create and destroy editor instances for Ajax applications</a></dt>
81 <dd>Creating and destroying CKEditor instances on the fly and saving the contents entered into the editor window.</dd>
82
83 <dt><a class="samples" href="api.html">Basic usage of the API</a></dt>
84 <dd>Using the CKEditor JavaScript API to interact with the editor at runtime.</dd>
85
86 <dt><a class="samples" href="xhtmlstyle.html">XHTML-compliant style</a></dt>
87 <dd>Configuring CKEditor to produce XHTML 1.1 compliant attributes and styles.</dd>
88
89 <dt><a class="samples" href="readonly.html">Read-only mode</a></dt>
90 <dd>Using the readOnly API to block introducing changes to the editor contents.</dd>
91
92 <dt><a class="samples" href="tabindex.html">"Tab" key-based navigation</a></dt>
93 <dd>Navigating among editor instances with tab key.</dd>
94
95
96
97 <!-- ADVANCED_SAMPLES -->
98 </dl>
99 </div>
100 </div>
101 <div id="footer">
102 <hr>
103 <p>
104 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
105 </p>
106 <p id="copy">
107 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico Knabben. All rights reserved.
108 </p>
109 </div>
110</body>
111</html>
diff --git a/sources/samples/old/inlineall.html b/sources/samples/old/inlineall.html
new file mode 100644
index 0000000..8220ea5
--- /dev/null
+++ b/sources/samples/old/inlineall.html
@@ -0,0 +1,314 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Massive inline editing &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <script>
12
13 // This code is generally not necessary, but it is here to demonstrate
14 // how to customize specific editor instances on the fly. This fits well
15 // this demo because we have editable elements (like headers) that
16 // require less features.
17
18 // The "instanceCreated" event is fired for every editor instance created.
19 CKEDITOR.on( 'instanceCreated', function( event ) {
20 var editor = event.editor,
21 element = editor.element;
22
23 // Customize editors for headers and tag list.
24 // These editors don't need features like smileys, templates, iframes etc.
25 if ( element.is( 'h1', 'h2', 'h3' ) || element.getAttribute( 'id' ) == 'taglist' ) {
26 // Customize the editor configurations on "configLoaded" event,
27 // which is fired after the configuration file loading and
28 // execution. This makes it possible to change the
29 // configurations before the editor initialization takes place.
30 editor.on( 'configLoaded', function() {
31
32 // Remove unnecessary plugins to make the editor simpler.
33 editor.config.removePlugins = 'colorbutton,find,flash,font,' +
34 'forms,iframe,image,newpage,removeformat,' +
35 'smiley,specialchar,stylescombo,templates';
36
37 // Rearrange the layout of the toolbar.
38 editor.config.toolbarGroups = [
39 { name: 'editing', groups: [ 'basicstyles', 'links' ] },
40 { name: 'undo' },
41 { name: 'clipboard', groups: [ 'selection', 'clipboard' ] },
42 { name: 'about' }
43 ];
44 });
45 }
46 });
47
48 </script>
49 <link href="sample.css" rel="stylesheet">
50 <style>
51
52 /* The following styles are just to make the page look nice. */
53
54 /* Workaround to show Arial Black in Firefox. */
55 @font-face
56 {
57 font-family: 'arial-black';
58 src: local('Arial Black');
59 }
60
61 *[contenteditable="true"]
62 {
63 padding: 10px;
64 }
65
66 #container
67 {
68 width: 960px;
69 margin: 30px auto 0;
70 }
71
72 #header
73 {
74 overflow: hidden;
75 padding: 0 0 30px;
76 border-bottom: 5px solid #05B2D2;
77 position: relative;
78 }
79
80 #headerLeft,
81 #headerRight
82 {
83 width: 49%;
84 overflow: hidden;
85 }
86
87 #headerLeft
88 {
89 float: left;
90 padding: 10px 1px 1px;
91 }
92
93 #headerLeft h2,
94 #headerLeft h3
95 {
96 text-align: right;
97 margin: 0;
98 overflow: hidden;
99 font-weight: normal;
100 }
101
102 #headerLeft h2
103 {
104 font-family: "Arial Black",arial-black;
105 font-size: 4.6em;
106 line-height: 1.1;
107 text-transform: uppercase;
108 }
109
110 #headerLeft h3
111 {
112 font-size: 2.3em;
113 line-height: 1.1;
114 margin: .2em 0 0;
115 color: #666;
116 }
117
118 #headerRight
119 {
120 float: right;
121 padding: 1px;
122 }
123
124 #headerRight p
125 {
126 line-height: 1.8;
127 text-align: justify;
128 margin: 0;
129 }
130
131 #headerRight p + p
132 {
133 margin-top: 20px;
134 }
135
136 #headerRight > div
137 {
138 padding: 20px;
139 margin: 0 0 0 30px;
140 font-size: 1.4em;
141 color: #666;
142 }
143
144 #columns
145 {
146 color: #333;
147 overflow: hidden;
148 padding: 20px 0;
149 }
150
151 #columns > div
152 {
153 float: left;
154 width: 33.3%;
155 }
156
157 #columns #column1 > div
158 {
159 margin-left: 1px;
160 }
161
162 #columns #column3 > div
163 {
164 margin-right: 1px;
165 }
166
167 #columns > div > div
168 {
169 margin: 0px 10px;
170 padding: 10px 20px;
171 }
172
173 #columns blockquote
174 {
175 margin-left: 15px;
176 }
177
178 #tagLine
179 {
180 border-top: 5px solid #05B2D2;
181 padding-top: 20px;
182 }
183
184 #taglist {
185 display: inline-block;
186 margin-left: 20px;
187 font-weight: bold;
188 margin: 0 0 0 20px;
189 }
190
191 </style>
192</head>
193<body>
194<div>
195 <h1 class="samples"><a href="index.html">CKEditor Samples</a> &raquo; Massive inline editing</h1>
196 <div class="warning deprecated">
197 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/inline.html">brand new version in CKEditor SDK</a>.
198 </div>
199 <div class="description">
200 <p>This sample page demonstrates the inline editing feature - CKEditor instances will be created automatically from page elements with <strong>contentEditable</strong> attribute set to value <strong>true</strong>:</p>
201 <pre class="samples">&lt;div <strong>contenteditable="true</strong>" &gt; ... &lt;/div&gt;</pre>
202 <p>Click inside of any element below to start editing.</p>
203 </div>
204</div>
205<div id="container">
206 <div id="header">
207 <div id="headerLeft">
208 <h2 id="sampleTitle" contenteditable="true">
209 CKEditor<br> Goes Inline!
210 </h2>
211 <h3 contenteditable="true">
212 Lorem ipsum dolor sit amet dolor duis blandit vestibulum faucibus a, tortor.
213 </h3>
214 </div>
215 <div id="headerRight">
216 <div contenteditable="true">
217 <p>
218 Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies.
219 </p>
220 <p>
221 Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor. Pellentesque facilisis. Nulla imperdiet sit amet magna. Vestibulum dapibus, mauris nec malesuada fames ac.
222 </p>
223 </div>
224 </div>
225 </div>
226 <div id="columns">
227 <div id="column1">
228 <div contenteditable="true">
229 <h3>
230 Fusce vitae porttitor
231 </h3>
232 <p>
233 <strong>
234 Lorem ipsum dolor sit amet dolor. Duis blandit vestibulum faucibus a, tortor.
235 </strong>
236 </p>
237 <p>
238 Proin nunc justo felis mollis tincidunt, risus risus pede, posuere cubilia Curae, Nullam euismod, enim. Etiam nibh ultricies dolor ac dignissim erat volutpat. Vivamus fermentum <a href="http://ckeditor.com/">nisl nulla sem in</a> metus. Maecenas wisi. Donec nec erat volutpat.
239 </p>
240 <blockquote>
241 <p>
242 Fusce vitae porttitor a, euismod convallis nisl, blandit risus tortor, pretium.
243 Vehicula vitae, imperdiet vel, ornare enim vel sodales rutrum
244 </p>
245 </blockquote>
246 <blockquote>
247 <p>
248 Libero nunc, rhoncus ante ipsum non ipsum. Nunc eleifend pede turpis id sollicitudin fringilla. Phasellus ultrices, velit ac arcu.
249 </p>
250 </blockquote>
251 <p>Pellentesque nunc. Donec suscipit erat. Pellentesque habitant morbi tristique ullamcorper.</p>
252 <p><s>Mauris mattis feugiat lectus nec mauris. Nullam vitae ante.</s></p>
253 </div>
254 </div>
255 <div id="column2">
256 <div contenteditable="true">
257 <h3>
258 Integer condimentum sit amet
259 </h3>
260 <p>
261 <strong>Aenean nonummy a, mattis varius. Cras aliquet.</strong>
262 Praesent <a href="http://ckeditor.com/">magna non mattis ac, rhoncus nunc</a>, rhoncus eget, cursus pulvinar mollis.</p>
263 <p>Proin id nibh. Sed eu libero posuere sed, lectus. Phasellus dui gravida gravida feugiat mattis ac, felis.</p>
264 <p>Integer condimentum sit amet, tempor elit odio, a dolor non ante at sapien. Sed ac lectus. Nulla ligula quis eleifend mi, id leo velit pede cursus arcu id nulla ac lectus. Phasellus vestibulum. Nunc viverra enim quis diam.</p>
265 </div>
266 <div contenteditable="true">
267 <h3>
268 Praesent wisi accumsan sit amet nibh
269 </h3>
270 <p>Donec ullamcorper, risus tortor, pretium porttitor. Morbi quam quis lectus non leo.</p>
271 <p style="margin-left: 40px; ">Integer faucibus scelerisque. Proin faucibus at, aliquet vulputate, odio at eros. Fusce <a href="http://ckeditor.com/">gravida, erat vitae augue</a>. Fusce urna fringilla gravida.</p>
272 <p>In hac habitasse platea dictumst. Praesent wisi accumsan sit amet nibh. Maecenas orci luctus a, lacinia quam sem, posuere commodo, odio condimentum tempor, pede semper risus. Suspendisse pede. In hac habitasse platea dictumst. Nam sed laoreet sit amet erat. Integer.</p>
273 </div>
274 </div>
275 <div id="column3">
276 <div contenteditable="true">
277 <p>
278 <img src="assets/inlineall/logo.png" alt="CKEditor logo" style="float:left">
279 </p>
280 <p>Quisque justo neque, mattis sed, fermentum ultrices <strong>posuere cubilia Curae</strong>, Vestibulum elit metus, quis placerat ut, lectus. Ut sagittis, nunc libero, egestas consequat lobortis velit rutrum ut, faucibus turpis. Fusce porttitor, nulla quis turpis. Nullam laoreet vel, consectetuer tellus suscipit ultricies, hendrerit wisi. Donec odio nec velit ac nunc sit amet, accumsan cursus aliquet. Vestibulum ante sit amet sagittis mi.</p>
281 <h3>
282 Nullam laoreet vel consectetuer tellus suscipit
283 </h3>
284 <ul>
285 <li>Ut sagittis, nunc libero, egestas consequat lobortis velit rutrum ut, faucibus turpis.</li>
286 <li>Fusce porttitor, nulla quis turpis. Nullam laoreet vel, consectetuer tellus suscipit ultricies, hendrerit wisi.</li>
287 <li>Mauris eget tellus. Donec non felis. Nam eget dolor. Vestibulum enim. Donec.</li>
288 </ul>
289 <p>Quisque justo neque, mattis sed, <a href="http://ckeditor.com/">fermentum ultrices posuere cubilia</a> Curae, Vestibulum elit metus, quis placerat ut, lectus.</p>
290 <p>Nullam laoreet vel, consectetuer tellus suscipit ultricies, hendrerit wisi. Ut sagittis, nunc libero, egestas consequat lobortis velit rutrum ut, faucibus turpis. Fusce porttitor, nulla quis turpis.</p>
291 <p>Donec odio nec velit ac nunc sit amet, accumsan cursus aliquet. Vestibulum ante sit amet sagittis mi. Sed in nonummy faucibus turpis. Mauris eget tellus. Donec non felis. Nam eget dolor. Vestibulum enim. Donec.</p>
292 </div>
293 </div>
294 </div>
295 <div id="tagLine">
296 Tags of this article:
297 <p id="taglist" contenteditable="true">
298 inline, editing, floating, CKEditor
299 </p>
300 </div>
301</div>
302<div id="footer">
303 <hr>
304 <p>
305 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">
306 http://ckeditor.com</a>
307 </p>
308 <p id="copy">
309 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a>
310 - Frederico Knabben. All rights reserved.
311 </p>
312</div>
313</body>
314</html>
diff --git a/sources/samples/old/inlinebycode.html b/sources/samples/old/inlinebycode.html
new file mode 100644
index 0000000..eea2723
--- /dev/null
+++ b/sources/samples/old/inlinebycode.html
@@ -0,0 +1,124 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Inline Editing by Code &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link href="sample.css" rel="stylesheet">
12 <style>
13
14 #editable
15 {
16 padding: 10px;
17 float: left;
18 }
19
20 </style>
21</head>
22<body>
23 <h1 class="samples">
24 <a href="index.html">CKEditor Samples</a> &raquo; Inline Editing by Code
25 </h1>
26 <div class="warning deprecated">
27 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/inline.html">brand new version in CKEditor SDK</a>.
28 </div>
29 <div class="description">
30 <p>
31 This sample shows how to create an inline editor instance of CKEditor. It is created
32 with a JavaScript call using the following code:
33 </p>
34<pre class="samples">
35// This property tells CKEditor to not activate every element with contenteditable=true element.
36CKEDITOR.disableAutoInline = true;
37
38var editor = CKEDITOR.inline( document.getElementById( 'editable' ) );
39</pre>
40 <p>
41 Note that <code>editable</code> in the code above is the <code>id</code>
42 attribute of the <code>&lt;div&gt;</code> element to be converted into an inline instance.
43 </p>
44 </div>
45 <div id="editable" contenteditable="true">
46 <h1><img alt="Saturn V carrying Apollo 11" class="right" src="assets/sample.jpg" /> Apollo 11</h1>
47
48 <p><b>Apollo 11</b> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.</p>
49
50 <p>Armstrong spent about <s>three and a half</s> two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&nbsp;kg) of lunar material for return to Earth. A third member of the mission, <a href="http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)" title="Michael Collins (astronaut)">Michael Collins</a>, piloted the <a href="http://en.wikipedia.org/wiki/Apollo_Command/Service_Module" title="Apollo Command/Service Module">command</a> spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.</p>
51
52 <h2>Broadcasting and <em>quotes</em> <a id="quotes" name="quotes"></a></h2>
53
54 <p>Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:</p>
55
56 <blockquote>
57 <p>One small step for [a] man, one giant leap for mankind.</p>
58 </blockquote>
59
60 <p>Apollo 11 effectively ended the <a href="http://en.wikipedia.org/wiki/Space_Race" title="Space Race">Space Race</a> and fulfilled a national goal proposed in 1961 by the late U.S. President <a href="http://en.wikipedia.org/wiki/John_F._Kennedy" title="John F. Kennedy">John F. Kennedy</a> in a speech before the United States Congress:</p>
61
62 <blockquote>
63 <p>[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.</p>
64 </blockquote>
65
66 <h2>Technical details <a id="tech-details" name="tech-details"></a></h2>
67
68 <table align="right" border="1" bordercolor="#ccc" cellpadding="5" cellspacing="0" style="border-collapse:collapse;margin:10px 0 10px 15px;">
69 <caption><strong>Mission crew</strong></caption>
70 <thead>
71 <tr>
72 <th scope="col">Position</th>
73 <th scope="col">Astronaut</th>
74 </tr>
75 </thead>
76 <tbody>
77 <tr>
78 <td>Commander</td>
79 <td>Neil A. Armstrong</td>
80 </tr>
81 <tr>
82 <td>Command Module Pilot</td>
83 <td>Michael Collins</td>
84 </tr>
85 <tr>
86 <td>Lunar Module Pilot</td>
87 <td>Edwin &quot;Buzz&quot; E. Aldrin, Jr.</td>
88 </tr>
89 </tbody>
90 </table>
91
92 <p>Launched by a <strong>Saturn V</strong> rocket from <a href="http://en.wikipedia.org/wiki/Kennedy_Space_Center" title="Kennedy Space Center">Kennedy Space Center</a> in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of <a href="http://en.wikipedia.org/wiki/NASA" title="NASA">NASA</a>&#39;s Apollo program. The Apollo spacecraft had three parts:</p>
93
94 <ol>
95 <li><strong>Command Module</strong> with a cabin for the three astronauts which was the only part which landed back on Earth</li>
96 <li><strong>Service Module</strong> which supported the Command Module with propulsion, electrical power, oxygen and water</li>
97 <li><strong>Lunar Module</strong> for landing on the Moon.</li>
98 </ol>
99
100 <p>After being sent to the Moon by the Saturn V&#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the <a href="http://en.wikipedia.org/wiki/Mare_Tranquillitatis" title="Mare Tranquillitatis">Sea of Tranquility</a>. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the <a href="http://en.wikipedia.org/wiki/Pacific_Ocean" title="Pacific Ocean">Pacific Ocean</a> on July 24.</p>
101
102 <hr />
103 <p style="text-align: right;"><small>Source: <a href="http://en.wikipedia.org/wiki/Apollo_11">Wikipedia.org</a></small></p>
104 </div>
105
106 <script>
107 // We need to turn off the automatic editor creation first.
108 CKEDITOR.disableAutoInline = true;
109
110 var editor = CKEDITOR.inline( 'editable' );
111 </script>
112 <div id="footer">
113 <hr>
114 <p contenteditable="true">
115 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">
116 http://ckeditor.com</a>
117 </p>
118 <p id="copy">
119 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a>
120 - Frederico Knabben. All rights reserved.
121 </p>
122 </div>
123</body>
124</html>
diff --git a/sources/samples/old/inlinetextarea.html b/sources/samples/old/inlinetextarea.html
new file mode 100644
index 0000000..57e664e
--- /dev/null
+++ b/sources/samples/old/inlinetextarea.html
@@ -0,0 +1,113 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Replace Textarea with Inline Editor &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link href="sample.css" rel="stylesheet">
12 <style>
13
14 /* Style the CKEditor element to look like a textfield */
15 .cke_textarea_inline
16 {
17 padding: 10px;
18 height: 200px;
19 overflow: auto;
20
21 border: 1px solid gray;
22 -webkit-appearance: textfield;
23 }
24
25 </style>
26</head>
27<body>
28 <h1 class="samples">
29 <a href="index.html">CKEditor Samples</a> &raquo; Replace Textarea with Inline Editor
30 </h1>
31 <div class="warning deprecated">
32 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/inline.html">brand new version in CKEditor SDK</a>.
33 </div>
34 <div class="description">
35 <p>
36 You can also create an inline editor from a <code>textarea</code>
37 element. In this case the <code>textarea</code> will be replaced
38 by a <code>div</code> element with inline editing enabled.
39 </p>
40<pre class="samples">
41// "article-body" is the name of a textarea element.
42var editor = CKEDITOR.inline( 'article-body' );
43</pre>
44 </div>
45 <form action="sample_posteddata.php" method="post">
46 <h2>This is a sample form with some fields</h2>
47 <p>
48 Title:<br>
49 <input type="text" name="title" value="Sample Form"></p>
50 <p>
51 Article Body (Textarea converted to CKEditor):<br>
52 <textarea name="article-body" style="height: 200px">
53 &lt;h2&gt;Technical details &lt;a id="tech-details" name="tech-details"&gt;&lt;/a&gt;&lt;/h2&gt;
54
55 &lt;table align="right" border="1" bordercolor="#ccc" cellpadding="5" cellspacing="0" style="border-collapse:collapse;margin:10px 0 10px 15px;"&gt;
56 &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt;
57 &lt;thead&gt;
58 &lt;tr&gt;
59 &lt;th scope="col"&gt;Position&lt;/th&gt;
60 &lt;th scope="col"&gt;Astronaut&lt;/th&gt;
61 &lt;/tr&gt;
62 &lt;/thead&gt;
63 &lt;tbody&gt;
64 &lt;tr&gt;
65 &lt;td&gt;Commander&lt;/td&gt;
66 &lt;td&gt;Neil A. Armstrong&lt;/td&gt;
67 &lt;/tr&gt;
68 &lt;tr&gt;
69 &lt;td&gt;Command Module Pilot&lt;/td&gt;
70 &lt;td&gt;Michael Collins&lt;/td&gt;
71 &lt;/tr&gt;
72 &lt;tr&gt;
73 &lt;td&gt;Lunar Module Pilot&lt;/td&gt;
74 &lt;td&gt;Edwin &quot;Buzz&quot; E. Aldrin, Jr.&lt;/td&gt;
75 &lt;/tr&gt;
76 &lt;/tbody&gt;
77 &lt;/table&gt;
78
79 &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href="http://en.wikipedia.org/wiki/Kennedy_Space_Center" title="Kennedy Space Center"&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href="http://en.wikipedia.org/wiki/NASA" title="NASA"&gt;NASA&lt;/a&gt;&#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt;
80
81 &lt;ol&gt;
82 &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt;
83 &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt;
84 &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt;
85 &lt;/ol&gt;
86
87 &lt;p&gt;After being sent to the Moon by the Saturn V&#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href="http://en.wikipedia.org/wiki/Mare_Tranquillitatis" title="Mare Tranquillitatis"&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href="http://en.wikipedia.org/wiki/Pacific_Ocean" title="Pacific Ocean"&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt;
88
89 &lt;hr /&gt;
90 &lt;p style="text-align: right;"&gt;&lt;small&gt;Source: &lt;a href="http://en.wikipedia.org/wiki/Apollo_11"&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
91 </textarea>
92 </p>
93 <p>
94 <input type="submit" value="Submit">
95 </p>
96 </form>
97
98 <script>
99 CKEDITOR.inline( 'article-body' );
100 </script>
101 <div id="footer">
102 <hr>
103 <p>
104 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">
105 http://ckeditor.com</a>
106 </p>
107 <p id="copy">
108 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a>
109 - Frederico Knabben. All rights reserved.
110 </p>
111 </div>
112</body>
113</html>
diff --git a/sources/samples/old/jquery.html b/sources/samples/old/jquery.html
new file mode 100644
index 0000000..2f6d958
--- /dev/null
+++ b/sources/samples/old/jquery.html
@@ -0,0 +1,103 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>jQuery Adapter &mdash; CKEditor Sample</title>
10 <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
11 <script src="../../ckeditor.js"></script>
12 <script src="../../adapters/jquery.js"></script>
13 <link href="sample.css" rel="stylesheet">
14 <style>
15
16 #editable
17 {
18 padding: 10px;
19 float: left;
20 }
21
22 </style>
23 <script>
24
25 CKEDITOR.disableAutoInline = true;
26
27 $( document ).ready( function() {
28 $( '#editor1' ).ckeditor(); // Use CKEDITOR.replace() if element is <textarea>.
29 $( '#editable' ).ckeditor(); // Use CKEDITOR.inline().
30 } );
31
32 function setValue() {
33 $( '#editor1' ).val( $( 'input#val' ).val() );
34 }
35
36 </script>
37</head>
38<body>
39 <h1 class="samples">
40 <a href="index.html" id="a-test">CKEditor Samples</a> &raquo; Create Editors with jQuery
41 </h1>
42 <div class="warning deprecated">
43 This sample is not maintained anymore. Check out the <a href="http://sdk.ckeditor.com/">brand new samples in CKEditor SDK</a>.
44 </div>
45 <form action="sample_posteddata.php" method="post">
46 <div class="description">
47 <p>
48 This sample shows how to use the <a href="http://docs.ckeditor.com/#!/guide/dev_jquery">jQuery adapter</a>.
49 Note that you have to include both CKEditor and jQuery scripts before including the adapter.
50 </p>
51
52<pre class="samples">
53&lt;script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"&gt;&lt;/script&gt;
54&lt;script src="/ckedit../../ckeditor.js"&gt;&lt;/script&gt;
55&lt;script src="/ckeditor/adapters/jquery.js"&gt;&lt;/script&gt;
56</pre>
57
58 <p>Then you can replace HTML elements with a CKEditor instance using the <code>ckeditor()</code> method.</p>
59
60<pre class="samples">
61$( document ).ready( function() {
62 $( 'textarea#editor1' ).ckeditor();
63} );
64</pre>
65 </div>
66
67 <h2 class="samples">Inline Example</h2>
68
69 <div id="editable" contenteditable="true">
70 <p><img alt="Saturn V carrying Apollo 11" class="right" src="assets/sample.jpg"/><b>Apollo 11</b> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.</p>
71 <p>Armstrong spent about <s>three and a half</s> two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&nbsp;kg) of lunar material for return to Earth. A third member of the mission, <a href="http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)" title="Michael Collins (astronaut)">Michael Collins</a>, piloted the <a href="http://en.wikipedia.org/wiki/Apollo_Command/Service_Module" title="Apollo Command/Service Module">command</a> spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.
72 <p>Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:</p>
73 <blockquote><p>One small step for [a] man, one giant leap for mankind.</p></blockquote> <p>Apollo 11 effectively ended the <a href="http://en.wikipedia.org/wiki/Space_Race" title="Space Race">Space Race</a> and fulfilled a national goal proposed in 1961 by the late U.S. President <a href="http://en.wikipedia.org/wiki/John_F._Kennedy" title="John F. Kennedy">John F. Kennedy</a> in a speech before the United States Congress:</p> <blockquote><p>[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.</p></blockquote>
74 </div>
75
76 <br style="clear: both">
77
78 <h2 class="samples">Classic (iframe-based) Example</h2>
79
80 <textarea cols="80" id="editor1" name="editor1" rows="10">
81 &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
82 </textarea>
83
84 <p style="overflow: hidden">
85 <input style="float: left" type="submit" value="Submit">
86 <span style="float: right">
87 <input type="text" id="val" value="I'm using jQuery val()!" size="30">
88 <input onclick="setValue();" type="button" value="Set value">
89 </span>
90 </p>
91 </form>
92 <div id="footer">
93 <hr>
94 <p>
95 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
96 </p>
97 <p id="copy">
98 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
99 Knabben. All rights reserved.
100 </p>
101 </div>
102</body>
103</html>
diff --git a/sources/samples/old/readonly.html b/sources/samples/old/readonly.html
new file mode 100644
index 0000000..084d6af
--- /dev/null
+++ b/sources/samples/old/readonly.html
@@ -0,0 +1,76 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Using the CKEditor Read-Only API &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link rel="stylesheet" href="sample.css">
12 <script>
13
14 var editor;
15
16 // The instanceReady event is fired, when an instance of CKEditor has finished
17 // its initialization.
18 CKEDITOR.on( 'instanceReady', function( ev ) {
19 editor = ev.editor;
20
21 // Show this "on" button.
22 document.getElementById( 'readOnlyOn' ).style.display = '';
23
24 // Event fired when the readOnly property changes.
25 editor.on( 'readOnly', function() {
26 document.getElementById( 'readOnlyOn' ).style.display = this.readOnly ? 'none' : '';
27 document.getElementById( 'readOnlyOff' ).style.display = this.readOnly ? '' : 'none';
28 });
29 });
30
31 function toggleReadOnly( isReadOnly ) {
32 // Change the read-only state of the editor.
33 // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setReadOnly
34 editor.setReadOnly( isReadOnly );
35 }
36
37 </script>
38</head>
39<body>
40 <h1 class="samples">
41 <a href="index.html">CKEditor Samples</a> &raquo; Using the CKEditor Read-Only API
42 </h1>
43 <div class="warning deprecated">
44 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/readonly.html">brand new version in CKEditor SDK</a>.
45 </div>
46 <div class="description">
47 <p>
48 This sample shows how to use the
49 <code><a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setReadOnly">setReadOnly</a></code>
50 API to put editor into the read-only state that makes it impossible for users to change the editor contents.
51 </p>
52 <p>
53 For details on how to create this setup check the source code of this sample page.
54 </p>
55 </div>
56 <form action="sample_posteddata.php" method="post">
57 <p>
58 <textarea class="ckeditor" id="editor1" name="editor1" cols="100" rows="10">&lt;p&gt;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using &lt;a href="http://ckeditor.com/"&gt;CKEditor&lt;/a&gt;.&lt;/p&gt;</textarea>
59 </p>
60 <p>
61 <input id="readOnlyOn" onclick="toggleReadOnly();" type="button" value="Make it read-only" style="display:none">
62 <input id="readOnlyOff" onclick="toggleReadOnly( false );" type="button" value="Make it editable again" style="display:none">
63 </p>
64 </form>
65 <div id="footer">
66 <hr>
67 <p>
68 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
69 </p>
70 <p id="copy">
71 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
72 Knabben. All rights reserved.
73 </p>
74 </div>
75</body>
76</html>
diff --git a/sources/samples/old/replacebyclass.html b/sources/samples/old/replacebyclass.html
new file mode 100644
index 0000000..23652d6
--- /dev/null
+++ b/sources/samples/old/replacebyclass.html
@@ -0,0 +1,60 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Replace Textareas by Class Name &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link rel="stylesheet" href="sample.css">
12</head>
13<body>
14 <h1 class="samples">
15 <a href="index.html">CKEditor Samples</a> &raquo; Replace Textarea Elements by Class Name
16 </h1>
17 <div class="warning deprecated">
18 This sample is not maintained anymore. Check out the <a href="http://sdk.ckeditor.com/">brand new samples in CKEditor SDK</a>.
19 </div>
20 <div class="description">
21 <p>
22 This sample shows how to automatically replace all <code>&lt;textarea&gt;</code> elements
23 of a given class with a CKEditor instance.
24 </p>
25 <p>
26 To replace a <code>&lt;textarea&gt;</code> element, simply assign it the <code>ckeditor</code>
27 class, as in the code below:
28 </p>
29<pre class="samples">
30&lt;textarea <strong>class="ckeditor</strong>" name="editor1"&gt;&lt;/textarea&gt;
31</pre>
32 <p>
33 Note that other <code>&lt;textarea&gt;</code> attributes (like <code>id</code> or <code>name</code>) need to be adjusted to your document.
34 </p>
35 </div>
36 <form action="sample_posteddata.php" method="post">
37 <p>
38 <label for="editor1">
39 Editor 1:
40 </label>
41 <textarea class="ckeditor" cols="80" id="editor1" name="editor1" rows="10">
42 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
43 </textarea>
44 </p>
45 <p>
46 <input type="submit" value="Submit">
47 </p>
48 </form>
49 <div id="footer">
50 <hr>
51 <p>
52 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
53 </p>
54 <p id="copy">
55 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
56 Knabben. All rights reserved.
57 </p>
58 </div>
59</body>
60</html>
diff --git a/sources/samples/old/replacebycode.html b/sources/samples/old/replacebycode.html
new file mode 100644
index 0000000..a4f4395
--- /dev/null
+++ b/sources/samples/old/replacebycode.html
@@ -0,0 +1,59 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>Replace Textarea by Code &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link href="sample.css" rel="stylesheet">
12</head>
13<body>
14 <h1 class="samples">
15 <a href="index.html">CKEditor Samples</a> &raquo; Replace Textarea Elements Using JavaScript Code
16 </h1>
17 <div class="warning deprecated">
18 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/classic.html">brand new version in CKEditor SDK</a>.
19 </div>
20 <form action="sample_posteddata.php" method="post">
21 <div class="description">
22 <p>
23 This editor is using an <code>&lt;iframe&gt;</code> element-based editing area, provided by the <strong>Wysiwygarea</strong> plugin.
24 </p>
25<pre class="samples">
26CKEDITOR.replace( '<em>textarea_id</em>' )
27</pre>
28 </div>
29 <textarea cols="80" id="editor1" name="editor1" rows="10">
30 &lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
31 </textarea>
32 <script>
33
34 // This call can be placed at any point after the
35 // <textarea>, or inside a <head><script> in a
36 // window.onload event handler.
37
38 // Replace the <textarea id="editor"> with an CKEditor
39 // instance, using default configurations.
40
41 CKEDITOR.replace( 'editor1' );
42
43 </script>
44 <p>
45 <input type="submit" value="Submit">
46 </p>
47 </form>
48 <div id="footer">
49 <hr>
50 <p>
51 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
52 </p>
53 <p id="copy">
54 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
55 Knabben. All rights reserved.
56 </p>
57 </div>
58</body>
59</html>
diff --git a/sources/samples/old/sample.css b/sources/samples/old/sample.css
new file mode 100644
index 0000000..3304111
--- /dev/null
+++ b/sources/samples/old/sample.css
@@ -0,0 +1,357 @@
1/*
2Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3For licensing, see LICENSE.md or http://ckeditor.com/license
4*/
5
6html, body, h1, h2, h3, h4, h5, h6, div, span, blockquote, p, address, form, fieldset, img, ul, ol, dl, dt, dd, li, hr, table, td, th, strong, em, sup, sub, dfn, ins, del, q, cite, var, samp, code, kbd, tt, pre
7{
8 line-height: 1.5;
9}
10
11body
12{
13 padding: 10px 30px;
14}
15
16input, textarea, select, option, optgroup, button, td, th
17{
18 font-size: 100%;
19}
20
21pre
22{
23 -moz-tab-size: 4;
24 tab-size: 4;
25}
26
27pre, code, kbd, samp, tt
28{
29 font-family: monospace,monospace;
30 font-size: 1em;
31}
32
33body {
34 width: 960px;
35 margin: 0 auto;
36}
37
38code
39{
40 background: #f3f3f3;
41 border: 1px solid #ddd;
42 padding: 1px 4px;
43 border-radius: 3px;
44}
45
46abbr
47{
48 border-bottom: 1px dotted #555;
49 cursor: pointer;
50}
51
52.new, .beta
53{
54 text-transform: uppercase;
55 font-size: 10px;
56 font-weight: bold;
57 padding: 1px 4px;
58 margin: 0 0 0 5px;
59 color: #fff;
60 float: right;
61 border-radius: 3px;
62}
63
64.new
65{
66 background: #FF7E00;
67 border: 1px solid #DA8028;
68 text-shadow: 0 1px 0 #C97626;
69
70 box-shadow: 0 2px 3px 0 #FFA54E inset;
71}
72
73.beta
74{
75 background: #18C0DF;
76 border: 1px solid #19AAD8;
77 text-shadow: 0 1px 0 #048CAD;
78 font-style: italic;
79
80 box-shadow: 0 2px 3px 0 #50D4FD inset;
81}
82
83h1.samples
84{
85 color: #0782C1;
86 font-size: 200%;
87 font-weight: normal;
88 margin: 0;
89 padding: 0;
90}
91
92h1.samples a
93{
94 color: #0782C1;
95 text-decoration: none;
96 border-bottom: 1px dotted #0782C1;
97}
98
99.samples a:hover
100{
101 border-bottom: 1px dotted #0782C1;
102}
103
104h2.samples
105{
106 color: #000000;
107 font-size: 130%;
108 margin: 15px 0 0 0;
109 padding: 0;
110}
111
112p, blockquote, address, form, pre, dl, h1.samples, h2.samples
113{
114 margin-bottom: 15px;
115}
116
117ul.samples
118{
119 margin-bottom: 15px;
120}
121
122.clear
123{
124 clear: both;
125}
126
127fieldset
128{
129 margin: 0;
130 padding: 10px;
131}
132
133body, input, textarea
134{
135 color: #333333;
136 font-family: Arial, Helvetica, sans-serif;
137}
138
139body
140{
141 font-size: 75%;
142}
143
144a.samples
145{
146 color: #189DE1;
147 text-decoration: none;
148}
149
150form
151{
152 margin: 0;
153 padding: 0;
154}
155
156pre.samples
157{
158 background-color: #F7F7F7;
159 border: 1px solid #D7D7D7;
160 overflow: auto;
161 padding: 0.25em;
162 white-space: pre-wrap; /* CSS 2.1 */
163 word-wrap: break-word; /* IE7 */
164}
165
166#footer
167{
168 clear: both;
169 padding-top: 10px;
170}
171
172#footer hr
173{
174 margin: 10px 0 15px 0;
175 height: 1px;
176 border: solid 1px gray;
177 border-bottom: none;
178}
179
180#footer p
181{
182 margin: 0 10px 10px 10px;
183 float: left;
184}
185
186#footer #copy
187{
188 float: right;
189}
190
191#outputSample
192{
193 width: 100%;
194 table-layout: fixed;
195}
196
197#outputSample thead th
198{
199 color: #dddddd;
200 background-color: #999999;
201 padding: 4px;
202 white-space: nowrap;
203}
204
205#outputSample tbody th
206{
207 vertical-align: top;
208 text-align: left;
209}
210
211#outputSample pre
212{
213 margin: 0;
214 padding: 0;
215}
216
217.description
218{
219 border: 1px dotted #B7B7B7;
220 margin-bottom: 10px;
221 padding: 10px 10px 0;
222 overflow: hidden;
223}
224
225label
226{
227 display: block;
228 margin-bottom: 6px;
229}
230
231/**
232 * CKEditor editables are automatically set with the "cke_editable" class
233 * plus cke_editable_(inline|themed) depending on the editor type.
234 */
235
236/* Style a bit the inline editables. */
237.cke_editable.cke_editable_inline
238{
239 cursor: pointer;
240}
241
242/* Once an editable element gets focused, the "cke_focus" class is
243 added to it, so we can style it differently. */
244.cke_editable.cke_editable_inline.cke_focus
245{
246 box-shadow: inset 0px 0px 20px 3px #ddd, inset 0 0 1px #000;
247 outline: none;
248 background: #eee;
249 cursor: text;
250}
251
252/* Avoid pre-formatted overflows inline editable. */
253.cke_editable_inline pre
254{
255 white-space: pre-wrap;
256 word-wrap: break-word;
257}
258
259/**
260 * Samples index styles.
261 */
262
263.twoColumns,
264.twoColumnsLeft,
265.twoColumnsRight
266{
267 overflow: hidden;
268}
269
270.twoColumnsLeft,
271.twoColumnsRight
272{
273 width: 45%;
274}
275
276.twoColumnsLeft
277{
278 float: left;
279}
280
281.twoColumnsRight
282{
283 float: right;
284}
285
286dl.samples
287{
288 padding: 0 0 0 40px;
289}
290dl.samples > dt
291{
292 display: list-item;
293 list-style-type: disc;
294 list-style-position: outside;
295 margin: 0 0 3px;
296}
297dl.samples > dd
298{
299 margin: 0 0 3px;
300}
301.warning
302{
303 color: #ff0000;
304 background-color: #FFCCBA;
305 border: 2px dotted #ff0000;
306 padding: 15px 10px;
307 margin: 10px 0;
308}
309
310.warning.deprecated {
311 font-size: 1.3em;
312}
313
314/* Used on inline samples */
315
316blockquote
317{
318 font-style: italic;
319 font-family: Georgia, Times, "Times New Roman", serif;
320 padding: 2px 0;
321 border-style: solid;
322 border-color: #ccc;
323 border-width: 0;
324}
325
326.cke_contents_ltr blockquote
327{
328 padding-left: 20px;
329 padding-right: 8px;
330 border-left-width: 5px;
331}
332
333.cke_contents_rtl blockquote
334{
335 padding-left: 8px;
336 padding-right: 20px;
337 border-right-width: 5px;
338}
339
340img.right {
341 border: 1px solid #ccc;
342 float: right;
343 margin-left: 15px;
344 padding: 5px;
345}
346
347img.left {
348 border: 1px solid #ccc;
349 float: left;
350 margin-right: 15px;
351 padding: 5px;
352}
353
354.marker
355{
356 background-color: Yellow;
357}
diff --git a/sources/samples/old/sample.js b/sources/samples/old/sample.js
new file mode 100644
index 0000000..7d4c74e
--- /dev/null
+++ b/sources/samples/old/sample.js
@@ -0,0 +1,51 @@
1/**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6// Tool scripts for the sample pages.
7// This file can be ignored and is not required to make use of CKEditor.
8
9( function() {
10 CKEDITOR.on( 'instanceReady', function( ev ) {
11 // Check for sample compliance.
12 var editor = ev.editor,
13 meta = CKEDITOR.document.$.getElementsByName( 'ckeditor-sample-required-plugins' ),
14 requires = meta.length ? CKEDITOR.dom.element.get( meta[ 0 ] ).getAttribute( 'content' ).split( ',' ) : [],
15 missing = [],
16 i;
17
18 if ( requires.length ) {
19 for ( i = 0; i < requires.length; i++ ) {
20 if ( !editor.plugins[ requires[ i ] ] )
21 missing.push( '<code>' + requires[ i ] + '</code>' );
22 }
23
24 if ( missing.length ) {
25 var warn = CKEDITOR.dom.element.createFromHtml(
26 '<div class="warning">' +
27 '<span>To fully experience this demo, the ' + missing.join( ', ' ) + ' plugin' + ( missing.length > 1 ? 's are' : ' is' ) + ' required.</span>' +
28 '</div>'
29 );
30 warn.insertBefore( editor.container );
31 }
32 }
33
34 // Set icons.
35 var doc = new CKEDITOR.dom.document( document ),
36 icons = doc.find( '.button_icon' );
37
38 for ( i = 0; i < icons.count(); i++ ) {
39 var icon = icons.getItem( i ),
40 name = icon.getAttribute( 'data-icon' ),
41 style = CKEDITOR.skin.getIconStyle( name, ( CKEDITOR.lang.dir == 'rtl' ) );
42
43 icon.addClass( 'cke_button_icon' );
44 icon.addClass( 'cke_button__' + name + '_icon' );
45 icon.setAttribute( 'style', style );
46 icon.setStyle( 'float', 'none' );
47
48 }
49 } );
50} )();
51// %LEAVE_UNMINIFIED% %REMOVE_LINE%
diff --git a/sources/samples/old/sample_posteddata.php b/sources/samples/old/sample_posteddata.php
new file mode 100644
index 0000000..54e9b7c
--- /dev/null
+++ b/sources/samples/old/sample_posteddata.php
@@ -0,0 +1,16 @@
1<?php /* <body><pre>
2
3-------------------------------------------------------------------------------------------
4 CKEditor - Posted Data
5
6 We are sorry, but your Web server does not support the PHP language used in this script.
7
8 Please note that CKEditor can be used with any other server-side language than just PHP.
9 To save the content created with CKEditor you need to read the POST data on the server
10 side and write it to a file or the database.
11
12 Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
13 For licensing, see LICENSE.md or http://ckeditor.com/license
14-------------------------------------------------------------------------------------------
15
16</pre><div style="display:none"></body> */ include "assets/posteddata.php"; ?>
diff --git a/sources/samples/old/tabindex.html b/sources/samples/old/tabindex.html
new file mode 100644
index 0000000..4238f33
--- /dev/null
+++ b/sources/samples/old/tabindex.html
@@ -0,0 +1,78 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>TAB Key-Based Navigation &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link href="sample.css" rel="stylesheet">
12 <style>
13
14 .cke_focused,
15 .cke_editable.cke_focused
16 {
17 outline: 3px dotted blue !important;
18 *border: 3px dotted blue !important; /* For IE7 */
19 }
20
21 </style>
22 <script>
23
24 CKEDITOR.on( 'instanceReady', function( evt ) {
25 var editor = evt.editor;
26 editor.setData( 'This editor has it\'s tabIndex set to <strong>' + editor.tabIndex + '</strong>' );
27
28 // Apply focus class name.
29 editor.on( 'focus', function() {
30 editor.container.addClass( 'cke_focused' );
31 });
32 editor.on( 'blur', function() {
33 editor.container.removeClass( 'cke_focused' );
34 });
35
36 // Put startup focus on the first editor in tab order.
37 if ( editor.tabIndex == 1 )
38 editor.focus();
39 });
40
41 </script>
42</head>
43<body>
44 <h1 class="samples">
45 <a href="index.html">CKEditor Samples</a> &raquo; TAB Key-Based Navigation
46 </h1>
47 <div class="warning deprecated">
48 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/tabindex.html">brand new version in CKEditor SDK</a>.
49 </div>
50 <div class="description">
51 <p>
52 This sample shows how tab key navigation among editor instances is
53 affected by the <code>tabIndex</code> attribute from
54 the original page element. Use TAB key to move between the editors.
55 </p>
56 </div>
57 <p>
58 <textarea class="ckeditor" cols="80" id="editor4" rows="10" tabindex="1"></textarea>
59 </p>
60 <div class="ckeditor" contenteditable="true" id="editor1" tabindex="4"></div>
61 <p>
62 <textarea class="ckeditor" cols="80" id="editor2" rows="10" tabindex="2"></textarea>
63 </p>
64 <p>
65 <textarea class="ckeditor" cols="80" id="editor3" rows="10" tabindex="3"></textarea>
66 </p>
67 <div id="footer">
68 <hr>
69 <p>
70 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
71 </p>
72 <p id="copy">
73 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
74 Knabben. All rights reserved.
75 </p>
76 </div>
77</body>
78</html>
diff --git a/sources/samples/old/uicolor.html b/sources/samples/old/uicolor.html
new file mode 100644
index 0000000..fb61b1f
--- /dev/null
+++ b/sources/samples/old/uicolor.html
@@ -0,0 +1,72 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>UI Color Picker &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <link rel="stylesheet" href="sample.css">
12</head>
13<body>
14 <h1 class="samples">
15 <a href="index.html">CKEditor Samples</a> &raquo; UI Color
16 </h1>
17 <div class="warning deprecated">
18 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/uicolor.html">brand new version in CKEditor SDK</a>.
19 </div>
20 <div class="description">
21 <p>
22 This sample shows how to automatically replace <code>&lt;textarea&gt;</code> elements
23 with a CKEditor instance with an option to change the color of its user interface.<br>
24 <strong>Note:</strong>The UI skin color feature depends on the CKEditor skin
25 compatibility. The Moono and Kama skins are examples of skins that work with it.
26 </p>
27 </div>
28 <form action="sample_posteddata.php" method="post">
29 <p>
30 This editor instance has a UI color value defined in configuration to change the skin color,
31 To specify the color of the user interface, set the <code>uiColor</code> property:
32 </p>
33 <pre class="samples">
34CKEDITOR.replace( '<em>textarea_id</em>', {
35 <strong>uiColor: '#14B8C4'</strong>
36});</pre>
37 <p>
38 Note that <code><em>textarea_id</em></code> in the code above is the <code>id</code> attribute of
39 the <code>&lt;textarea&gt;</code> element to be replaced.
40 </p>
41 <p>
42 <textarea cols="80" id="editor1" name="editor1" rows="10">&lt;p&gt;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using &lt;a href="http://ckeditor.com/"&gt;CKEditor&lt;/a&gt;.&lt;/p&gt;</textarea>
43 <script>
44
45 // Replace the <textarea id="editor"> with an CKEditor
46 // instance, using default configurations.
47 CKEDITOR.replace( 'editor1', {
48 uiColor: '#14B8C4',
49 toolbar: [
50 [ 'Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink' ],
51 [ 'FontSize', 'TextColor', 'BGColor' ]
52 ]
53 });
54
55 </script>
56 </p>
57 <p>
58 <input type="submit" value="Submit">
59 </p>
60 </form>
61 <div id="footer">
62 <hr>
63 <p>
64 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
65 </p>
66 <p id="copy">
67 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
68 Knabben. All rights reserved.
69 </p>
70 </div>
71</body>
72</html>
diff --git a/sources/samples/old/uilanguages.html b/sources/samples/old/uilanguages.html
new file mode 100644
index 0000000..76749cb
--- /dev/null
+++ b/sources/samples/old/uilanguages.html
@@ -0,0 +1,122 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>User Interface Globalization &mdash; CKEditor Sample</title>
10 <script src="../../ckeditor.js"></script>
11 <script src="assets/uilanguages/languages.js"></script>
12 <link rel="stylesheet" href="sample.css">
13</head>
14<body>
15 <h1 class="samples">
16 <a href="index.html">CKEditor Samples</a> &raquo; User Interface Languages
17 </h1>
18 <div class="warning deprecated">
19 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/uilanguages.html">brand new version in CKEditor SDK</a>.
20 </div>
21 <div class="description">
22 <p>
23 This sample shows how to automatically replace <code>&lt;textarea&gt;</code> elements
24 with a CKEditor instance with an option to change the language of its user interface.
25 </p>
26 <p>
27 It pulls the language list from CKEditor <code>_languages.js</code> file that contains the list of supported languages and creates
28 a drop-down list that lets the user change the UI language.
29 </p>
30 <p>
31 By default, CKEditor automatically localizes the editor to the language of the user.
32 The UI language can be controlled with two configuration options:
33 <code><a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-language">language</a></code> and
34 <code><a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-defaultLanguage">
35 defaultLanguage</a></code>. The <code>defaultLanguage</code> setting specifies the
36 default CKEditor language to be used when a localization suitable for user's settings is not available.
37 </p>
38 <p>
39 To specify the user interface language that will be used no matter what language is
40 specified in user's browser or operating system, set the <code>language</code> property:
41 </p>
42<pre class="samples">
43CKEDITOR.replace( '<em>textarea_id</em>', {
44 // Load the German interface.
45 <strong>language: 'de'</strong>
46});</pre>
47 <p>
48 Note that <code><em>textarea_id</em></code> in the code above is the <code>id</code> attribute of
49 the <code>&lt;textarea&gt;</code> element to be replaced.
50 </p>
51 </div>
52 <form action="sample_posteddata.php" method="post">
53 <p>
54 Available languages (<span id="count"> </span> languages!):<br>
55 <script>
56
57 document.write( '<select disabled="disabled" id="languages" onchange="createEditor( this.value );">' );
58
59 // Get the language list from the _languages.js file.
60 for ( var i = 0 ; i < window.CKEDITOR_LANGS.length ; i++ ) {
61 document.write(
62 '<option value="' + window.CKEDITOR_LANGS[i].code + '">' +
63 window.CKEDITOR_LANGS[i].name +
64 '</option>' );
65 }
66
67 document.write( '</select>' );
68
69 </script>
70 <br>
71 <span style="color: #888888">
72 (You may see strange characters if your system does not support the selected language)
73 </span>
74 </p>
75 <p>
76 <textarea cols="80" id="editor1" name="editor1" rows="10">&lt;p&gt;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using &lt;a href="http://ckeditor.com/"&gt;CKEditor&lt;/a&gt;.&lt;/p&gt;</textarea>
77 <script>
78
79 // Set the number of languages.
80 document.getElementById( 'count' ).innerHTML = window.CKEDITOR_LANGS.length;
81
82 var editor;
83
84 function createEditor( languageCode ) {
85 if ( editor )
86 editor.destroy();
87
88 // Replace the <textarea id="editor"> with an CKEditor
89 // instance, using default configurations.
90 editor = CKEDITOR.replace( 'editor1', {
91 language: languageCode,
92
93 on: {
94 instanceReady: function() {
95 // Wait for the editor to be ready to set
96 // the language combo.
97 var languages = document.getElementById( 'languages' );
98 languages.value = this.langCode;
99 languages.disabled = false;
100 }
101 }
102 });
103 }
104
105 // At page startup, load the default language:
106 createEditor( '' );
107
108 </script>
109 </p>
110 </form>
111 <div id="footer">
112 <hr>
113 <p>
114 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
115 </p>
116 <p id="copy">
117 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
118 Knabben. All rights reserved.
119 </p>
120 </div>
121</body>
122</html>
diff --git a/sources/samples/old/xhtmlstyle.html b/sources/samples/old/xhtmlstyle.html
new file mode 100644
index 0000000..c918766
--- /dev/null
+++ b/sources/samples/old/xhtmlstyle.html
@@ -0,0 +1,234 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<html>
7<head>
8 <meta charset="utf-8">
9 <title>XHTML Compliant Output &mdash; CKEditor Sample</title>
10 <meta name="ckeditor-sample-required-plugins" content="sourcearea">
11 <script src="../../ckeditor.js"></script>
12 <script src="sample.js"></script>
13 <link href="sample.css" rel="stylesheet">
14</head>
15<body>
16 <h1 class="samples">
17 <a href="index.html">CKEditor Samples</a> &raquo; Producing XHTML Compliant Output
18 </h1>
19 <div class="warning deprecated">
20 This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/basicstyles.html">brand new version in CKEditor SDK</a>.
21 </div>
22 <div class="description">
23 <p>
24 This sample shows how to configure CKEditor to output valid
25 <a class="samples" href="http://www.w3.org/TR/xhtml11/">XHTML 1.1</a> code.
26 Deprecated elements (<code>&lt;font&gt;</code>, <code>&lt;u&gt;</code>) or attributes
27 (<code>size</code>, <code>face</code>) will be replaced with XHTML compliant code.
28 </p>
29 <p>
30 To add a CKEditor instance outputting valid XHTML code, load the editor using a standard
31 JavaScript call and define CKEditor features to use the XHTML compliant elements and styles.
32 </p>
33 <p>
34 A snippet of the configuration code can be seen below; check the source of this page for
35 full definition:
36 </p>
37<pre class="samples">
38CKEDITOR.replace( '<em>textarea_id</em>', {
39 contentsCss: 'assets/outputxhtml.css',
40
41 coreStyles_bold: {
42 element: 'span',
43 attributes: { 'class': 'Bold' }
44 },
45 coreStyles_italic: {
46 element: 'span',
47 attributes: { 'class': 'Italic' }
48 },
49
50 ...
51});</pre>
52 </div>
53 <form action="sample_posteddata.php" method="post">
54 <p>
55 <label for="editor1">
56 Editor 1:
57 </label>
58 <textarea cols="80" id="editor1" name="editor1" rows="10">&lt;p&gt;This is some &lt;span class="Bold"&gt;sample text&lt;/span&gt;. You are using &lt;a href="http://ckeditor.com/"&gt;CKEditor&lt;/a&gt;.&lt;/p&gt;</textarea>
59 <script>
60
61 CKEDITOR.replace( 'editor1', {
62 /*
63 * Style sheet for the contents
64 */
65 contentsCss: 'assets/outputxhtml/outputxhtml.css',
66
67 /*
68 * Special allowed content rules for spans used by
69 * font face, size, and color buttons.
70 *
71 * Note: all rules have been written separately so
72 * it was possible to specify required classes.
73 */
74 extraAllowedContent: 'span(!FontColor1);span(!FontColor2);span(!FontColor3);' +
75 'span(!FontColor1BG);span(!FontColor2BG);span(!FontColor3BG);' +
76 'span(!FontComic);span(!FontCourier);span(!FontTimes);' +
77 'span(!FontSmaller);span(!FontLarger);span(!FontSmall);span(!FontBig);span(!FontDouble)',
78
79 /*
80 * Core styles.
81 */
82 coreStyles_bold: {
83 element: 'span',
84 attributes: { 'class': 'Bold' }
85 },
86 coreStyles_italic: {
87 element: 'span',
88 attributes: { 'class': 'Italic' }
89 },
90 coreStyles_underline: {
91 element: 'span',
92 attributes: { 'class': 'Underline' }
93 },
94 coreStyles_strike: {
95 element: 'span',
96 attributes: { 'class': 'StrikeThrough' },
97 overrides: 'strike'
98 },
99 coreStyles_subscript: {
100 element: 'span',
101 attributes: { 'class': 'Subscript' },
102 overrides: 'sub'
103 },
104 coreStyles_superscript: {
105 element: 'span',
106 attributes: { 'class': 'Superscript' },
107 overrides: 'sup'
108 },
109
110 /*
111 * Font face.
112 */
113
114 // List of fonts available in the toolbar combo. Each font definition is
115 // separated by a semi-colon (;). We are using class names here, so each font
116 // is defined by {Combo Label}/{Class Name}.
117 font_names: 'Comic Sans MS/FontComic;Courier New/FontCourier;Times New Roman/FontTimes',
118
119 // Define the way font elements will be applied to the document. The "span"
120 // element will be used. When a font is selected, the font name defined in the
121 // above list is passed to this definition with the name "Font", being it
122 // injected in the "class" attribute.
123 // We must also instruct the editor to replace span elements that are used to
124 // set the font (Overrides).
125 font_style: {
126 element: 'span',
127 attributes: { 'class': '#(family)' },
128 overrides: [
129 {
130 element: 'span',
131 attributes: {
132 'class': /^Font(?:Comic|Courier|Times)$/
133 }
134 }
135 ]
136 },
137
138 /*
139 * Font sizes.
140 */
141 fontSize_sizes: 'Smaller/FontSmaller;Larger/FontLarger;8pt/FontSmall;14pt/FontBig;Double Size/FontDouble',
142 fontSize_style: {
143 element: 'span',
144 attributes: { 'class': '#(size)' },
145 overrides: [
146 {
147 element: 'span',
148 attributes: {
149 'class': /^Font(?:Smaller|Larger|Small|Big|Double)$/
150 }
151 }
152 ]
153 } ,
154
155 /*
156 * Font colors.
157 */
158 colorButton_enableMore: false,
159
160 colorButton_colors: 'FontColor1/FF9900,FontColor2/0066CC,FontColor3/F00',
161 colorButton_foreStyle: {
162 element: 'span',
163 attributes: { 'class': '#(color)' },
164 overrides: [
165 {
166 element: 'span',
167 attributes: {
168 'class': /^FontColor(?:1|2|3)$/
169 }
170 }
171 ]
172 },
173
174 colorButton_backStyle: {
175 element: 'span',
176 attributes: { 'class': '#(color)BG' },
177 overrides: [
178 {
179 element: 'span',
180 attributes: {
181 'class': /^FontColor(?:1|2|3)BG$/
182 }
183 }
184 ]
185 },
186
187 /*
188 * Indentation.
189 */
190 indentClasses: [ 'Indent1', 'Indent2', 'Indent3' ],
191
192 /*
193 * Paragraph justification.
194 */
195 justifyClasses: [ 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyFull' ],
196
197 /*
198 * Styles combo.
199 */
200 stylesSet: [
201 { name: 'Strong Emphasis', element: 'strong' },
202 { name: 'Emphasis', element: 'em' },
203
204 { name: 'Computer Code', element: 'code' },
205 { name: 'Keyboard Phrase', element: 'kbd' },
206 { name: 'Sample Text', element: 'samp' },
207 { name: 'Variable', element: 'var' },
208
209 { name: 'Deleted Text', element: 'del' },
210 { name: 'Inserted Text', element: 'ins' },
211
212 { name: 'Cited Work', element: 'cite' },
213 { name: 'Inline Quotation', element: 'q' }
214 ]
215 });
216
217 </script>
218 </p>
219 <p>
220 <input type="submit" value="Submit">
221 </p>
222 </form>
223 <div id="footer">
224 <hr>
225 <p>
226 CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
227 </p>
228 <p id="copy">
229 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
230 Knabben. All rights reserved.
231 </p>
232 </div>
233</body>
234</html>
diff --git a/sources/samples/toolbarconfigurator/bender.js b/sources/samples/toolbarconfigurator/bender.js
new file mode 100644
index 0000000..d592866
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/bender.js
@@ -0,0 +1,52 @@
1/* jshint browser: false, node: true */
2
3'use strict';
4
5var config = {
6
7 applications: {
8 ckeditor: {
9 path: '../../',
10 files: [
11 'ckeditor.js'
12 ]
13 },
14
15 codemirror: {
16 path: '.',
17 files: [
18 'js/lib/codemirror/codemirror.js'
19 ]
20 },
21
22 toolbartool: {
23 path: '.',
24 files: [
25 'js/fulltoolbareditor.js',
26 'js/abstracttoolbarmodifier.js',
27 'js/toolbarmodifier.js',
28 'js/toolbartextmodifier.js'
29 ]
30 }
31 },
32
33 plugins: [
34 'node_modules/benderjs-mocha',
35 'node_modules/benderjs-chai'
36 ],
37
38 framework: 'mocha',
39
40 tests: {
41 'main': {
42 applications: [ 'ckeditor', 'codemirror', 'toolbartool' ],
43 basePath: 'tests/',
44 paths: [
45 '**',
46 '!**/_*/**'
47 ]
48 }
49 }
50};
51
52module.exports = config;
diff --git a/sources/samples/toolbarconfigurator/css/fontello.css b/sources/samples/toolbarconfigurator/css/fontello.css
new file mode 100644
index 0000000..bb32199
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/css/fontello.css
@@ -0,0 +1,55 @@
1@font-face {
2 font-family: 'fontello';
3 src: url('../font/fontello.eot?89024372');
4 src: url('../font/fontello.eot?89024372#iefix') format('embedded-opentype'),
5 url('../font/fontello.woff?89024372') format('woff'),
6 url('../font/fontello.ttf?89024372') format('truetype'),
7 url('../font/fontello.svg?89024372#fontello') format('svg');
8 font-weight: normal;
9 font-style: normal;
10}
11/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
12/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
13/*
14@media screen and (-webkit-min-device-pixel-ratio:0) {
15 @font-face {
16 font-family: 'fontello';
17 src: url('../font/fontello.svg?89024372#fontello') format('svg');
18 }
19}
20*/
21
22 [class^="icon-"]:before, [class*=" icon-"]:before {
23 font-family: "fontello";
24 font-style: normal;
25 font-weight: normal;
26 speak: none;
27
28 display: inline-block;
29 text-decoration: inherit;
30 width: 1em;
31 margin-right: .2em;
32 text-align: center;
33 /* opacity: .8; */
34
35 /* For safety - reset parent styles, that can break glyph codes*/
36 font-variant: normal;
37 text-transform: none;
38
39 /* fix buttons height, for twitter bootstrap */
40 line-height: 1em;
41
42 /* Animation center compensation - margins should be symmetric */
43 /* remove if not needed */
44 margin-left: .2em;
45
46 /* you can be more comfortable with increased icons size */
47 /* font-size: 120%; */
48
49 /* Uncomment for 3D effect */
50 /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
51}
52
53.icon-trash:before { content: '\e802'; } /* '' */
54.icon-down-big:before { content: '\e800'; } /* '' */
55.icon-up-big:before { content: '\e801'; } /* '' */ \ No newline at end of file
diff --git a/sources/samples/toolbarconfigurator/font/LICENSE.txt b/sources/samples/toolbarconfigurator/font/LICENSE.txt
new file mode 100644
index 0000000..b511054
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/font/LICENSE.txt
@@ -0,0 +1,12 @@
1Font license info
2
3
4## Font Awesome
5
6 Copyright (C) 2012 by Dave Gandy
7
8 Author: Dave Gandy
9 License: SIL ()
10 Homepage: http://fortawesome.github.com/Font-Awesome/
11
12
diff --git a/sources/samples/toolbarconfigurator/font/config.json b/sources/samples/toolbarconfigurator/font/config.json
new file mode 100644
index 0000000..94809d7
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/font/config.json
@@ -0,0 +1,28 @@
1{
2 "name": "",
3 "css_prefix_text": "icon-",
4 "css_use_suffix": false,
5 "hinting": true,
6 "units_per_em": 1000,
7 "ascent": 850,
8 "glyphs": [
9 {
10 "uid": "f48ae54adfb27d8ada53d0fd9e34ee10",
11 "css": "trash-empty",
12 "code": 59392,
13 "src": "fontawesome"
14 },
15 {
16 "uid": "1c4068ed75209e21af36017df8871802",
17 "css": "down-big",
18 "code": 59393,
19 "src": "fontawesome"
20 },
21 {
22 "uid": "95376bf082bfec6ce06ea1cda7bd7ead",
23 "css": "up-big",
24 "code": 59394,
25 "src": "fontawesome"
26 }
27 ]
28} \ No newline at end of file
diff --git a/sources/samples/toolbarconfigurator/font/fontello.eot b/sources/samples/toolbarconfigurator/font/fontello.eot
new file mode 100644
index 0000000..2732fad
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/font/fontello.eot
Binary files differ
diff --git a/sources/samples/toolbarconfigurator/font/fontello.svg b/sources/samples/toolbarconfigurator/font/fontello.svg
new file mode 100644
index 0000000..33d14ac
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/font/fontello.svg
@@ -0,0 +1,14 @@
1<?xml version="1.0" standalone="no"?>
2<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3<svg xmlns="http://www.w3.org/2000/svg">
4<metadata>Copyright (C) 2014 by original authors @ fontello.com</metadata>
5<defs>
6<font id="fontello" horiz-adv-x="1000" >
7<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
8<missing-glyph horiz-adv-x="1000" />
9<glyph glyph-name="trash" unicode="&#xe802;" d="m286 439v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m143 0v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m142 0v-321q0-8-5-13t-12-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q7 0 12-5t5-13z m72-404v529h-500v-529q0-12 4-22t8-15t6-5h464q2 0 6 5t8 15t4 22z m-375 601h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q22 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
10<glyph glyph-name="down-big" unicode="&#xe800;" d="m899 386q0-30-21-50l-363-364q-22-21-51-21q-29 0-50 21l-363 364q-21 20-21 50q0 29 21 51l41 41q22 21 51 21q29 0 50-21l164-164v393q0 29 21 50t51 22h71q29 0 50-22t21-50v-393l164 164q21 21 51 21q29 0 50-21l42-42q21-21 21-50z" horiz-adv-x="928.6" />
11<glyph glyph-name="up-big" unicode="&#xe801;" d="m899 308q0-28-21-50l-42-42q-21-21-50-21q-30 0-51 21l-164 164v-393q0-29-20-47t-51-19h-71q-30 0-51 19t-21 47v393l-164-164q-20-21-50-21t-50 21l-42 42q-21 21-21 50q0 30 21 51l363 363q20 21 50 21q30 0 51-21l363-363q21-22 21-51z" horiz-adv-x="928.6" />
12</font>
13</defs>
14</svg> \ No newline at end of file
diff --git a/sources/samples/toolbarconfigurator/font/fontello.ttf b/sources/samples/toolbarconfigurator/font/fontello.ttf
new file mode 100644
index 0000000..fbcbf06
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/font/fontello.ttf
Binary files differ
diff --git a/sources/samples/toolbarconfigurator/font/fontello.woff b/sources/samples/toolbarconfigurator/font/fontello.woff
new file mode 100644
index 0000000..e1d5647
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/font/fontello.woff
Binary files differ
diff --git a/sources/samples/toolbarconfigurator/index.html b/sources/samples/toolbarconfigurator/index.html
new file mode 100644
index 0000000..4d31289
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/index.html
@@ -0,0 +1,446 @@
1<!DOCTYPE html>
2<!--
3Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
4For licensing, see LICENSE.md or http://ckeditor.com/license
5-->
6<!--[if IE 8]><html class="ie8"><![endif]-->
7<!--[if gt IE 8]><html><![endif]-->
8<!--[if !IE]><!--><html><!--<![endif]-->
9<head>
10 <meta charset="utf-8">
11 <title>Toolbar Configurator</title>
12 <script src="../../ckeditor.js"></script>
13 <script>
14 if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
15 CKEDITOR.tools.enableHtml5Elements( document );
16 </script>
17 <link rel="stylesheet" href="lib/codemirror/codemirror.css">
18 <link rel="stylesheet" href="lib/codemirror/show-hint.css">
19 <link rel="stylesheet" href="lib/codemirror/neo.css">
20 <link rel="stylesheet" href="css/fontello.css">
21 <link rel="stylesheet" href="../css/samples.css">
22</head>
23<body id="toolbar">
24
25<nav class="navigation-a">
26 <div class="grid-container">
27 <ul class="navigation-a-left grid-width-70">
28 <li><a href="http://ckeditor.com">Project Homepage</a></li>
29 <li><a href="http://dev.ckeditor.com/">I found a bug</a></li>
30 <li><a href="http://github.com/ckeditor/ckeditor-dev" class="icon-pos-right icon-navigation-a-github">Fork CKEditor on GitHub</a></li>
31 </ul>
32 <ul class="navigation-a-right grid-width-30">
33 <li><a href="http://ckeditor.com/blog-list">CKEditor Blog</a></li>
34 </ul>
35 </div>
36</nav>
37
38<header class="header-a">
39 <div class="grid-container">
40 <h1 class="header-a-logo grid-width-30">
41 <a href="../index.html"><img src="../img/logo.png" alt="CKEditor Logo"></a>
42 </h1>
43 <nav class="navigation-b grid-width-70">
44 <ul>
45 <li><a href="../index.html" class="button-a">Start</a></li>
46 <li><a href="index.html" class="button-a button-a-background">Toolbar configurator</a></li>
47 </ul>
48 </nav>
49 </div>
50</header>
51
52<main>
53 <div class="adjoined-top">
54 <div class="grid-container">
55 <div class="content grid-width-100">
56 <div class="grid-container-nested">
57 <h1 class="grid-width-60">
58 Toolbar Configurator
59 <a href="#help-content" type="button" title="Configurator help" id="help" class="button-a button-a-background button-a-no-text icon-pos-left icon-question-mark">Help</a>
60 </h1>
61
62 <div class="grid-width-40 grid-switch-magic">
63 <div class="switch">
64 <span class="balloon-a balloon-a-se">Select configurator type</span>
65 <input type="radio" name="radio" data-num="1" id="radio-basic" />
66 <input type="radio" name="radio" data-num="2" id="radio-advanced" />
67 <label data-for="1" for="radio-basic">Basic</label>
68 <span class="switch-inner">
69 <span class="handler"></span>
70 </span>
71 <label data-for="2" for="radio-advanced">Advanced</label>
72 </div>
73 </div>
74 </div>
75 </div>
76 </div>
77 </div>
78 <div class="adjoined-bottom">
79 <div class="grid-container">
80 <div class="grid-width-100">
81 <div class="editors-container">
82 <div id="editor-basic"></div>
83 <div id="editor-advanced"></div>
84 </div>
85 </div>
86 </div>
87 </div>
88
89 <div class="grid-container configurator">
90 <div class="content grid-width-100">
91 <div class="configurator">
92 <div>
93 <div id="toolbarModifierWrapper"></div>
94 </div>
95 </div>
96 </div>
97 </div>
98
99 <div id="help-content">
100 <div class="grid-container">
101 <div class="grid-width-100">
102 <h2>What Am I Doing Here?</h2>
103
104 <div class="grid-container grid-container-nested">
105 <div class="basic">
106 <div class="grid-width-50">
107 <p>Arrange <a href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-toolbarGroups">toolbar groups</a>, toggle <a href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-removeButtons">button visibility</a> according to your needs and get your toolbar configuration.</p>
108 <p>You can replace the content of the <a href="../../config.js"><code>config.js</code></a> file with the generated configuration. If you already set some configuration options you will need to merge both configurations.</p>
109 </div>
110 <div class="grid-width-50">
111 <p>Read more about different ways of <a href="http://docs.ckeditor.com/#!/guide/dev_configuration">setting configuration</a> and do not forget about <strong>clearing browser cache</strong>.</p>
112 <p>Arranging toolbar groups is the recommended way of configuring the toolbar, but if you need more freedom you can use the <a href="#advanced">advanced configurator</a>.</p>
113 </div>
114 </div>
115 <div class="advanced" style="display: none;">
116 <div class="grid-width-50">
117 <p>With this code editor you can edit your <a href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-toolbar">toolbar configuration</a> live.</p>
118 <p>You can replace the content of the <a href="../../config.js"><code>config.js</code></a> file with the generated configuration. If you already set some configuration options you will need to merge both configurations.</p>
119 </div>
120 <div class="grid-width-50">
121 <p>Read more about different ways of <a href="http://docs.ckeditor.com/#!/guide/dev_configuration">setting configuration</a> and do not forget about <strong>clearing browser cache</strong>.</p>
122 </div>
123 </div>
124 </div>
125
126 <p class="grid-container grid-container-nested">
127 <button type="button" class="help-content-close grid-width-100 button-a button-a-background">Got it. Let's play!</button>
128 </p>
129 </div>
130 </div>
131 </div>
132</main>
133
134<footer class="footer-a grid-container">
135 <p class="grid-width-100">
136 CKEditor &ndash; The text editor for the Internet &ndash; <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
137 </p>
138 <p class="grid-width-100" id="copy">
139 Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> &ndash; Frederico Knabben. All rights reserved.
140 </p>
141</footer>
142
143<script src="lib/codemirror/codemirror.js"></script>
144<script src="lib/codemirror/javascript.js"></script>
145<script src="lib/codemirror/show-hint.js"></script>
146
147<script src="js/fulltoolbareditor.js"></script>
148<script src="js/abstracttoolbarmodifier.js"></script>
149<script src="js/toolbarmodifier.js"></script>
150<script src="js/toolbartextmodifier.js"></script>
151<script src="../js/sf.js"></script>
152
153<script>
154 ( function() {
155 'use strict';
156
157 var mode = ( window.location.hash.substr( 1 ) === 'advanced' ) ? 'advanced' : 'basic',
158 configuratorSection = CKEDITOR.document.findOne( 'main > .grid-container.configurator' ),
159 basicInstruction = CKEDITOR.document.findOne( '#help-content .basic' ),
160 advancedInstruction = CKEDITOR.document.findOne( '#help-content .advanced' ),
161
162 // Configurator mode switcher.
163 modeSwitchBasic = CKEDITOR.document.getById( 'radio-basic' ),
164 modeSwitchAdvanced = CKEDITOR.document.getById( 'radio-advanced' );
165
166 // Initial setup
167 function updateSwitcher() {
168 if ( mode === 'advanced' ) {
169 modeSwitchAdvanced.$.checked = true;
170 } else {
171 modeSwitchBasic.$.checked = true;
172 }
173 }
174
175 updateSwitcher();
176
177 CKEDITOR.document.getWindow().on( 'hashchange', function( e ) {
178 var hash = window.location.hash.substr( 1 );
179 if ( !( hash === 'advanced' || hash === 'basic' ) ) {
180 return;
181 }
182 mode = hash;
183 onToolbarsDone( mode );
184 } );
185
186 CKEDITOR.document.getWindow().on( 'resize', function() {
187 updateToolbar( ( mode === 'basic' ? toolbarModifier : toolbarTextModifier )[ 'editorInstance' ] );
188 } );
189
190 function onRefresh( modifier ) {
191 modifier = modifier || this;
192
193 if ( mode === 'basic' && modifier instanceof ToolbarConfigurator.ToolbarTextModifier ) {
194 return;
195 }
196
197 // CodeMirror container becomes visible, so we need to refresh and to avoid rendering problems.
198 if ( mode === 'advanced' && modifier instanceof ToolbarConfigurator.ToolbarTextModifier ) {
199 modifier.codeContainer.refresh();
200 }
201
202 updateToolbar( modifier.editorInstance );
203 }
204
205 function updateToolbar( editor ) {
206 var editorContainer = editor.container;
207
208 // Not always editor is loaded.
209 if ( !editorContainer ) {
210 return;
211 }
212
213 var displayStyle = editorContainer.getStyle( 'display' );
214
215 editorContainer.setStyle( 'display', 'block' );
216
217 var newHeight = editorContainer.getSize( 'height' );
218
219 var newMarginTop = parseInt( editorContainer.getComputedStyle( 'margin-top' ), 10 );
220 newMarginTop = ( isNaN( newMarginTop ) ? 0 : Number( newMarginTop ) );
221
222 var newMarginBottom = parseInt( editorContainer.getComputedStyle( 'margin-bottom' ), 10 );
223 newMarginBottom = ( isNaN( newMarginBottom ) ? 0 : Number( newMarginBottom ) );
224
225 var result = newHeight + newMarginTop + newMarginBottom;
226
227 editorContainer.setStyle( 'display', displayStyle );
228
229 editor.container.getAscendant( 'div' ).setStyle( 'height', result + 'px' );
230 }
231
232 var toolbarModifier = new ToolbarConfigurator.ToolbarModifier( 'editor-basic' );
233
234 var done = 0;
235 toolbarModifier.init( onToolbarInit );
236 toolbarModifier.onRefresh = onRefresh;
237
238 CKEDITOR.document.getById( 'toolbarModifierWrapper' ).append( toolbarModifier.mainContainer );
239
240 var toolbarTextModifier = new ToolbarConfigurator.ToolbarTextModifier( 'editor-advanced' );
241 toolbarTextModifier.init( onToolbarInit );
242 toolbarTextModifier.onRefresh = onRefresh;
243
244 function onToolbarInit() {
245 if ( ++done === 2 ) {
246 onToolbarsDone();
247
248 positionSticky.watch( CKEDITOR.document.findOne( '.toolbar' ), function() {
249 return mode === 'advanced';
250 } );
251 }
252 }
253
254 function onToolbarsDone() {
255 if ( mode === 'basic' ) {
256 toggleModeBasic( false );
257 } else {
258 toggleModeAdvanced( false );
259 }
260
261 updateSwitcher();
262
263 setTimeout( function() {
264 CKEDITOR.document.findOne( '.editors-container' ).addClass( 'active' );
265 CKEDITOR.document.findOne( '#toolbarModifierWrapper' ).addClass( 'active' );
266 }, 200 );
267 }
268
269 CKEDITOR.document.getById( 'toolbarModifierWrapper' ).append( toolbarTextModifier.mainContainer );
270
271 function toogleModeSwitch( onElement, offElement, onModifier, offModifier ) {
272 onElement.addClass( 'fancy-button-active' );
273 offElement.removeClass( 'fancy-button-active' );
274
275 onModifier.showUI();
276 offModifier.hideUI();
277 }
278
279 function toggleModeBasic( callOnRefresh ) {
280 callOnRefresh = ( callOnRefresh !== false );
281 mode = 'basic';
282 window.location.hash = '#basic';
283 toogleModeSwitch( modeSwitchBasic, modeSwitchAdvanced, toolbarModifier, toolbarTextModifier );
284
285 configuratorSection.removeClass( 'freed-width' );
286 basicInstruction.show();
287 advancedInstruction.hide();
288
289 callOnRefresh && onRefresh( toolbarModifier );
290 }
291
292 function toggleModeAdvanced( callOnRefresh ) {
293 callOnRefresh = ( callOnRefresh !== false );
294 mode = 'advanced';
295 window.location.hash = '#advanced';
296 toogleModeSwitch( modeSwitchAdvanced, modeSwitchBasic, toolbarTextModifier, toolbarModifier );
297
298 configuratorSection.addClass( 'freed-width' );
299 advancedInstruction.show();
300 basicInstruction.hide();
301
302 callOnRefresh && onRefresh( toolbarTextModifier );
303 }
304
305 modeSwitchBasic.on( 'click', toggleModeBasic );
306 modeSwitchAdvanced.on( 'click', toggleModeAdvanced );
307
308 //
309 // Position:sticky for the toolbar.
310 //
311
312 // Will make elements behave like they were styled with position:sticky.
313 var positionSticky = {
314 // Store object: {
315 // element: CKEDITOR.dom.element, // Element which will float.
316 // placeholder: CKEDITOR.dom.element, // Placeholder which is place to prevent page bounce.
317 // isFixed: boolean // Whether element float now.
318 // }
319 watched: [],
320
321 active: [],
322
323 staticContainer: null,
324
325 init: function() {
326 var element = CKEDITOR.dom.element.createFromHtml(
327 '<div class="staticContainer">' +
328 '<div class="grid-container" >' +
329 '<div class="grid-width-100">' +
330 '<div class="inner"></div>' +
331 '</div>' +
332 '</div>' +
333 '</div>' );
334
335 this.staticContainer = element.findOne( '.inner' );
336
337 CKEDITOR.document.getBody().append( element );
338 },
339
340 watch: function( element, preventFunc ) {
341 this.watched.push( {
342 element: element,
343 placeholder: new CKEDITOR.dom.element( 'div' ),
344 isFixed: false,
345 preventFunc: preventFunc
346 } );
347 },
348
349 checkAll: function() {
350 for ( var i = 0; i < this.watched.length; i++ ) {
351 this.check( this.watched[ i ] );
352 }
353 },
354
355 check: function( element ) {
356 var isFixed = element.isFixed;
357 var shouldBeFixed = this.shouldBeFixed( element );
358
359 // Nothing to be done.
360 if ( isFixed === shouldBeFixed ) {
361 return;
362 }
363
364 var placeholder = element.placeholder;
365
366 if ( isFixed ) {
367 // Unfixing.
368
369 element.element.insertBefore( placeholder );
370 placeholder.remove();
371
372 element.element.removeStyle( 'margin' );
373
374 this.active.splice( CKEDITOR.tools.indexOf( this.active, element ), 1 );
375
376 } else {
377 // Fixing.
378 placeholder.setStyle( 'width', element.element.getSize( 'width' ) + 'px' );
379 placeholder.setStyle( 'height', element.element.getSize( 'height' ) + 'px' );
380 placeholder.setStyle( 'margin-bottom', element.element.getComputedStyle( 'margin-bottom' ) );
381 placeholder.setStyle( 'display', element.element.getComputedStyle( 'display' ) );
382 placeholder.insertAfter( element.element );
383
384 this.staticContainer.append( element.element );
385
386 this.active.push( element );
387 }
388
389 element.isFixed = !element.isFixed;
390 },
391
392 shouldBeFixed: function( element ) {
393 if ( element.preventFunc && element.preventFunc() ) {
394 return false;
395 }
396
397 // If element is already fixed we are checking it's placeholder.
398 var related = ( element.isFixed ? element.placeholder : element.element ),
399 clientRect = related.$.getBoundingClientRect(),
400 staticHeight = this.staticContainer.getSize('height' ),
401 elemHeight = element.element.getSize( 'height' );
402
403 if ( element.isFixed ) {
404 return ( clientRect.top + elemHeight < staticHeight );
405 } else {
406 return ( clientRect.top < staticHeight );
407 }
408 }
409 };
410
411 positionSticky.init();
412
413 CKEDITOR.document.getWindow().on( 'scroll',
414 new CKEDITOR.tools.eventsBuffer( 100, positionSticky.checkAll, positionSticky ).input
415 );
416
417 // Make the toolbar sticky.
418 positionSticky.watch( CKEDITOR.document.findOne( '.editors-container' ) );
419
420 // Help button and help-content.
421 ( function() {
422 var helpButton = CKEDITOR.document.getById( 'help' ),
423 helpContent = CKEDITOR.document.getById( 'help-content' );
424
425 // Don't show help button on IE8 because it's unsupported by Pico Modal.
426 if ( CKEDITOR.env.ie && CKEDITOR.env.version == 8 ) {
427 helpButton.hide();
428 } else {
429 // Display help modal when the button is clicked.
430 helpButton.on( 'click', function( evt ) {
431 SF.modal( {
432 // Clone modal content from DOM.
433 content: helpContent.getHtml(),
434
435 afterCreate: function( modal ) {
436 // Enable modal content button to close the modal.
437 new CKEDITOR.dom.element( modal.modalElem() ).findOne( '.help-content-close' ).once( 'click', modal.close );
438 }
439 } ).show();
440 } );
441 }
442 } )();
443 } )();
444</script>
445</body>
446</html>
diff --git a/sources/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js b/sources/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js
new file mode 100644
index 0000000..af6cd62
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js
@@ -0,0 +1,566 @@
1/* global ToolbarConfigurator */
2
3'use strict';
4
5// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
6if ( typeof Object.create != 'function' ) {
7 ( function() {
8 var F = function() {};
9 Object.create = function( o ) {
10 if ( arguments.length > 1 ) {
11 throw Error( 'Second argument not supported' );
12 }
13 if ( o === null ) {
14 throw Error( 'Cannot set a null [[Prototype]]' );
15 }
16 if ( typeof o != 'object' ) {
17 throw TypeError( 'Argument must be an object' );
18 }
19 F.prototype = o;
20 return new F();
21 };
22 } )();
23}
24
25// Copy of the divarea plugin (with some enhancements), so we always have some editable mode, regardless of the build's config.
26CKEDITOR.plugins.add( 'toolbarconfiguratorarea', {
27 // Use afterInit to override wysiwygarea's mode. May still fail to override divarea, but divarea is nice.
28 afterInit: function( editor ) {
29 editor.addMode( 'wysiwyg', function( callback ) {
30 var editingBlock = CKEDITOR.dom.element.createFromHtml( '<div class="cke_wysiwyg_div cke_reset" hidefocus="true"></div>' );
31
32 var contentSpace = editor.ui.space( 'contents' );
33 contentSpace.append( editingBlock );
34
35 editingBlock = editor.editable( editingBlock );
36
37 editingBlock.detach = CKEDITOR.tools.override( editingBlock.detach,
38 function( org ) {
39 return function() {
40 org.apply( this, arguments );
41 this.remove();
42 };
43 } );
44
45 editor.setData( editor.getData( 1 ), callback );
46 editor.fire( 'contentDom' );
47 } );
48
49 // Additions to the divarea.
50
51 // Speed up data processing.
52 editor.dataProcessor.toHtml = function( html ) {
53 return html;
54 };
55 editor.dataProcessor.toDataFormat = function( html ) {
56 return html;
57 };
58
59 // End of the additions.
60 }
61} );
62
63// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
64if ( !Object.keys ) {
65 Object.keys = ( function() {
66 var hasOwnProperty = Object.prototype.hasOwnProperty,
67 hasDontEnumBug = !( { toString: null } ).propertyIsEnumerable( 'toString' ),
68 dontEnums = [
69 'toString',
70 'toLocaleString',
71 'valueOf',
72 'hasOwnProperty',
73 'isPrototypeOf',
74 'propertyIsEnumerable',
75 'constructor'
76 ],
77 dontEnumsLength = dontEnums.length;
78
79 return function( obj ) {
80 if ( typeof obj !== 'object' && ( typeof obj !== 'function' || obj === null ) )
81 throw new TypeError( 'Object.keys called on non-object' );
82
83 var result = [], prop, i;
84
85 for ( prop in obj ) {
86 if ( hasOwnProperty.call( obj, prop ) )
87 result.push( prop );
88
89 }
90
91 if ( hasDontEnumBug ) {
92 for ( i = 0; i < dontEnumsLength; i++ ) {
93 if ( hasOwnProperty.call( obj, dontEnums[ i ] ) )
94 result.push( dontEnums[ i ] );
95
96 }
97 }
98 return result;
99 };
100 }() );
101}
102
103( function() {
104 /**
105 * @class ToolbarConfigurator.AbstractToolbarModifier
106 * @param {String} editorId An id of modified editor
107 * @constructor
108 */
109 function AbstractToolbarModifier( editorId, cfg ) {
110 this.cfg = cfg || {};
111 this.hidden = false;
112 this.editorId = editorId;
113 this.fullToolbarEditor = new ToolbarConfigurator.FullToolbarEditor();
114
115 this.mainContainer = null;
116
117 this.originalConfig = null;
118 this.actualConfig = null;
119
120 this.waitForReady = false;
121 this.isEditableVisible = false;
122
123 this.toolbarContainer = null;
124 this.toolbarButtons = [];
125 }
126
127 // Expose the class.
128 ToolbarConfigurator.AbstractToolbarModifier = AbstractToolbarModifier;
129
130 /**
131 * @param {String} config
132 */
133 AbstractToolbarModifier.prototype.setConfig = function( config ) {
134 this._onInit( undefined, config, true );
135 };
136
137 /**
138 * @param {Function} [callback]
139 */
140 AbstractToolbarModifier.prototype.init = function( callback ) {
141 var that = this;
142
143 this.mainContainer = new CKEDITOR.dom.element( 'div' );
144
145 if ( this.fullToolbarEditor.editorInstance !== null ) {
146 throw 'Only one instance of ToolbarModifier is allowed';
147 }
148
149 if ( !this.editorInstance ) {
150 // Do not refresh yet, let's wait for the full toolbar editor (see below).
151 this._createEditor( false );
152 }
153
154 this.editorInstance.once( 'loaded', function() {
155 that.fullToolbarEditor.init( function() {
156 that._onInit( callback );
157
158 if ( typeof that.onRefresh == 'function' ) {
159 that.onRefresh();
160 }
161 }, that.editorInstance.config );
162 } );
163
164 return this.mainContainer;
165 };
166
167 /**
168 * Called editor initialization finished.
169 *
170 * @param {Function} callback
171 * @param {String} [actualConfig]
172 * @private
173 */
174 AbstractToolbarModifier.prototype._onInit = function( callback, actualConfig ) {
175 this.originalConfig = this.editorInstance.config;
176
177 if ( !actualConfig ) {
178 this.actualConfig = JSON.parse( JSON.stringify( this.originalConfig ) );
179 } else {
180 this.actualConfig = JSON.parse( actualConfig );
181 }
182
183 if ( !this.actualConfig.toolbarGroups && !this.actualConfig.toolbar ) {
184 this.actualConfig.toolbarGroups = getDefaultToolbarGroups( this.editorInstance );
185 }
186
187 if ( typeof callback === 'function' )
188 callback( this.mainContainer );
189
190 // Here we are going to keep only `name` and `groups` data from editor `toolbar` property.
191 function getDefaultToolbarGroups( editor ) {
192 var toolbarGroups = editor.toolbar,
193 copy = [];
194
195 var max = toolbarGroups.length;
196 for ( var i = 0; i < max; i++ ) {
197 var group = toolbarGroups[ i ];
198
199 if ( typeof group == 'string' ) {
200 copy.push( group ); // separator
201 } else {
202 copy.push( {
203 name: group.name,
204 groups: group.groups ? group.groups.slice() : []
205 } );
206 }
207 }
208
209 return copy;
210 }
211 };
212
213 /**
214 * Creates DOM structure of tool.
215 *
216 * @returns {CKEDITOR.dom.element}
217 * @private
218 */
219 AbstractToolbarModifier.prototype._createModifier = function() {
220 this.mainContainer.addClass( 'unselectable' );
221
222 if ( this.modifyContainer ) {
223 this.modifyContainer.remove();
224 }
225
226 this.modifyContainer = new CKEDITOR.dom.element( 'div' );
227 this.modifyContainer.addClass( 'toolbarModifier' );
228
229 this.mainContainer.append( this.modifyContainer );
230
231 return this.mainContainer;
232 };
233
234 /**
235 * Find editable area in CKEditor instance DOM container
236 *
237 * @returns {CKEDITOR.dom.element}
238 */
239 AbstractToolbarModifier.prototype.getEditableArea = function() {
240 var selector = ( '#' + this.editorInstance.id + '_contents' );
241
242 return this.editorInstance.container.findOne( selector );
243 };
244
245 /**
246 * Hide editable area in modified editor by sets its height to 0.
247 *
248 * @private
249 */
250 AbstractToolbarModifier.prototype._hideEditable = function() {
251 var area = this.getEditableArea();
252
253 this.isEditableVisible = false;
254
255 this.lastEditableAreaHeight = area.getStyle( 'height' );
256 area.setStyle( 'height', '0' );
257 };
258
259 /**
260 * Show editable area in modified editor.
261 *
262 * @private
263 */
264 AbstractToolbarModifier.prototype._showEditable = function() {
265 this.isEditableVisible = true;
266
267 this.getEditableArea().setStyle( 'height', this.lastEditableAreaHeight || 'auto' );
268 };
269
270 /**
271 * Toggle editable area visibility.
272 *
273 * @private
274 */
275 AbstractToolbarModifier.prototype._toggleEditable = function() {
276 if ( this.isEditableVisible )
277 this._hideEditable();
278 else
279 this._showEditable();
280 };
281
282 /**
283 * Usually called when configuration changes.
284 *
285 * @private
286 */
287 AbstractToolbarModifier.prototype._refreshEditor = function() {
288 var that = this,
289 status = this.editorInstance.status;
290
291 // Wait for ready only once.
292 if ( this.waitForReady )
293 return;
294
295 // Not ready.
296 if ( status == 'unloaded' || status == 'loaded' ) {
297 this.waitForReady = true;
298
299 this.editorInstance.once( 'instanceReady', function() {
300 refresh();
301 }, this );
302 // Ready or destroyed.
303 } else {
304 refresh();
305 }
306
307 function refresh() {
308 that.editorInstance.destroy();
309 that._createEditor( true, that.getActualConfig() );
310 that.waitForReady = false;
311 }
312 };
313
314 /**
315 * Creates editor that can be used to present the toolbar configuration.
316 *
317 * @private
318 */
319 AbstractToolbarModifier.prototype._createEditor = function( doRefresh, configOverrides ) {
320 var that = this;
321
322 this.editorInstance = CKEDITOR.replace( this.editorId );
323
324 this.editorInstance.on( 'configLoaded', function() {
325 var config = that.editorInstance.config;
326
327 if ( configOverrides ) {
328 CKEDITOR.tools.extend( config, configOverrides, true );
329 }
330
331 AbstractToolbarModifier.extendPluginsConfig( config );
332 } );
333
334 // Prevent creating any other space than the top one.
335 this.editorInstance.on( 'uiSpace', function( evt ) {
336 if ( evt.data.space != 'top' ) {
337 evt.stop();
338 }
339 }, null, null, -999 );
340
341 this.editorInstance.once( 'loaded', function() {
342 var btns = that.editorInstance.ui.instances;
343
344 for ( var i in btns ) {
345 if ( btns[ i ] ) {
346 btns[ i ].click = empty;
347 btns[ i ].onClick = empty;
348 }
349 }
350
351 if ( !that.isEditableVisible ) {
352 that._hideEditable();
353 }
354
355 if ( that.currentActive && that.currentActive.name ) {
356 that._highlightGroup( that.currentActive.name );
357 }
358
359 if ( that.hidden ) {
360 that.hideUI();
361 } else {
362 that.showUI();
363 }
364
365 if ( doRefresh && ( typeof that.onRefresh === 'function' ) ) {
366 that.onRefresh();
367 }
368 } );
369
370 function empty() {}
371 };
372
373 /**
374 * Always returns copy of config.
375 *
376 * @returns {Object}
377 */
378 AbstractToolbarModifier.prototype.getActualConfig = function() {
379 return JSON.parse( JSON.stringify( this.actualConfig ) );
380 };
381
382 /**
383 * Creates toolbar in tool.
384 *
385 * @private
386 */
387 AbstractToolbarModifier.prototype._createToolbar = function() {
388 if ( !this.toolbarButtons.length ) {
389 return;
390 }
391
392 this.toolbarContainer = new CKEDITOR.dom.element( 'div' );
393 this.toolbarContainer.addClass( 'toolbar' );
394
395 var max = this.toolbarButtons.length;
396 for ( var i = 0; i < max; i += 1 ) {
397 this._createToolbarBtn( this.toolbarButtons[ i ] );
398 }
399 };
400
401 /**
402 * Create toolbar button and add it to toolbar container
403 *
404 * @param {Object} cfg
405 * @returns {CKEDITOR.dom.element}
406 * @private
407 */
408 AbstractToolbarModifier.prototype._createToolbarBtn = function( cfg ) {
409 var btnText = ( typeof cfg.text === 'string' ? cfg.text : cfg.text.inactive ),
410 btn = ToolbarConfigurator.FullToolbarEditor.createButton( btnText, cfg.cssClass );
411
412 this.toolbarContainer.append( btn );
413 btn.data( 'group', cfg.group );
414 btn.addClass( cfg.position );
415 btn.on( 'click', function() {
416 cfg.clickCallback.call( this, btn, cfg );
417 }, this );
418
419 return btn;
420 };
421
422 /**
423 * @private
424 * @param {Object} config
425 */
426 AbstractToolbarModifier.prototype._fixGroups = function( config ) {
427 var groups = config.toolbarGroups || [];
428
429 var max = groups.length;
430 for ( var i = 0; i < max; i += 1 ) {
431 var currentGroup = groups[ i ];
432
433 // separator, in config, is in raw format
434 // need to make it more sophisticated to keep unique id
435 // for each one
436 if ( currentGroup == '/' ) {
437 currentGroup = groups[ i ] = {};
438 currentGroup.type = 'separator';
439 currentGroup.name = ( 'separator' + CKEDITOR.tools.getNextNumber() );
440 continue;
441 }
442
443 // sometimes subgroups are not set (basic package), so need to
444 // create them artifically
445 currentGroup.groups = currentGroup.groups || [];
446
447 // when there is no subgroup with same name like its parent name
448 // then it have to be added artificially
449 // in order to maintain consistency between user interface and config
450 if ( CKEDITOR.tools.indexOf( currentGroup.groups, currentGroup.name ) == -1 ) {
451 this.editorInstance.ui.addToolbarGroup( currentGroup.name, currentGroup.groups[ currentGroup.groups.length - 1 ], currentGroup.name );
452 currentGroup.groups.push( currentGroup.name );
453 }
454
455 this._fixSubgroups( currentGroup );
456 }
457 };
458
459 /**
460 * Transform subgroup string to object literal
461 * with keys: {String} name and {Number} totalBtns
462 * Please note: this method modify Object provided in first argument
463 *
464 * input:
465 * [
466 * { groups: [ 'nameOne', 'nameTwo' ] }
467 * ]
468 *
469 * output:
470 * [
471 * { groups: [ { name: 'nameOne', totalBtns: 3 }, { name: 'nameTwo', totalBtns: 5 } ] }
472 * ]
473 *
474 * @param {Object} group
475 * @private
476 */
477 AbstractToolbarModifier.prototype._fixSubgroups = function( group ) {
478 var subGroups = group.groups;
479
480 var max = subGroups.length;
481 for ( var i = 0; i < max; i += 1 ) {
482 var subgroupName = subGroups[ i ];
483
484 subGroups[ i ] = {
485 name: subgroupName,
486 totalBtns: ToolbarConfigurator.ToolbarModifier.getTotalSubGroupButtonsNumber( subgroupName, this.fullToolbarEditor )
487 };
488 }
489 };
490
491 /**
492 * Same as JSON.stringify method but returned string is in one line
493 *
494 * @param {Object} json
495 * @param {Object} opts
496 * @param {Boolean} opts.addSpaces
497 * @param {Boolean} opts.noQuotesOnKey
498 * @param {Boolean} opts.singleQuotes
499 * @returns {Object}
500 */
501 AbstractToolbarModifier.stringifyJSONintoOneLine = function( json, opts ) {
502 opts = opts || {};
503 var stringJSON = JSON.stringify( json, null, '' );
504
505 // IE8 make new line characters
506 stringJSON = stringJSON.replace( /\n/g, '' );
507
508 if ( opts.addSpaces ) {
509 stringJSON = stringJSON.replace( /(\{|:|,|\[|\])/g, function( sentence ) {
510 return sentence + ' ';
511 } );
512
513 stringJSON = stringJSON.replace( /(\])/g, function( sentence ) {
514 return ' ' + sentence;
515 } );
516 }
517
518 if ( opts.noQuotesOnKey ) {
519 stringJSON = stringJSON.replace( /"(\w*)":/g, function( sentence, word ) {
520 return word + ':';
521 } );
522 }
523
524 if ( opts.singleQuotes ) {
525 stringJSON = stringJSON.replace( /\"/g, '\'' );
526 }
527
528 return stringJSON;
529 };
530
531 /**
532 * Hide toolbar configurator
533 */
534 AbstractToolbarModifier.prototype.hideUI = function() {
535 this.hidden = true;
536 this.mainContainer.hide();
537 if ( this.editorInstance.container ) {
538 this.editorInstance.container.hide();
539 }
540 };
541
542 /**
543 * Show toolbar configurator
544 */
545 AbstractToolbarModifier.prototype.showUI = function() {
546 this.hidden = false;
547 this.mainContainer.show();
548 if ( this.editorInstance.container ) {
549 this.editorInstance.container.show();
550 }
551 };
552
553
554 /**
555 * Extends plugins setttings in the specified config with settings useful for
556 * the toolbar configurator.
557 *
558 * @static
559 */
560 AbstractToolbarModifier.extendPluginsConfig = function( config ) {
561 var extraPlugins = config.extraPlugins;
562
563 // Enable the special, lightweight area to replace wysiwygarea.
564 config.extraPlugins = ( extraPlugins ? extraPlugins + ',' : '' ) + 'toolbarconfiguratorarea';
565 };
566} )();
diff --git a/sources/samples/toolbarconfigurator/js/fulltoolbareditor.js b/sources/samples/toolbarconfigurator/js/fulltoolbareditor.js
new file mode 100644
index 0000000..2b9f15f
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/js/fulltoolbareditor.js
@@ -0,0 +1,365 @@
1/* exported ToolbarConfigurator */
2/* global ToolbarConfigurator */
3
4'use strict';
5
6window.ToolbarConfigurator = {};
7
8( function() {
9 /**
10 * @class ToolbarConfigurator.FullToolbarEditor
11 * @constructor
12 */
13 function FullToolbarEditor() {
14 this.instanceid = 'fte' + CKEDITOR.tools.getNextId();
15 this.textarea = new CKEDITOR.dom.element( 'textarea' );
16 this.textarea.setAttributes( {
17 id: this.instanceid,
18 name: this.instanceid,
19 contentEditable: true
20 } );
21
22 this.buttons = null;
23 this.editorInstance = null;
24 }
25
26 // Expose the class.
27 ToolbarConfigurator.FullToolbarEditor = FullToolbarEditor;
28
29 /**
30 * @param {Function} callback
31 * @param {Object} cfg
32 */
33 FullToolbarEditor.prototype.init = function( callback ) {
34 var that = this;
35
36 document.body.appendChild( this.textarea.$ );
37
38 CKEDITOR.replace( this.instanceid );
39
40 this.editorInstance = CKEDITOR.instances[ this.instanceid ];
41
42 this.editorInstance.once( 'configLoaded', function( e ) {
43 var cfg = e.editor.config;
44
45 // We want all the buttons.
46 delete cfg.removeButtons;
47 delete cfg.toolbarGroups;
48 delete cfg.toolbar;
49 ToolbarConfigurator.AbstractToolbarModifier.extendPluginsConfig( cfg );
50
51 e.editor.once( 'loaded', function() {
52 that.buttons = FullToolbarEditor.toolbarToButtons( that.editorInstance.toolbar );
53
54 that.buttonsByGroup = FullToolbarEditor.groupButtons( that.buttons );
55
56 that.buttonNamesByGroup = that.groupButtonNamesByGroup( that.buttons );
57
58 e.editor.container.hide();
59
60 if ( typeof callback === 'function' )
61 callback( that.buttons );
62 } );
63 } );
64 };
65
66 /**
67 * Group array of button names by their group parents.
68 *
69 * @param {Array} buttons
70 * @returns {Object}
71 */
72 FullToolbarEditor.prototype.groupButtonNamesByGroup = function( buttons ) {
73 var that = this,
74 groups = FullToolbarEditor.groupButtons( buttons );
75
76 for ( var groupName in groups ) {
77 var currGroup = groups[ groupName ];
78
79 groups[ groupName ] = FullToolbarEditor.map( currGroup, function( button ) {
80 return that.getCamelCasedButtonName( button.name );
81 } );
82 }
83
84 return groups;
85 };
86
87 /**
88 * Returns group literal.
89 *
90 * @param {String} name
91 * @returns {Object}
92 */
93 FullToolbarEditor.prototype.getGroupByName = function( name ) {
94 var groups = this.editorInstance.config.toolbarGroups || this.getFullToolbarGroupsConfig();
95
96 var max = groups.length;
97 for ( var i = 0; i < max; i += 1 ) {
98 if ( groups[ i ].name === name )
99 return groups[ i ];
100 }
101
102 return null;
103 };
104
105 /**
106 * @param {String} name
107 * @returns {String | null}
108 */
109 FullToolbarEditor.prototype.getCamelCasedButtonName = function( name ) {
110 var items = this.editorInstance.ui.items;
111
112 for ( var key in items ) {
113 if ( items[ key ].name == name )
114 return key;
115 }
116
117 return null;
118 };
119
120 /**
121 * Returns full toolbarGroups config value which is used when
122 * there is no toolbarGroups field in config.
123 *
124 * @param {Boolean} [pickSeparators=false]
125 * @returns {Array}
126 */
127 FullToolbarEditor.prototype.getFullToolbarGroupsConfig = function( pickSeparators ) {
128 pickSeparators = ( pickSeparators === true ? true : false );
129
130 var result = [],
131 toolbarGroups = this.editorInstance.toolbar;
132
133 var max = toolbarGroups.length;
134 for ( var i = 0; i < max; i += 1 ) {
135 var currentGroup = toolbarGroups[ i ],
136 copiedGroup = {};
137
138 if ( typeof currentGroup.name != 'string' ) {
139 // this is not a group
140 if ( pickSeparators ) {
141 result.push( '/' );
142 }
143 continue;
144 }
145
146 copiedGroup.name = currentGroup.name;
147 if ( currentGroup.groups )
148 copiedGroup.groups = Array.prototype.slice.call( currentGroup.groups );
149
150 result.push( copiedGroup );
151 }
152
153 return result;
154 };
155
156 /**
157 * Filters array items based on checker provided in second argument.
158 * Returns new array.
159 *
160 * @param {Array} arr
161 * @param {Function} checker
162 * @returns {Array}
163 */
164 FullToolbarEditor.filter = function( arr, checker ) {
165 var max = ( arr && arr.length ? arr.length : 0 ),
166 result = [];
167
168 for ( var i = 0; i < max; i += 1 ) {
169 if ( checker( arr[ i ] ) )
170 result.push( arr[ i ] );
171 }
172
173 return result;
174 };
175
176 /**
177 * Simplified http://underscorejs.org/#map functionality
178 *
179 * @param {Array | Object} enumerable
180 * @param {Function} modifier
181 * @returns {Array | Object}
182 */
183 FullToolbarEditor.map = function( enumerable, modifier ) {
184 var result;
185
186 if ( CKEDITOR.tools.isArray( enumerable ) ) {
187 result = [];
188
189 var max = enumerable.length;
190 for ( var i = 0; i < max; i += 1 )
191 result.push( modifier( enumerable[ i ] ) );
192 } else {
193 result = {};
194
195 for ( var key in enumerable )
196 result[ key ] = modifier( enumerable[ key ] );
197 }
198
199 return result;
200 };
201
202 /**
203 * Group buttons by their parent names.
204 *
205 * @static
206 * @param {Array} buttons
207 * @returns {Object} The object (`name => group`) representing CKEDITOR.ui.button or CKEDITOR.ui.richCombo
208 */
209 FullToolbarEditor.groupButtons = function( buttons ) {
210 var groups = {};
211
212 var max = buttons.length;
213 for ( var i = 0; i < max; i += 1 ) {
214 var currBtn = buttons[ i ],
215 currBtnGroupName = currBtn.toolbar.split( ',' )[ 0 ];
216
217 groups[ currBtnGroupName ] = groups[ currBtnGroupName ] || [];
218
219 groups[ currBtnGroupName ].push( currBtn );
220 }
221
222 return groups;
223 };
224
225 /**
226 * Pick all buttons from toolbar.
227 *
228 * @static
229 * @param {Array} groups
230 * @returns {Array}
231 */
232 FullToolbarEditor.toolbarToButtons = function( groups ) {
233 var buttons = [];
234
235 var max = groups.length;
236 for ( var i = 0; i < max; i += 1 ) {
237 var currentGroup = groups[ i ];
238
239 if ( typeof currentGroup == 'object' )
240 buttons = buttons.concat( FullToolbarEditor.groupToButtons( groups[ i ] ) );
241 }
242
243 return buttons;
244 };
245
246 /**
247 * Creates HTML button representation for view.
248 *
249 * @static
250 * @param {CKEDITOR.ui.button | CKEDITOR.ui.richCombo} button
251 * @returns {CKEDITOR.dom.element}
252 */
253 FullToolbarEditor.createToolbarButton = function( button ) {
254 var $button = new CKEDITOR.dom.element( 'a' ),
255 icon = FullToolbarEditor.createIcon( button.name, button.icon, button.command );
256
257 $button.setStyle( 'float', 'none' );
258
259 $button.addClass( 'cke_' + ( CKEDITOR.lang.dir == 'rtl' ? 'rtl' : 'ltr' ) );
260
261 if ( button instanceof CKEDITOR.ui.button ) {
262 $button.addClass( 'cke_button' );
263 $button.addClass( 'cke_toolgroup' );
264
265 $button.append( icon );
266 } else if ( CKEDITOR.ui.richCombo && button instanceof CKEDITOR.ui.richCombo ) {
267 var comboLabel = new CKEDITOR.dom.element( 'span' ),
268 comboOpen = new CKEDITOR.dom.element( 'span' ),
269 comboArrow = new CKEDITOR.dom.element( 'span' );
270
271 $button.addClass( 'cke_combo_button' );
272
273 comboLabel.addClass( 'cke_combo_text' );
274 comboLabel.addClass( 'cke_combo_inlinelabel' );
275 comboLabel.setText( button.label );
276
277 comboOpen.addClass( 'cke_combo_open' );
278 comboArrow.addClass( 'cke_combo_arrow' );
279 comboOpen.append( comboArrow );
280
281 $button.append( comboLabel );
282 $button.append( comboOpen );
283 }
284
285 return $button;
286 };
287
288 /**
289 * Create and return icon element.
290 *
291 * @param {String} name
292 * @param {String} icon
293 * @param {String} command
294 * @static
295 * @returns {CKEDITOR.dom.element}
296 */
297 FullToolbarEditor.createIcon = function( name, icon, command ) {
298 var iconStyle = CKEDITOR.skin.getIconStyle( name, ( CKEDITOR.lang.dir == 'rtl' ) );
299
300 // We don't know exactly how to get icon style. Especially for extra plugins,
301 // Which definition may vary.
302 iconStyle = iconStyle || CKEDITOR.skin.getIconStyle( icon, ( CKEDITOR.lang.dir == 'rtl' ) );
303 iconStyle = iconStyle || CKEDITOR.skin.getIconStyle( command, ( CKEDITOR.lang.dir == 'rtl' ) );
304
305 var iconElement = new CKEDITOR.dom.element( 'span' );
306
307 iconElement.addClass( 'cke_button_icon' );
308 iconElement.addClass( 'cke_button__' + name + '_icon' );
309 iconElement.setAttribute( 'style', iconStyle );
310 iconElement.setStyle( 'float', 'none' );
311
312 return iconElement;
313 };
314
315 /**
316 * Create and return button element
317 *
318 * @param {String} text
319 * @param {String} cssClasses
320 * @returns {CKEDITOR.dom.element}
321 */
322 FullToolbarEditor.createButton = function( text, cssClasses ) {
323 var $button = new CKEDITOR.dom.element( 'button' );
324
325 $button.addClass( 'button-a' );
326
327 $button.setAttribute( 'type', 'button' );
328
329 if ( typeof cssClasses == 'string' ) {
330 cssClasses = cssClasses.split( ' ' );
331
332 var i = cssClasses.length;
333 while ( i-- ) {
334 $button.addClass( cssClasses[ i ] );
335 }
336 }
337
338 $button.setHtml( text );
339
340 return $button;
341 };
342
343 /**
344 * @static
345 * @param {Object} group
346 * @returns {Array} representing HTML buttons for view
347 */
348 FullToolbarEditor.groupToButtons = function( group ) {
349 var buttons = [],
350 items = group.items;
351
352 var max = items ? items.length : 0;
353 for ( var i = 0; i < max; i += 1 ) {
354 var item = items[ i ];
355
356 if ( item instanceof CKEDITOR.ui.button || CKEDITOR.ui.richCombo && item instanceof CKEDITOR.ui.richCombo ) {
357 item.$ = FullToolbarEditor.createToolbarButton( item );
358 buttons.push( item );
359 }
360 }
361
362 return buttons;
363 };
364
365} )();
diff --git a/sources/samples/toolbarconfigurator/js/toolbarmodifier.js b/sources/samples/toolbarconfigurator/js/toolbarmodifier.js
new file mode 100644
index 0000000..bd33d24
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/js/toolbarmodifier.js
@@ -0,0 +1,1366 @@
1/* global ToolbarConfigurator, alert */
2
3'use strict';
4
5( function() {
6 var AbstractToolbarModifier = ToolbarConfigurator.AbstractToolbarModifier;
7
8 /**
9 * @class ToolbarConfigurator.ToolbarModifier
10 * @param {String} editorId An id of modified editor
11 * @param {Object} cfg
12 * @extends AbstractToolbarModifier
13 * @constructor
14 */
15 function ToolbarModifier( editorId, cfg ) {
16 AbstractToolbarModifier.call( this, editorId, cfg );
17
18 this.removedButtons = null;
19 this.originalConfig = null;
20 this.actualConfig = null;
21 this.emptyVisible = false;
22
23 // edit, paste, config
24 this.state = 'edit';
25
26 this.toolbarButtons = [
27 {
28 text: {
29 active: 'Hide empty toolbar groups',
30 inactive: 'Show empty toolbar groups'
31 },
32 group: 'edit',
33 position: 'left',
34 cssClass: 'button-a-soft',
35 clickCallback: function( button, buttonDefinition ) {
36 var className = 'button-a-background';
37
38 button[ button.hasClass( className ) ? 'removeClass' : 'addClass' ]( className );
39
40 this._toggleVisibilityEmptyElements();
41
42 if ( this.emptyVisible ) {
43 button.setText( buttonDefinition.text.active );
44 } else {
45 button.setText( buttonDefinition.text.inactive );
46 }
47 }
48 },
49 {
50 text: 'Add row separator',
51 group: 'edit',
52 position: 'left',
53 cssClass: 'button-a-soft',
54 clickCallback: function() {
55 this._addSeparator();
56 }
57 },
58 /*{
59 text: 'Paste config',
60 group: 'edit',
61 position: 'left',
62 clickCallback: function() {
63 this.state = 'paste';
64
65 this.modifyContainer.addClass( 'hidden' );
66 this.configContainer.removeClass( 'hidden' );
67 this.configContainer.setHtml( '<textarea></textarea>' );
68 this.showToolbarBtnsByGroupName( 'config' );
69 }
70 },*/
71 {
72 text: 'Select config',
73 group: 'config',
74 position: 'left',
75 cssClass: 'button-a-soft',
76 clickCallback: function() {
77 this.configContainer.findOne( 'textarea' ).$.select();
78 }
79 },
80 {
81 text: 'Back to configurator',
82 group: 'config',
83 position: 'right',
84 cssClass: 'button-a-background',
85 clickCallback: function() {
86 if ( this.state === 'paste' ) {
87 var cfg = this.configContainer.findOne( 'textarea' ).getValue();
88 cfg = ToolbarModifier.evaluateToolbarGroupsConfig( cfg );
89
90 if ( cfg ) {
91 this.setConfig( cfg );
92 } else {
93 alert( 'Your pasted config is wrong.' );
94 }
95 }
96
97 this.state = 'edit';
98 this._showConfigurationTool();
99 this.showToolbarBtnsByGroupName( this.state );
100 }
101 },
102 {
103 text: 'Get toolbar <span class="highlight">config</span>',
104 group: 'edit',
105 position: 'right',
106 cssClass: 'button-a-background icon-pos-left icon-download',
107 clickCallback: function() {
108 this.state = 'config';
109 this._showConfig();
110 this.showToolbarBtnsByGroupName( this.state );
111 }
112 }
113 ];
114
115 this.cachedActiveElement = null;
116 }
117
118 // Expose the class.
119 ToolbarConfigurator.ToolbarModifier = ToolbarModifier;
120
121 ToolbarModifier.prototype = Object.create( ToolbarConfigurator.AbstractToolbarModifier.prototype );
122
123 /**
124 * @returns {Object}
125 */
126 ToolbarModifier.prototype.getActualConfig = function() {
127 var copy = AbstractToolbarModifier.prototype.getActualConfig.call( this );
128
129 if ( copy.toolbarGroups ) {
130
131 var max = copy.toolbarGroups.length;
132 for ( var i = 0; i < max; i += 1 ) {
133 var currentGroup = copy.toolbarGroups[ i ];
134
135 copy.toolbarGroups[ i ] = ToolbarModifier.parseGroupToConfigValue( currentGroup );
136 }
137
138 }
139
140 return copy;
141 };
142
143 /**
144 * @param {Function} callback
145 * @param {String} [config]
146 * @param {Boolean} [forceKeepRemoveButtons=false]
147 * @private
148 */
149 ToolbarModifier.prototype._onInit = function( callback, config, forceKeepRemoveButtons ) {
150 forceKeepRemoveButtons = ( forceKeepRemoveButtons === true );
151 AbstractToolbarModifier.prototype._onInit.call( this, undefined, config );
152
153 this.removedButtons = [];
154
155 if ( forceKeepRemoveButtons ) {
156 if ( this.actualConfig.removeButtons ) {
157 this.removedButtons = this.actualConfig.removeButtons.split( ',' );
158 } else {
159 this.removedButtons = [];
160 }
161 } else {
162 if ( !( 'removeButtons' in this.originalConfig ) ) {
163 this.originalConfig.removeButtons = '';
164 this.removedButtons = [];
165 } else {
166 this.removedButtons = this.originalConfig.removeButtons ? this.originalConfig.removeButtons.split( ',' ) : [];
167 }
168 }
169
170 if ( !this.actualConfig.toolbarGroups )
171 this.actualConfig.toolbarGroups = this.fullToolbarEditor.getFullToolbarGroupsConfig();
172
173 this._fixGroups( this.actualConfig );
174 this._calculateTotalBtns();
175
176 this._createModifier();
177 this._refreshMoveBtnsAvalibility();
178 this._refreshBtnTabIndexes();
179
180 if ( typeof callback === 'function' )
181 callback( this.mainContainer );
182 };
183
184 /**
185 * @private
186 */
187 ToolbarModifier.prototype._showConfigurationTool = function() {
188 this.configContainer.addClass( 'hidden' );
189 this.modifyContainer.removeClass( 'hidden' );
190 };
191
192 /**
193 * Show configuration file in tool
194 *
195 * @private
196 */
197 ToolbarModifier.prototype._showConfig = function() {
198 var that = this,
199 actualConfig = this.getActualConfig(),
200 cfg = {};
201 if ( actualConfig.toolbarGroups ) {
202 cfg.toolbarGroups = actualConfig.toolbarGroups;
203
204 var groups = prepareGroups( actualConfig.toolbarGroups, this.cfg.trimEmptyGroups );
205
206 cfg.toolbarGroups = '\n\t\t' + groups.join( ',\n\t\t' );
207 }
208
209 function prepareGroups( toolbarGroups, trimEmptyGroups ) {
210 var groups = [],
211 max = toolbarGroups.length;
212
213 for ( var i = 0; i < max; i++ ) {
214 var group = toolbarGroups[ i ];
215
216 if ( group === '/' ) {
217 groups.push( '\'/\'' );
218 continue;
219 }
220
221 if ( trimEmptyGroups ) {
222 var max2 = group.groups.length;
223 while ( max2-- ) {
224 var subgroup = group.groups[ max2 ];
225
226 if ( ToolbarModifier.getTotalSubGroupButtonsNumber( subgroup, that.fullToolbarEditor ) === 0 ) {
227 group.groups.splice( max2, 1 );
228 }
229 }
230 }
231
232 if ( !( trimEmptyGroups && group.groups.length === 0 ) ) {
233 groups.push( AbstractToolbarModifier.stringifyJSONintoOneLine( group, {
234 addSpaces: true,
235 noQuotesOnKey: true,
236 singleQuotes: true
237 } ) );
238 }
239 }
240
241 return groups;
242 }
243
244 if ( actualConfig.removeButtons ) {
245 cfg.removeButtons = actualConfig.removeButtons;
246 }
247
248 var content = [
249 '<textarea class="configCode" readonly>',
250 'CKEDITOR.editorConfig = function( config ) {\n',
251 ( cfg.toolbarGroups ? '\tconfig.toolbarGroups = [' + cfg.toolbarGroups + '\n\t];' : '' ),
252 ( cfg.removeButtons ? '\n\n' : '' ),
253 ( cfg.removeButtons ? '\tconfig.removeButtons = \'' + cfg.removeButtons + '\';' : '' ),
254 '\n};',
255 '</textarea>'
256 ].join( '' );
257
258
259
260 this.modifyContainer.addClass( 'hidden' );
261 this.configContainer.removeClass( 'hidden' );
262
263 this.configContainer.setHtml( content );
264 };
265
266 /**
267 * Toggle empty groups and subgroups visibility.
268 *
269 * @private
270 */
271 ToolbarModifier.prototype._toggleVisibilityEmptyElements = function() {
272 if ( this.modifyContainer.hasClass( 'empty-visible' ) ) {
273 this.modifyContainer.removeClass( 'empty-visible' );
274 this.emptyVisible = false;
275 } else {
276 this.modifyContainer.addClass( 'empty-visible' );
277 this.emptyVisible = true;
278 }
279
280 this._refreshMoveBtnsAvalibility();
281 };
282
283 /**
284 * Creates HTML main container of modifier.
285 *
286 * @returns {CKEDITOR.dom.element}
287 * @private
288 */
289 ToolbarModifier.prototype._createModifier = function() {
290 var that = this;
291
292 AbstractToolbarModifier.prototype._createModifier.call( this );
293
294 this.modifyContainer.setHtml( this._toolbarConfigToListString() );
295
296 var groupLi = this.modifyContainer.find( 'li[data-type="group"]' );
297
298 this.modifyContainer.on( 'mouseleave', function() {
299 this._dehighlightActiveToolGroup();
300 }, this );
301
302 var max = groupLi.count();
303 for ( var i = 0; i < max; i += 1 ) {
304 groupLi.getItem( i ).on( 'mouseenter', onGroupHover );
305 }
306
307 function onGroupHover() {
308 that._highlightGroup( this.data( 'name' ) );
309 }
310
311 CKEDITOR.document.on( 'keypress', function( e ) {
312 var nativeEvent = e.data.$,
313 keyCode = nativeEvent.keyCode,
314 spaceOrEnter = ( keyCode === 32 || keyCode === 13 ),
315 active = new CKEDITOR.dom.element( CKEDITOR.document.$.activeElement );
316
317 var mainContainer = active.getAscendant( function( node ) {
318 return node.$ === that.mainContainer.$;
319 } );
320
321 if ( !mainContainer || !spaceOrEnter ) {
322 return;
323 }
324
325 if ( active.data( 'type' ) === 'button' ) {
326 active.findOne( 'input' ).$.click();
327 }
328 } );
329
330 this.modifyContainer.on( 'click', function( e ) {
331 var origEvent = e.data.$,
332 target = new CKEDITOR.dom.element( ( origEvent.target || origEvent.srcElement ) ),
333 relativeGroupOrSeparatorLi = ToolbarModifier.getGroupOrSeparatorLiAncestor( target );
334
335 if ( !relativeGroupOrSeparatorLi ) {
336 return;
337 }
338
339 that.cachedActiveElement = document.activeElement;
340
341 // checkbox clicked
342 if ( target.$ instanceof HTMLInputElement )
343 that._handleCheckboxClicked( target );
344
345 // link clicked
346 else if ( target.$ instanceof HTMLButtonElement ) {
347 if ( origEvent.preventDefault )
348 origEvent.preventDefault();
349 else
350 origEvent.returnValue = false;
351
352 var result = that._handleAnchorClicked( target.$ );
353
354 if ( result && result.action == 'remove' )
355 return;
356
357 }
358
359 var elementType = relativeGroupOrSeparatorLi.data( 'type' ),
360 elementName = relativeGroupOrSeparatorLi.data( 'name' );
361
362 that._setActiveElement( elementType, elementName );
363
364 if ( that.cachedActiveElement )
365 that.cachedActiveElement.focus();
366 } );
367
368 if ( !this.toolbarContainer ) {
369 this._createToolbar();
370 this.toolbarContainer.insertBefore( this.mainContainer.getChildren().getItem( 0 ) );
371 }
372
373 this.showToolbarBtnsByGroupName( 'edit' );
374
375 if ( !this.configContainer ) {
376 this.configContainer = new CKEDITOR.dom.element( 'div' );
377 this.configContainer.addClass( 'configContainer' );
378 this.configContainer.addClass( 'hidden' );
379
380 this.mainContainer.append( this.configContainer );
381 }
382
383 return this.mainContainer;
384 };
385
386 /**
387 * Show toolbar buttons related to group name provided in argument
388 * and hide other buttons
389 * Please note: this method works on toolbar in tool, which is located
390 * on top of the tool
391 *
392 * @param {String} groupName
393 */
394 ToolbarModifier.prototype.showToolbarBtnsByGroupName = function( groupName ) {
395 if ( !this.toolbarContainer ) {
396 return;
397 }
398
399 var allButtons = this.toolbarContainer.find( 'button' );
400
401 var max = allButtons.count();
402 for ( var i = 0; i < max; i += 1 ) {
403 var currentBtn = allButtons.getItem( i );
404
405 if ( currentBtn.data( 'group' ) == groupName )
406 currentBtn.removeClass( 'hidden' );
407 else
408 currentBtn.addClass( 'hidden' );
409
410 }
411 };
412
413 /**
414 * Parse group "model" to configuration value
415 *
416 * @param {Object} group
417 * @returns {Object}
418 * @private
419 */
420 ToolbarModifier.parseGroupToConfigValue = function( group ) {
421 if ( group.type == 'separator' ) {
422 return '/';
423 }
424
425 var groups = group.groups,
426 max = groups.length;
427
428 delete group.totalBtns;
429 for ( var i = 0; i < max; i += 1 ) {
430 groups[ i ] = groups[ i ].name;
431 }
432
433 return group;
434 };
435
436 /**
437 * Find closest Li ancestor in DOM tree which is group or separator element
438 *
439 * @param {CKEDITOR.dom.element} element
440 * @returns {CKEDITOR.dom.element}
441 */
442 ToolbarModifier.getGroupOrSeparatorLiAncestor = function( element ) {
443 if ( element.$ instanceof HTMLLIElement && element.data( 'type' ) == 'group' )
444 return element;
445 else {
446 return ToolbarModifier.getFirstAncestor( element, function( ancestor ) {
447 var type = ancestor.data( 'type' );
448
449 return ( type == 'group' || type == 'separator' );
450 } );
451 }
452 };
453
454 /**
455 * Set active element in tool by provided type and name.
456 *
457 * @param {String} type
458 * @param {String} name
459 */
460 ToolbarModifier.prototype._setActiveElement = function( type, name ) {
461 // clear current active element
462 if ( this.currentActive )
463 this.currentActive.elem.removeClass( 'active' );
464
465 if ( type === null ) {
466 this._dehighlightActiveToolGroup();
467 this.currentActive = null;
468 return;
469 }
470
471 var liElem = this.mainContainer.findOne( 'ul[data-type=table-body] li[data-type="' + type + '"][data-name="' + name + '"]' );
472
473 liElem.addClass( 'active' );
474
475 // setup model
476 this.currentActive = {
477 type: type,
478 name: name,
479 elem: liElem
480 };
481
482 // highlight group in toolbar
483 if ( type == 'group' )
484 this._highlightGroup( name );
485
486 if ( type == 'separator' )
487 this._dehighlightActiveToolGroup();
488 };
489
490 /**
491 * @returns {CKEDITOR.dom.element|null}
492 */
493 ToolbarModifier.prototype.getActiveToolGroup = function() {
494 if ( this.editorInstance.container )
495 return this.editorInstance.container.findOne( '.cke_toolgroup.active, .cke_toolbar.active' );
496 else
497 return null;
498 };
499
500 /**
501 * @private
502 */
503 ToolbarModifier.prototype._dehighlightActiveToolGroup = function() {
504 var currentActive = this.getActiveToolGroup();
505
506 if ( currentActive )
507 currentActive.removeClass( 'active' );
508
509 // @see ToolbarModifier.prototype._highlightGroup.
510 if ( this.editorInstance.container ) {
511 this.editorInstance.container.removeClass( 'some-toolbar-active' );
512 }
513 };
514
515 /**
516 * Highlight group by its name, and dehighlight current group.
517 *
518 * @param {String} name
519 */
520 ToolbarModifier.prototype._highlightGroup = function( name ) {
521 if ( !this.editorInstance.container )
522 return;
523
524 var foundBtnName = this.getFirstEnabledButtonInGroup( name ),
525 foundBtn = this.editorInstance.container.findOne( '.cke_button__' + foundBtnName + ', .cke_combo__' + foundBtnName );
526
527 this._dehighlightActiveToolGroup();
528
529 // Helpful to dim other toolbar groups if one is highlighted.
530 if ( this.editorInstance.container ) {
531 this.editorInstance.container.addClass( 'some-toolbar-active' );
532 }
533
534 if ( foundBtn ) {
535 var btnToolbar = ToolbarModifier.getFirstAncestor( foundBtn, function( ancestor ) {
536 return ancestor.hasClass( 'cke_toolbar' );
537 } );
538
539 if ( btnToolbar )
540 btnToolbar.addClass( 'active' );
541 }
542 };
543
544 /**
545 * @param {String} groupName
546 * @return {String|null}
547 */
548 ToolbarModifier.prototype.getFirstEnabledButtonInGroup = function( groupName ) {
549 var groups = this.actualConfig.toolbarGroups,
550 groupIndex = this.getGroupIndex( groupName ),
551 group = groups[ groupIndex ];
552
553 if ( groupIndex === -1 ) {
554 return null;
555 }
556
557 var max = group.groups ? group.groups.length : 0;
558 for ( var i = 0; i < max; i += 1 ) {
559 var currSubgroupName = group.groups[ i ].name,
560 firstEnabled = this.getFirstEnabledButtonInSubgroup( currSubgroupName );
561
562 if ( firstEnabled )
563 return firstEnabled;
564 }
565 return null;
566 };
567
568 /**
569 * @param {String} subgroupName
570 * @returns {String|null}
571 */
572 ToolbarModifier.prototype.getFirstEnabledButtonInSubgroup = function( subgroupName ) {
573 var subgroupBtns = this.fullToolbarEditor.buttonsByGroup[ subgroupName ];
574
575 var max = subgroupBtns ? subgroupBtns.length : 0;
576 for ( var i = 0; i < max; i += 1 ) {
577 var currBtnName = subgroupBtns[ i ].name;
578 if ( !this.isButtonRemoved( currBtnName ) )
579 return currBtnName;
580 }
581
582 return null;
583 };
584
585 /**
586 * Sets up parameters and call adequate action.
587 *
588 * @param {CKEDITOR.dom.element} checkbox
589 * @private
590 */
591 ToolbarModifier.prototype._handleCheckboxClicked = function( checkbox ) {
592 var closestLi = checkbox.getAscendant( 'li' ),
593 elementName = closestLi.data( 'name' ),
594 aboutToAddToRemoved = !checkbox.$.checked;
595
596 if ( aboutToAddToRemoved )
597 this._addButtonToRemoved( elementName );
598 else
599 this._removeButtonFromRemoved( elementName );
600 };
601
602 /**
603 * Sets up parameters and call adequate action.
604 *
605 * @param {HTMLAnchorElement} anchor
606 * @private
607 */
608 ToolbarModifier.prototype._handleAnchorClicked = function( anchor ) {
609 var anchorDOM = new CKEDITOR.dom.element( anchor ),
610 relativeLi = anchorDOM.getAscendant( 'li' ),
611 relativeUl = relativeLi.getAscendant( 'ul' ),
612 elementType = relativeLi.data( 'type' ),
613 elementName = relativeLi.data( 'name' ),
614 direction = anchorDOM.data( 'direction' ),
615 nearestLi = ( direction === 'up' ? relativeLi.getPrevious() : relativeLi.getNext() ),
616 groupName,
617 subgroupName,
618 newIndex;
619
620 // nothing to do
621 if ( anchorDOM.hasClass( 'disabled' ) )
622 return null;
623
624 // remove separator and nothing else
625 if ( anchorDOM.hasClass( 'remove' ) ) {
626 relativeLi.remove();
627 this._removeSeparator( relativeLi.data( 'name' ) );
628 this._setActiveElement( null );
629 return { action: 'remove' };
630 }
631
632 if ( !anchorDOM.hasClass( 'move' ) || !nearestLi )
633 return { action: null };
634
635 // move group or separator
636 if ( elementType === 'group' || elementType === 'separator' ) {
637 groupName = elementName;
638 newIndex = this._moveGroup( direction, groupName );
639 }
640
641 // move subgroup
642 if ( elementType === 'subgroup' ) {
643 subgroupName = elementName;
644 groupName = relativeLi.getAscendant( 'li' ).data( 'name' );
645 newIndex = this._moveSubgroup( direction, groupName, subgroupName );
646 }
647
648 // Visual effect
649 if ( direction === 'up' )
650 relativeLi.insertBefore( relativeUl.getChild( newIndex ) );
651
652 if ( direction === 'down' )
653 relativeLi.insertAfter( relativeUl.getChild( newIndex ) );
654
655 // Should know whether there is next li element after modifications.
656 var nextLi = relativeLi;
657
658 // We are looking for next li element in list (to check whether current one is the last one)
659 var found;
660 while ( nextLi = ( direction === 'up' ? nextLi.getPrevious() : nextLi.getNext() ) ) {
661 if ( !this.emptyVisible && nextLi.hasClass( 'empty' ) ) {
662 continue;
663 }
664
665 found = nextLi;
666 break;
667 }
668
669 // If not found, it means that we reached end.
670 if ( !found ) {
671 var selector = ( '[data-direction="' + ( direction === 'up' ? 'down' : 'up' ) + '"]' );
672
673 // Shifting direction.
674 this.cachedActiveElement = anchorDOM.getParent().findOne( selector );
675 }
676
677 this._refreshMoveBtnsAvalibility();
678 this._refreshBtnTabIndexes();
679
680 return {
681 action: 'move'
682 };
683 };
684
685 /**
686 * First element can not be moved up, and last element can not be moved down,
687 * so they are disabled.
688 */
689 ToolbarModifier.prototype._refreshMoveBtnsAvalibility = function() {
690 var that = this,
691 disabledBtns = this.mainContainer.find( 'ul[data-type=table-body] li > p > span > button.move.disabled' );
692
693 // enabling all disabled buttons
694 var max = disabledBtns.count();
695 for ( var i = 0; i < max; i += 1 ) {
696 var currentBtn = disabledBtns.getItem( i );
697 currentBtn.removeClass( 'disabled' );
698 }
699
700
701 function disableElementsInLists( ulList ) {
702 var max = ulList.count();
703 for ( i = 0; i < max; i += 1 ) {
704 that._disableElementsInList( ulList.getItem( i ) );
705 }
706 }
707
708 // Disable buttons in toolbars.
709 disableElementsInLists( this.mainContainer.find( 'ul[data-type=table-body]' ) );
710
711 // Disable buttons in toolbar groups.
712 disableElementsInLists( this.mainContainer.find( 'ul[data-type=table-body] > li > ul' ) );
713 };
714
715 /**
716 * @private
717 */
718 ToolbarModifier.prototype._refreshBtnTabIndexes = function() {
719 var tabindexed = this.mainContainer.find( '[data-tab="true"]' );
720
721 var max = tabindexed.count();
722 for ( var i = 0; i < max; i++ ) {
723 var item = tabindexed.getItem( i ),
724 disabled = item.hasClass( 'disabled' );
725
726 item.setAttribute( 'tabindex', disabled ? -1 : i );
727 }
728 };
729
730 /**
731 * Disable buttons to move elements up and down which should be disabled.
732 *
733 * @param {CKEDITOR.dom.element} ul
734 * @private
735 */
736 ToolbarModifier.prototype._disableElementsInList = function( ul ) {
737 var liList = ul.getChildren();
738
739 if ( !liList.count() )
740 return;
741
742 var firstDisabled, lastDisabled;
743 if ( this.emptyVisible ) {
744 firstDisabled = ul.getFirst();
745 lastDisabled = ul.getLast();
746 } else {
747 firstDisabled = ul.getFirst( isNotEmptyChecker );
748 lastDisabled = ul.getLast( isNotEmptyChecker );
749 }
750
751 function isNotEmptyChecker( element ) {
752 return !element.hasClass( 'empty' );
753 }
754
755 if ( firstDisabled )
756 var firstDisabledBtn = firstDisabled.findOne( 'p button[data-direction="up"]' );
757
758 if ( lastDisabled )
759 var lastDisabledBtn = lastDisabled.findOne( 'p button[data-direction="down"]' );
760
761 if ( firstDisabledBtn ) {
762 firstDisabledBtn.addClass( 'disabled' );
763 firstDisabledBtn.setAttribute( 'tabindex', '-1' );
764 }
765
766 if ( lastDisabledBtn ) {
767 lastDisabledBtn.addClass( 'disabled' );
768 lastDisabledBtn.setAttribute( 'tabindex', '-1' );
769 }
770 };
771
772 /**
773 * Gets group index in actual config toolbarGroups
774 *
775 * @param {String} name
776 * @returns {Number}
777 */
778 ToolbarModifier.prototype.getGroupIndex = function( name ) {
779 var groups = this.actualConfig.toolbarGroups;
780
781 var max = groups.length;
782 for ( var i = 0; i < max; i += 1 ) {
783 if ( groups[ i ].name === name )
784 return i;
785 }
786
787 return -1;
788 };
789
790 /**
791 * Handle adding separator.
792 *
793 * @private
794 */
795 ToolbarModifier.prototype._addSeparator = function() {
796 var separatorIndex = this._determineSeparatorToAddIndex(),
797 separator = ToolbarModifier.createSeparatorLiteral(),
798 domSeparator = CKEDITOR.dom.element.createFromHtml( ToolbarModifier.getToolbarSeparatorString( separator ) );
799
800 this.actualConfig.toolbarGroups.splice( separatorIndex, 0, separator );
801
802 domSeparator.insertBefore( this.modifyContainer.findOne( 'ul[data-type=table-body]' ).getChild( separatorIndex ) );
803
804 this._setActiveElement( 'separator', separator.name );
805 this._refreshMoveBtnsAvalibility();
806 this._refreshBtnTabIndexes();
807 this._refreshEditor();
808 };
809
810 /**
811 * Handle removing separator.
812 *
813 * @param {String} name
814 */
815 ToolbarModifier.prototype._removeSeparator = function( name ) {
816 var separatorIndex = CKEDITOR.tools.indexOf( this.actualConfig.toolbarGroups, function( group ) {
817 return group.type == 'separator' && group.name == name;
818 } );
819
820 this.actualConfig.toolbarGroups.splice( separatorIndex, 1 );
821
822 this._refreshMoveBtnsAvalibility();
823 this._refreshBtnTabIndexes();
824 this._refreshEditor();
825 };
826
827 /**
828 * Determine index where separator should be added, based on currently selected element.
829 *
830 * @returns {Number}
831 * @private
832 */
833 ToolbarModifier.prototype._determineSeparatorToAddIndex = function() {
834 if ( !this.currentActive )
835 return 0;
836
837 var groupLi;
838 if ( this.currentActive.elem.data( 'type' ) == 'group' || this.currentActive.elem.data( 'type' ) == 'separator' )
839 groupLi = this.currentActive.elem;
840 else
841 groupLi = this.currentActive.elem.getAscendant( 'li' );
842
843 return groupLi.getIndex();
844 };
845
846 /**
847 * @param {Array} elementsArray
848 * @param {Number} elementIndex
849 * @param {String} direction
850 * @returns {Number}
851 * @private
852 */
853 ToolbarModifier.prototype._moveElement = function( elementsArray, elementIndex, direction ) {
854 var nextIndex;
855
856 if ( this.emptyVisible )
857 nextIndex = ( direction == 'down' ? elementIndex + 1 : elementIndex - 1 );
858 else {
859 // When empty elements are not visible, there is need to skip them.
860 nextIndex = ToolbarModifier.getFirstElementIndexWith( elementsArray, elementIndex, direction, isEmptyOrSeparatorChecker );
861 }
862
863 function isEmptyOrSeparatorChecker( element ) {
864 return element.totalBtns || element.type == 'separator';
865 }
866
867 var offset = nextIndex - elementIndex;
868
869 return ToolbarModifier.moveTo( offset, elementsArray, elementIndex );
870 };
871
872 /**
873 * Moves group located in config level up or down and refresh editor.
874 *
875 * @param {String} direction
876 * @param {String} groupName
877 * @returns {Number}
878 */
879 ToolbarModifier.prototype._moveGroup = function( direction, groupName ) {
880 var groupIndex = this.getGroupIndex( groupName ),
881 groups = this.actualConfig.toolbarGroups,
882 newIndex = this._moveElement( groups, groupIndex, direction );
883
884 this._refreshMoveBtnsAvalibility();
885 this._refreshBtnTabIndexes();
886 this._refreshEditor();
887
888 return newIndex;
889 };
890
891 /**
892 * Moves subgroup located in config level up or down and refresh editor.
893 *
894 * @param {String} direction
895 * @param {String} groupName
896 * @param {String} subgroupName
897 * @private
898 */
899 ToolbarModifier.prototype._moveSubgroup = function( direction, groupName, subgroupName ) {
900 var groupIndex = this.getGroupIndex( groupName ),
901 groups = this.actualConfig.toolbarGroups,
902 group = groups[ groupIndex ],
903 subgroupIndex = CKEDITOR.tools.indexOf( group.groups, function( subgroup ) {
904 return subgroup.name == subgroupName;
905 } ),
906 newIndex = this._moveElement( group.groups, subgroupIndex, direction );
907
908 this._refreshEditor();
909
910 return newIndex;
911 };
912
913 /**
914 * Set `totalBtns` property in `actualConfig.toolbarGroups` elements.
915 *
916 * @private
917 */
918 ToolbarModifier.prototype._calculateTotalBtns = function() {
919 var groups = this.actualConfig.toolbarGroups;
920
921 var i = groups.length;
922 // from the end
923 while ( i-- ) {
924 var currentGroup = groups[ i ],
925 totalBtns = ToolbarModifier.getTotalGroupButtonsNumber( currentGroup, this.fullToolbarEditor );
926
927 if ( currentGroup.type == 'separator' ) {
928 // nothing to do with separator
929 continue;
930 }
931
932 currentGroup.totalBtns = totalBtns;
933 }
934 };
935
936 /**
937 * Add button to removeButtons field in config and refresh editor.
938 *
939 * @param {String} buttonName
940 * @private
941 */
942 ToolbarModifier.prototype._addButtonToRemoved = function( buttonName ) {
943 if ( CKEDITOR.tools.indexOf( this.removedButtons, buttonName ) != -1 )
944 throw 'Button already added to removed';
945
946 this.removedButtons.push( buttonName );
947 this.actualConfig.removeButtons = this.removedButtons.join( ',' );
948 this._refreshEditor();
949 };
950
951 /**
952 * Remove button from removeButtons field in config and refresh editor.
953 *
954 * @param {String} buttonName
955 * @private
956 */
957 ToolbarModifier.prototype._removeButtonFromRemoved = function( buttonName ) {
958 var foundAtIndex = CKEDITOR.tools.indexOf( this.removedButtons, buttonName );
959
960 if ( foundAtIndex === -1 )
961 throw 'Trying to remove button from removed, but not found';
962
963 this.removedButtons.splice( foundAtIndex, 1 );
964 this.actualConfig.removeButtons = this.removedButtons.join( ',' );
965 this._refreshEditor();
966 };
967
968 /**
969 * Parse group "model" to configuration value
970 *
971 * @param {Object} group
972 * @returns {Object}
973 * @static
974 */
975 ToolbarModifier.parseGroupToConfigValue = function( group ) {
976 if ( group.type == 'separator' ) {
977 return '/';
978 }
979
980 var groups = group.groups,
981 max = groups.length;
982
983 delete group.totalBtns;
984 for ( var i = 0; i < max; i += 1 ) {
985 groups[ i ] = groups[ i ].name;
986 }
987
988 return group;
989 };
990
991 /**
992 * Find closest Li ancestor in DOM tree which is group or separator element
993 *
994 * @param {CKEDITOR.dom.element} element
995 * @returns {CKEDITOR.dom.element}
996 * @static
997 */
998 ToolbarModifier.getGroupOrSeparatorLiAncestor = function( element ) {
999 if ( element.$ instanceof HTMLLIElement && element.data( 'type' ) == 'group' )
1000 return element;
1001 else {
1002 return ToolbarModifier.getFirstAncestor( element, function( ancestor ) {
1003 var type = ancestor.data( 'type' );
1004
1005 return ( type == 'group' || type == 'separator' );
1006 } );
1007 }
1008 };
1009
1010 /**
1011 * Create separator literal with unique id.
1012 *
1013 * @public
1014 * @static
1015 * @return {Object}
1016 */
1017 ToolbarModifier.createSeparatorLiteral = function() {
1018 return {
1019 type: 'separator',
1020 name: ( 'separator' + CKEDITOR.tools.getNextNumber() )
1021 };
1022 };
1023
1024 /**
1025 * Creates HTML unordered list string based on toolbarGroups field in config.
1026 *
1027 * @returns {String}
1028 * @static
1029 */
1030 ToolbarModifier.prototype._toolbarConfigToListString = function() {
1031 var groups = this.actualConfig.toolbarGroups || [],
1032 listString = '<ul data-type="table-body">';
1033
1034 var max = groups.length;
1035 for ( var i = 0; i < max; i += 1 ) {
1036 var currentGroup = groups[ i ];
1037
1038 if ( currentGroup.type === 'separator' )
1039 listString += ToolbarModifier.getToolbarSeparatorString( currentGroup );
1040 else
1041 listString += this._getToolbarGroupString( currentGroup );
1042 }
1043
1044 listString += '</ul>';
1045
1046 var headerString = ToolbarModifier.getToolbarHeaderString();
1047
1048 return headerString + listString;
1049 };
1050
1051 /**
1052 * Created HTML group list element based on group field in config.
1053 *
1054 * @param {Object} group
1055 * @returns {String}
1056 * @private
1057 */
1058 ToolbarModifier.prototype._getToolbarGroupString = function( group ) {
1059 var subgroups = group.groups,
1060 groupString = '';
1061
1062 groupString += [
1063 '<li ',
1064 'data-type="group" ',
1065 'data-name="', group.name, '" ',
1066 ( group.totalBtns ? '' : 'class="empty"' ),
1067 '>'
1068 ].join( '' );
1069 groupString += ToolbarModifier.getToolbarElementPreString( group ) + '<ul>';
1070
1071 var max = subgroups.length;
1072
1073 for ( var i = 0; i < max; i += 1 ) {
1074 var currentSubgroup = subgroups[ i ],
1075 subgroupBtns = this.fullToolbarEditor.buttonsByGroup[ currentSubgroup.name ];
1076
1077 groupString += this._getToolbarSubgroupString( currentSubgroup, subgroupBtns );
1078 }
1079 groupString += '</ul></li>';
1080
1081 return groupString;
1082 };
1083
1084 /**
1085 * @param {Object} separator
1086 * @returns {String}
1087 * @static
1088 */
1089 ToolbarModifier.getToolbarSeparatorString = function( separator ) {
1090 return [
1091 '<li ',
1092 'data-type="', separator.type , '" ',
1093 'data-name="', separator.name , '"',
1094 '>',
1095 ToolbarModifier.getToolbarElementPreString( 'row separator' ),
1096 '</li>'
1097 ].join( '' );
1098 };
1099
1100 /**
1101 * @returns {string}
1102 */
1103 ToolbarModifier.getToolbarHeaderString = function() {
1104 return '<ul data-type="table-header">' +
1105 '<li data-type="header">' +
1106 '<p>Toolbars</p>' +
1107 '<ul>' +
1108 '<li>' +
1109 '<p>Toolbar groups</p>' +
1110 '<p>Toolbar group items</p>' +
1111 '</li>' +
1112 '</ul>' +
1113 '</li>' +
1114 '</ul>';
1115 };
1116
1117 /**
1118 * Find and return first ancestor of element provided in first argument
1119 * which match the criteria checked in function provided in second argument.
1120 *
1121 * @param {CKEDITOR.dom.element} element
1122 * @param {Function} checker
1123 * @returns {CKEDITOR.dom.element|null}
1124 */
1125 ToolbarModifier.getFirstAncestor = function( element, checker ) {
1126 var ancestors = element.getParents(),
1127 i = ancestors.length;
1128
1129 while ( i-- ) {
1130 if ( checker( ancestors[ i ] ) )
1131 return ancestors[ i ];
1132 }
1133
1134 return null;
1135 };
1136
1137 /**
1138 * Looking through array elements start from index provided in second argument
1139 * and go 'up' or 'down' in array
1140 * last argument is condition checker which should return Boolean value
1141 *
1142 * User cases:
1143 *
1144 * ToolbarModifier.getFirstElementIndexWith( [3, 4, 8, 1, 4], 2, 'down', function( elem ) { return elem == 4; } ); // 4
1145 * ToolbarModifier.getFirstElementIndexWith( [3, 4, 8, 1, 4], 2, 'up', function( elem ) { return elem == 4; } ); // 1
1146 *
1147 * @param {Array} array
1148 * @param {Number} i
1149 * @param {String} direction 'up' or 'down'
1150 * @param {Function} conditionChecker
1151 * @static
1152 * @returns {Number} index of found element
1153 */
1154 ToolbarModifier.getFirstElementIndexWith = function( array, i, direction, conditionChecker ) {
1155 function whileChecker() {
1156 var result;
1157 if ( direction === 'up' )
1158 result = i--;
1159 else
1160 result = ( ++i < array.length );
1161
1162 return result;
1163 }
1164
1165 while ( whileChecker() ) {
1166 if ( conditionChecker( array[ i ] ) )
1167 return i;
1168
1169 }
1170
1171 return -1;
1172 };
1173
1174 /**
1175 * Moves array element at index level up or down.
1176 *
1177 * @static
1178 * @param {String} direction
1179 * @param {Array} array
1180 * @param {Number} index
1181 * @returns {Number}
1182 */
1183 ToolbarModifier.moveTo = function( offset, array, index ) {
1184 var element, newIndex;
1185
1186 if ( index !== -1 )
1187 element = array.splice( index, 1 )[ 0 ];
1188
1189 newIndex = index + offset;
1190
1191 array.splice( newIndex, 0, element );
1192
1193 return newIndex;
1194 };
1195
1196 /**
1197 * @static
1198 * @param {Object} subgroup
1199 * @returns {Number}
1200 */
1201 ToolbarModifier.getTotalSubGroupButtonsNumber = function( subgroup, fullToolbarEditor ) {
1202 var subgroupName = ( typeof subgroup == 'string' ? subgroup : subgroup.name ),
1203 subgroupBtns = fullToolbarEditor.buttonsByGroup[ subgroupName ];
1204
1205 return ( subgroupBtns ? subgroupBtns.length : 0 );
1206 };
1207
1208 /**
1209 * Returns all buttons number in group which are nested in subgroups also.
1210 *
1211 * @param {Object} group
1212 * @param {ToolbarModifier.FullToolbarEditor}
1213 * @static
1214 * @returns {Number}
1215 */
1216 ToolbarModifier.getTotalGroupButtonsNumber = function( group, fullToolbarEditor ) {
1217 var total = 0,
1218 subgroups = group.groups;
1219
1220 var max = subgroups ? subgroups.length : 0;
1221 for ( var i = 0; i < max; i += 1 )
1222 total += ToolbarModifier.getTotalSubGroupButtonsNumber( subgroups[ i ], fullToolbarEditor );
1223
1224 return total;
1225 };
1226
1227 /**
1228 * Creates HTML subgroup list element based on subgroup field in config.
1229 *
1230 * @param {Object} subgroup
1231 * @param {Array} groupBtns
1232 * @returns {String}
1233 * @private
1234 */
1235 ToolbarModifier.prototype._getToolbarSubgroupString = function( subgroup, groupBtns ) {
1236 var subgroupString = '';
1237
1238 subgroupString += [
1239 '<li ',
1240 'data-type="subgroup" ',
1241 'data-name="', subgroup.name, '" ',
1242 ( subgroup.totalBtns ? '' : 'class="empty" ' ),
1243 '>'
1244 ].join( '' );
1245 subgroupString += ToolbarModifier.getToolbarElementPreString( subgroup.name );
1246 subgroupString += '<ul>';
1247
1248 var max = groupBtns ? groupBtns.length : 0;
1249 for ( var i = 0; i < max; i += 1 )
1250 subgroupString += this.getButtonString( groupBtns[ i ] );
1251
1252 subgroupString += '</ul>';
1253
1254 subgroupString += '</li>';
1255
1256 return subgroupString;
1257 };
1258
1259 /**
1260 * @param {String} buttonName
1261 * @returns {String|null}
1262 * @private
1263 */
1264 ToolbarModifier.prototype._getConfigButtonName = function( buttonName ) {
1265 var items = this.fullToolbarEditor.editorInstance.ui.items;
1266
1267 var name;
1268 for ( name in items ) {
1269 if ( items[ name ].name == buttonName )
1270 return name;
1271 }
1272
1273 return null;
1274 };
1275
1276 /**
1277 * @param {String} buttonName
1278 * @returns {Boolean}
1279 */
1280 ToolbarModifier.prototype.isButtonRemoved = function( buttonName ) {
1281 return CKEDITOR.tools.indexOf( this.removedButtons, this._getConfigButtonName( buttonName ) ) != -1;
1282 };
1283
1284 /**
1285 * @param {CKEDITOR.ui.button/CKEDITOR.ui.richCombo} button
1286 * @returns {String}
1287 * @public
1288 */
1289 ToolbarModifier.prototype.getButtonString = function( button ) {
1290 var checked = ( this.isButtonRemoved( button.name ) ? '' : 'checked="checked"' );
1291
1292 return [
1293 '<li data-tab="true" data-type="button" data-name="', this._getConfigButtonName( button.name ), '">',
1294 '<label title="', button.label, '" >',
1295 '<input ',
1296 'tabindex="-1"',
1297 'type="checkbox"',
1298 checked,
1299 '/>',
1300 button.$.getOuterHtml(),
1301 '</label>',
1302 '</li>'
1303 ].join( '' );
1304 };
1305
1306 /**
1307 * Creates group header string.
1308 *
1309 * @param {Object|String} group
1310 * @returns {String}
1311 * @static
1312 */
1313 ToolbarModifier.getToolbarElementPreString = function( group ) {
1314 var name = ( group.name ? group.name : group );
1315
1316 return [
1317 '<p>',
1318 '<span>',
1319 '<button title="Move element upward" data-tab="true" data-direction="up" class="move icon-up-big"></button>',
1320 '<button title="Move element downward" data-tab="true" data-direction="down" class="move icon-down-big"></button>',
1321 ( name == 'row separator' ? '<button title="Remove element" data-tab="true" class="remove icon-trash"></button>' : '' ),
1322 name,
1323 '</span>',
1324 '</p>'
1325 ].join( '' );
1326 };
1327
1328 /**
1329 * @static
1330 * @param {String} cfg
1331 * @returns {String}
1332 */
1333 ToolbarModifier.evaluateToolbarGroupsConfig = function( cfg ) {
1334 cfg = ( function( cfg ) {
1335 var config = {}, result;
1336
1337 /*jshint -W002 */
1338 try {
1339 result = eval( '(' + cfg + ')' );
1340 } catch ( e ) {
1341 try {
1342 result = eval( cfg );
1343 } catch ( e ) {
1344 return null;
1345 }
1346 }
1347 /*jshint +W002 */
1348
1349 if ( config.toolbarGroups && typeof config.toolbarGroups.length === 'number' ) {
1350 return JSON.stringify( config );
1351 } else if ( result && typeof result.length === 'number' ) {
1352 return JSON.stringify( { toolbarGroups: result } );
1353 } else if ( result && result.toolbarGroups ) {
1354 return JSON.stringify( result );
1355 } else {
1356 return null;
1357 }
1358
1359 }( cfg ) );
1360
1361 return cfg;
1362 };
1363
1364 return ToolbarModifier;
1365} )();
1366
diff --git a/sources/samples/toolbarconfigurator/js/toolbartextmodifier.js b/sources/samples/toolbarconfigurator/js/toolbartextmodifier.js
new file mode 100644
index 0000000..4c14dd2
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/js/toolbartextmodifier.js
@@ -0,0 +1,623 @@
1/* global CodeMirror, ToolbarConfigurator */
2
3'use strict';
4
5( function() {
6 var AbstractToolbarModifier = ToolbarConfigurator.AbstractToolbarModifier,
7 FullToolbarEditor = ToolbarConfigurator.FullToolbarEditor;
8
9 /**
10 * @class ToolbarConfigurator.ToolbarTextModifier
11 * @param {String} editorId An id of modified editor
12 * @extends AbstractToolbarModifier
13 * @constructor
14 */
15 function ToolbarTextModifier( editorId ) {
16 AbstractToolbarModifier.call( this, editorId );
17
18 this.codeContainer = null;
19 this.hintContainer = null;
20 }
21
22 // Expose the class.
23 ToolbarConfigurator.ToolbarTextModifier = ToolbarTextModifier;
24
25 ToolbarTextModifier.prototype = Object.create( AbstractToolbarModifier.prototype );
26
27 /**
28 * @param {Function} callback
29 * @param {String} [config]
30 * @private
31 */
32 ToolbarTextModifier.prototype._onInit = function( callback, config ) {
33 AbstractToolbarModifier.prototype._onInit.call( this, undefined, config );
34
35 this._createModifier( config ? this.actualConfig : undefined );
36
37 if ( typeof callback === 'function' )
38 callback( this.mainContainer );
39 };
40
41 /**
42 * Creates HTML main container of modifier.
43 *
44 * @param {String} cfg
45 * @returns {CKEDITOR.dom.element}
46 * @private
47 */
48 ToolbarTextModifier.prototype._createModifier = function( cfg ) {
49 var that = this;
50
51 this._createToolbar();
52
53 if ( this.toolbarContainer ) {
54 this.mainContainer.append( this.toolbarContainer );
55 }
56
57 AbstractToolbarModifier.prototype._createModifier.call( this );
58
59 this._setupActualConfig( cfg );
60
61 var toolbarCfg = this.actualConfig.toolbar,
62 cfgValue;
63
64 if ( CKEDITOR.tools.isArray( toolbarCfg ) ) {
65 var stringifiedToolbar = '[\n\t\t' + FullToolbarEditor.map( toolbarCfg, function( json ) {
66 return AbstractToolbarModifier.stringifyJSONintoOneLine( json, {
67 addSpaces: true,
68 noQuotesOnKey: true,
69 singleQuotes: true
70 } );
71 } ).join( ',\n\t\t' ) + '\n\t]';
72
73 cfgValue = '\tconfig.toolbar = ' + stringifiedToolbar + ';';
74 } else {
75 cfgValue = 'config.toolbar = [];';
76 }
77
78 cfgValue = [
79 'CKEDITOR.editorConfig = function( config ) {\n',
80 cfgValue,
81 '\n};'
82 ].join( '' );
83
84 function hint( cm ) {
85 var data = setupData( cm );
86
87 if ( data.charsBetween === null ) {
88 return;
89 }
90
91 var unused = that.getUnusedButtonsArray( that.actualConfig.toolbar, true, data.charsBetween ),
92 to = cm.getCursor(),
93 from = CodeMirror.Pos( to.line, ( to.ch - ( data.charsBetween.length ) ) ),
94 token = cm.getTokenAt( to ),
95 prevToken = cm.getTokenAt( { line: to.line, ch: token.start } );
96
97 // determine that we are at beginning of group,
98 // so first key is "name"
99 if ( prevToken.string === '{' )
100 unused = [ 'name' ];
101
102 // preventing close with special character and move cursor forward
103 // when no autocomplete
104 if ( unused.length === 0 )
105 return;
106
107 return new HintData( from, to, unused );
108 }
109
110 function HintData( from, to, list ) {
111 this.from = from;
112 this.to = to;
113 this.list = list;
114 this._handlers = [];
115 }
116
117 function setupData( cm, character ) {
118 var result = {};
119
120 result.cur = cm.getCursor();
121 result.tok = cm.getTokenAt( result.cur );
122
123 result[ 'char' ] = character || result.tok.string.charAt( result.tok.string.length - 1 );
124
125 // Getting string between begin of line and cursor.
126 var curLineTillCur = cm.getRange( CodeMirror.Pos( result.cur.line, 0 ), result.cur );
127
128 // Reverse string.
129 var currLineTillCurReversed = curLineTillCur.split( '' ).reverse().join( '' );
130
131 // Removing proper string definitions :
132 // FROM:
133 // R' ,'odeR' ,'odnU' [ :smeti{
134 // ^^^^^^ ^^^^^^
135 // TO:
136 // R' , [ :smeti{
137 currLineTillCurReversed = currLineTillCurReversed.replace( /(['|"]\w*['|"])/g, '' );
138
139 // Matching letters till ' or " character and end string char.
140 // R' , [ :smeti{
141 // ^
142 result.charsBetween = currLineTillCurReversed.match( /(^\w*)(['|"])/ );
143
144 if ( result.charsBetween ) {
145 result.endChar = result.charsBetween[ 2 ];
146
147 // And reverse string (bring to original state).
148 result.charsBetween = result.charsBetween[ 1 ].split( '' ).reverse().join( '' );
149 }
150
151 return result;
152 }
153
154 function complete( cm ) {
155 setTimeout( function() {
156 if ( !cm.state.completionActive ) {
157 CodeMirror.showHint( cm, hint, {
158 hintsClass: 'toolbar-modifier',
159 completeSingle: false
160 } );
161 }
162 }, 100 );
163
164 return CodeMirror.Pass;
165 }
166
167 var codeMirrorWrapper = new CKEDITOR.dom.element( 'div' );
168 codeMirrorWrapper.addClass( 'codemirror-wrapper' );
169 this.modifyContainer.append( codeMirrorWrapper );
170 this.codeContainer = CodeMirror( codeMirrorWrapper.$, {
171 mode: { name: 'javascript', json: true },
172 // For some reason (most likely CM's bug) gutter breaks CM's height.
173 // Refreshing CM does not help.
174 lineNumbers: false,
175 lineWrapping: true,
176 // Trick to make CM autogrow. http://codemirror.net/demo/resize.html
177 viewportMargin: Infinity,
178 value: cfgValue,
179 smartIndent: false,
180 indentWithTabs: true,
181 indentUnit: 4,
182 tabSize: 4,
183 theme: 'neo',
184 extraKeys: {
185 'Left': complete,
186 'Right': complete,
187 "'''": complete,
188 "'\"'": complete,
189 Backspace: complete,
190 Delete: complete,
191 'Shift-Tab': 'indentLess'
192 }
193 } );
194
195 this.codeContainer.on( 'endCompletion', function( cm, completionData ) {
196 var data = setupData( cm );
197
198 // preventing close with special character and move cursor forward
199 // when no autocomplete
200 if ( completionData === undefined )
201 return;
202
203 cm.replaceSelection( data.endChar );
204 } );
205
206 this.codeContainer.on( 'change', function() {
207 var value = that.codeContainer.getValue();
208
209 value = that._evaluateValue( value );
210
211 if ( value !== null ) {
212 that.actualConfig.toolbar = ( value.toolbar ? value.toolbar : that.actualConfig.toolbar );
213
214 that._fillHintByUnusedElements();
215 that._refreshEditor();
216
217 that.mainContainer.removeClass( 'invalid' );
218 } else {
219 that.mainContainer.addClass( 'invalid' );
220 }
221 } );
222
223 this.hintContainer = new CKEDITOR.dom.element( 'div' );
224 this.hintContainer.addClass( 'toolbarModifier-hints' );
225
226 this._fillHintByUnusedElements();
227 this.hintContainer.insertBefore( codeMirrorWrapper );
228 };
229
230 /**
231 * Create DOM string and set to hint container,
232 * show proper information when no unused element left.
233 *
234 * @private
235 */
236 ToolbarTextModifier.prototype._fillHintByUnusedElements = function() {
237 var unused = this.getUnusedButtonsArray( this.actualConfig.toolbar, true );
238 unused = this.groupButtonNamesByGroup( unused );
239
240 var unusedElements = FullToolbarEditor.map( unused, function( elem ) {
241 var buttonsList = FullToolbarEditor.map( elem.buttons, function( buttonName ) {
242 return '<code>' + buttonName + '</code> ';
243 } ).join( '' );
244
245 return [
246 '<dt>',
247 '<code>', elem.name, '</code>',
248 '</dt>',
249 '<dd>',
250 buttonsList,
251 '</dd>'
252 ].join( '' );
253 } ).join( ' ' );
254
255 var listHeader = [
256 '<dt class="list-header">Toolbar group</dt>',
257 '<dd class="list-header">Unused items</dd>'
258 ].join( '' );
259
260 var header = '<h3>Unused toolbar items</h3>';
261
262 if ( !unused.length ) {
263 listHeader = '<p>All items are in use.</p>';
264 }
265
266 this.codeContainer.refresh();
267
268 this.hintContainer.setHtml( header + '<dl>' + listHeader + unusedElements + '</dl>' );
269 };
270
271 /**
272 * @param {String} buttonName
273 * @returns {String}
274 */
275 ToolbarTextModifier.prototype.getToolbarGroupByButtonName = function( buttonName ) {
276 var buttonNames = this.fullToolbarEditor.buttonNamesByGroup;
277
278 for ( var groupName in buttonNames ) {
279 var buttons = buttonNames[ groupName ];
280
281 var i = buttons.length;
282 while ( i-- ) {
283 if ( buttonName === buttons[ i ] ) {
284 return groupName;
285 }
286 }
287
288 }
289
290 return null;
291 };
292
293 /**
294 * Filter all available toolbar elements by array of elements provided in first argument.
295 * Returns elements which are not used.
296 *
297 * @param {Object} toolbar
298 * @param {Boolean} [sorted=false]
299 * @param {String} prefix
300 * @returns {Array}
301 */
302 ToolbarTextModifier.prototype.getUnusedButtonsArray = function( toolbar, sorted, prefix ) {
303 sorted = ( sorted === true ? true : false );
304 var providedElements = ToolbarTextModifier.mapToolbarCfgToElementsList( toolbar ),
305 allElements = Object.keys( this.fullToolbarEditor.editorInstance.ui.items );
306
307 // get rid of "-" elements
308 allElements = FullToolbarEditor.filter( allElements, function( elem ) {
309 var isSeparator = ( elem === '-' ),
310 matchPrefix = ( prefix === undefined || elem.toLowerCase().indexOf( prefix.toLowerCase() ) === 0 );
311
312 return !isSeparator && matchPrefix;
313 } );
314
315 var elementsNotUsed = FullToolbarEditor.filter( allElements, function( elem ) {
316 return CKEDITOR.tools.indexOf( providedElements, elem ) == -1;
317 } );
318
319 if ( sorted )
320 elementsNotUsed.sort();
321
322 return elementsNotUsed;
323 };
324
325 /**
326 *
327 * @param {Array} buttons
328 * @returns {Array}
329 */
330 ToolbarTextModifier.prototype.groupButtonNamesByGroup = function( buttons ) {
331 var result = [],
332 groupedBtns = JSON.parse( JSON.stringify( this.fullToolbarEditor.buttonNamesByGroup ) );
333
334 for ( var groupName in groupedBtns ) {
335 var currGroup = groupedBtns[ groupName ];
336 currGroup = FullToolbarEditor.filter( currGroup, function( btnName ) {
337 return CKEDITOR.tools.indexOf( buttons, btnName ) !== -1;
338 } );
339
340 if ( currGroup.length ) {
341 result.push( {
342 name: groupName,
343 buttons: currGroup
344 } );
345 }
346
347 }
348
349 return result;
350 };
351
352 /**
353 * Map toolbar config value to flat items list.
354 *
355 * input:
356 * [
357 * { name: "basicstyles", items: ["Bold", "Italic"] },
358 * { name: "advancedstyles", items: ["Bold", "Outdent", "Indent"] }
359 * ]
360 *
361 * output:
362 * ["Bold", "Italic", "Outdent", "Indent"]
363 *
364 * @param {Object} toolbar
365 * @returns {Array}
366 */
367 ToolbarTextModifier.mapToolbarCfgToElementsList = function( toolbar ) {
368 var elements = [];
369
370 var max = toolbar.length;
371 for ( var i = 0; i < max; i += 1 ) {
372 if ( !toolbar[ i ] || typeof toolbar[ i ] === 'string' )
373 continue;
374
375 elements = elements.concat( FullToolbarEditor.filter( toolbar[ i ].items, checker ) );
376 }
377
378 function checker( elem ) {
379 return elem !== '-';
380 }
381
382 return elements;
383 };
384
385 /**
386 * @param {String} cfg
387 * @private
388 */
389 ToolbarTextModifier.prototype._setupActualConfig = function( cfg ) {
390 cfg = cfg || this.editorInstance.config;
391
392 // if toolbar already exists in config, there is nothing to do
393 if ( CKEDITOR.tools.isArray( cfg.toolbar ) )
394 return;
395
396 // if toolbar group not present, we need to pick them from full toolbar instance
397 if ( !cfg.toolbarGroups )
398 cfg.toolbarGroups = this.fullToolbarEditor.getFullToolbarGroupsConfig( true );
399
400 this._fixGroups( cfg );
401
402 cfg.toolbar = this._mapToolbarGroupsToToolbar( cfg.toolbarGroups, this.actualConfig.removeButtons );
403
404 this.actualConfig.toolbar = cfg.toolbar;
405 this.actualConfig.removeButtons = '';
406 };
407
408 /**
409 * **Please note:** This method modify element provided in first argument.
410 *
411 * @param {Array} toolbarGroups
412 * @returns {Array}
413 * @private
414 */
415 ToolbarTextModifier.prototype._mapToolbarGroupsToToolbar = function( toolbarGroups, removedBtns ) {
416 removedBtns = removedBtns || this.editorInstance.config.removedBtns;
417 removedBtns = typeof removedBtns == 'string' ? removedBtns.split( ',' ) : [];
418
419 // from the end, because array indexes may change
420 var i = toolbarGroups.length;
421 while ( i-- ) {
422 var mappedSubgroup = this._mapToolbarSubgroup( toolbarGroups[ i ], removedBtns );
423
424 if ( toolbarGroups[ i ].type === 'separator' ) {
425 toolbarGroups[ i ] = '/';
426 continue;
427 }
428
429 // don't want empty groups
430 if ( CKEDITOR.tools.isArray( mappedSubgroup ) && mappedSubgroup.length === 0 ) {
431 toolbarGroups.splice( i, 1 );
432 continue;
433 }
434
435 if ( typeof mappedSubgroup == 'string' )
436 toolbarGroups[ i ] = mappedSubgroup;
437 else {
438 toolbarGroups[ i ] = {
439 name: toolbarGroups[ i ].name,
440 items: mappedSubgroup
441 };
442 }
443 }
444
445 return toolbarGroups;
446 };
447
448 /**
449 *
450 * @param {String|Object} group
451 * @param {Array} removedBtns
452 * @returns {Array}
453 * @private
454 */
455 ToolbarTextModifier.prototype._mapToolbarSubgroup = function( group, removedBtns ) {
456 var totalBtns = 0;
457 if ( typeof group == 'string' )
458 return group;
459
460 var max = group.groups ? group.groups.length : 0,
461 result = [];
462 for ( var i = 0; i < max; i += 1 ) {
463 var currSubgroup = group.groups[ i ];
464
465 var buttons = this.fullToolbarEditor.buttonsByGroup[ typeof currSubgroup === 'string' ? currSubgroup : currSubgroup.name ] || [];
466 buttons = this._mapButtonsToButtonsNames( buttons, removedBtns );
467 var currTotalBtns = buttons.length;
468 totalBtns += currTotalBtns;
469 result = result.concat( buttons );
470
471 if ( currTotalBtns )
472 result.push( '-' );
473 }
474
475 if ( result[ result.length - 1 ] == '-' )
476 result.pop();
477
478 return result;
479 };
480
481 /**
482 *
483 * @param {Array} buttons
484 * @param {Array} removedBtns
485 * @returns {Array}
486 * @private
487 */
488 ToolbarTextModifier.prototype._mapButtonsToButtonsNames = function( buttons, removedBtns ) {
489 var i = buttons.length;
490 while ( i-- ) {
491 var currBtn = buttons[ i ],
492 camelCasedName;
493
494 if ( typeof currBtn === 'string' ) {
495 camelCasedName = currBtn;
496 } else {
497 camelCasedName = this.fullToolbarEditor.getCamelCasedButtonName( currBtn.name );
498 }
499
500 if ( CKEDITOR.tools.indexOf( removedBtns, camelCasedName ) !== -1 ) {
501 buttons.splice( i, 1 );
502 continue;
503 }
504
505 buttons[ i ] = camelCasedName;
506 }
507
508 return buttons;
509 };
510
511 /**
512 * @param {String} val
513 * @returns {Object}
514 * @private
515 */
516 ToolbarTextModifier.prototype._evaluateValue = function( val ) {
517 var parsed;
518
519 try {
520 var config = {};
521 ( function() {
522 var CKEDITOR = Function( 'var CKEDITOR = {}; ' + val + '; return CKEDITOR;' )();
523
524 CKEDITOR.editorConfig( config );
525 parsed = config;
526 } )();
527
528 // CKEditor does not handle empty arrays in configuration files
529 // on IE8
530 var i = parsed.toolbar.length;
531 while ( i-- )
532 if ( !parsed.toolbar[ i ] ) parsed.toolbar.splice( i, 1 );
533
534 } catch ( e ) {
535 parsed = null;
536 }
537
538 return parsed;
539 };
540
541 /**
542 * @param {Array} toolbar
543 * @returns {{toolbarGroups: Array, removeButtons: string}}
544 */
545 ToolbarTextModifier.prototype.mapToolbarToToolbarGroups = function( toolbar ) {
546 var usedGroups = {},
547 removeButtons = [],
548 toolbarGroups = [];
549
550 var max = toolbar.length;
551 for ( var i = 0; i < max; i++ ) {
552 if ( toolbar[ i ] === '/' ) {
553 toolbarGroups.push( '/' );
554 continue;
555 }
556
557 var items = toolbar[ i ].items;
558
559 var toolbarGroup = {};
560 toolbarGroup.name = toolbar[ i ].name;
561 toolbarGroup.groups = [];
562
563 var max2 = items.length;
564 for ( var j = 0; j < max2; j++ ) {
565 var item = items[ j ];
566
567 if ( item === '-' ) {
568 continue;
569 }
570
571 var groupName = this.getToolbarGroupByButtonName( item );
572
573 var groupIndex = toolbarGroup.groups.indexOf( groupName );
574 if ( groupIndex === -1 ) {
575 toolbarGroup.groups.push( groupName );
576 }
577
578 usedGroups[ groupName ] = usedGroups[ groupName ] || {};
579
580 var buttons = ( usedGroups[ groupName ].buttons = usedGroups[ groupName ].buttons || {} );
581
582 buttons[ item ] = buttons[ item ] || { used: 0, origin: toolbarGroup.name };
583 buttons[ item ].used++;
584 }
585
586 toolbarGroups.push( toolbarGroup );
587 }
588
589 // Handling removed buttons
590 removeButtons = prepareRemovedButtons( usedGroups, this.fullToolbarEditor.buttonNamesByGroup );
591
592 function prepareRemovedButtons( usedGroups, buttonNames ) {
593 var removed = [];
594
595 for ( var groupName in usedGroups ) {
596 var group = usedGroups[ groupName ];
597 var allButtonsInGroup = buttonNames[ groupName ].slice();
598
599 removed = removed.concat( removeStuffFromArray( allButtonsInGroup, Object.keys( group.buttons ) ) );
600 }
601
602 return removed;
603 }
604
605 function removeStuffFromArray( array, stuff ) {
606 array = array.slice();
607 var i = stuff.length;
608
609 while ( i-- ) {
610 var atIndex = array.indexOf( stuff[ i ] );
611 if ( atIndex !== -1 ) {
612 array.splice( atIndex, 1 );
613 }
614 }
615
616 return array;
617 }
618
619 return { toolbarGroups: toolbarGroups, removeButtons: removeButtons.join( ',' ) };
620 };
621
622 return ToolbarTextModifier;
623} )();
diff --git a/sources/samples/toolbarconfigurator/less/base.less b/sources/samples/toolbarconfigurator/less/base.less
new file mode 100644
index 0000000..0a212ff
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/less/base.less
@@ -0,0 +1,38 @@
1// Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
2// For licensing, see LICENSE.html or http://cksource.com/ckeditor/license
3
4@base-font-size: 16px;
5@base-line-height: 24px;
6@base-line-ratio: 1.8;
7
8@sample-font-stack: Arial, Helvetica, sans-serif;
9@sample-font-stack-monospace: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
10@sample-font-maven: "Maven Pro";
11@sample-font-indie: "Indie Flower";
12@sample-text-color: #575757;
13
14@sample-link-color: #cf5d4e;
15@sample-link-color-hover: lighten( @sample-link-color, -10% );
16
17@sample-box-background-color: #f5f5f5;
18@sample-box-border-color: #ddd;
19
20@sample-top-navigation-background: #454545;
21
22// Standard gaps
23@sample-standard-vgap: 1.2em;
24@sample-standard-hgap: 1.5em;
25
26// Generic font-size/line-height mixin.
27.font-size( @remSize ) {
28 @pxSize: round( @remSize * @base-font-size, 2 );
29
30 @remHeight: round( @remSize * @base-line-ratio, 2 );
31 @pxHeight: round( @pxSize * @base-line-ratio, 2 );
32
33 font-size: ~"@{pxSize}";
34 font-size: ~"@{remSize}rem";
35
36 line-height: ~"@{pxHeight}";
37 line-height: ~"@{remHeight}rem";
38}
diff --git a/sources/samples/toolbarconfigurator/less/toolbarmodifier.less b/sources/samples/toolbarconfigurator/less/toolbarmodifier.less
new file mode 100644
index 0000000..8aa5d08
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/less/toolbarmodifier.less
@@ -0,0 +1,508 @@
1// Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
2// For licensing, see LICENSE.html or http://cksource.com/ckeditor/license
3
4@import "base.less";
5
6@modifier-group-hover-color: #fffbe3;
7@modifier-group-active-color: #f0fafb;
8
9@modifier-active-toolbar-color: darken( @modifier-group-hover-color, 10% );
10
11@modifier-toolbar-border-color: #ccc;
12@modifier-toolbar-group-border-color: #ddd;
13@modifier-toolbar-group-vpadding: 2px;
14@modifier-toolbar-hgap: 5px;
15
16@modifier-toolbar-button-color: #e7e7e7;
17
18#toolbar .cke_toolbar {
19 pointer-events: none;
20 .user-select( none );
21 cursor: default;
22}
23
24// Dim all but active toolbars if some is active.
25.some-toolbar-active .cke_toolbar {
26 .opacity( .5 );
27}
28
29.cke_toolbar.active {
30 position: relative;
31
32 // Active toolbar is always highlighted.
33 .opacity( 1 );
34
35 &:after {
36 content: '';
37 display: block;
38 position: absolute;
39 top: 0;
40 right: 6px;
41 bottom: 5px;
42 left: 0;
43 .border-radius( 5px );
44 .box-shadow( 0px 0px 15px 3px @modifier-active-toolbar-color );
45 }
46
47 .cke_toolgroup {
48 .box-shadow( none );
49 border-color: darken( @modifier-active-toolbar-color, 40% );
50 }
51
52 .cke_combo,
53 .cke_toolgroup {
54 position: relative;
55 z-index: 2;
56 }
57
58 .cke_combo_button {
59 .box-shadow( none );
60 }
61}
62
63.unselectable {
64 .user-select( none );
65}
66.toolbar {
67 padding: 5px 0;
68 margin-bottom: 2 * @sample-standard-vgap;
69 overflow: hidden;
70 background: #fff;
71
72 button.button-a {
73 &.cke_button {
74 cursor: pointer;
75
76 display: inline-block;
77 padding: 4px 6px;
78 outline: 0;
79 border: 1px solid #a6a6a6;
80 }
81
82 &.hidden {
83 display: none;
84 }
85
86 &.left {
87 float: left;
88 margin-right: 8px;
89 }
90
91 &.right {
92 float: right;
93 margin-left: 8px;
94 }
95
96 .highlight {
97 color: #ffefc1;
98 }
99 }
100}
101
102// Styles applied when configurator is hidden and code is being displayed (and vice-versa).
103.configContainer.hidden,
104.toolbarModifier.hidden,
105.toolbarModifier-hints.hidden {
106 display: none;
107}
108
109.toolbarModifier :focus,
110.toolbar button:focus,
111.configContainer textarea.configCode:focus {
112 outline: none;
113}
114
115div.toolbarModifier {
116 padding: 0;
117 overflow: hidden;
118 width: 100%;
119 position: relative;
120 display: table;
121 border-collapse: collapse;
122
123 ::-moz-focus-inner {
124 border: 0;
125 }
126
127 .empty {
128 display: none;
129 }
130
131 &.empty-visible .empty {
132 display: table-row;
133 .opacity( 0.6 );
134 }
135
136 // Give empty toolbar groups height similar to height of non empty groups.
137 // Non empty groups are stretched by contained toolbar buttons.
138 .empty > p {
139 line-height: 31px;
140 }
141
142 // List of toolbars.
143 & > ul {
144 padding: 0;
145 margin: 0;
146 border-top: 1px solid @modifier-toolbar-border-color;
147 width: 100%;
148
149 &[data-type="table-header"] {
150 display: table-header-group;
151 }
152
153 &[data-type="table-body"] {
154 display: table-row-group;
155 }
156
157 // Override global margins and paddings.
158 p {
159 padding: 0;
160 margin: 0;
161 }
162
163 // A single toolbar.
164 & > li {
165 display: table-row;
166
167 &[data-type="header"] {
168 font-weight: bold;
169 user-select: none;
170 cursor: default;
171 }
172
173 &[data-type="group"],
174 &[data-type="separator"] {
175 border-bottom: 1px solid @modifier-toolbar-border-color;
176 }
177
178 &[data-type="subgroup"] {
179 border-top: 1px solid #eee;
180
181 &:first-child {
182 border-top: none;
183 }
184 }
185
186 &[data-type="group"].active,
187 &[data-type="group"]:hover,
188 &[data-type="separator"].active,
189 &[data-type="separator"]:hover {
190 overflow: hidden;
191 z-index: 2;
192 }
193
194 &[data-type="group"].active,
195 &[data-type="separator"].active,
196 &[data-type="group"].active:hover,
197 &[data-type="separator"].active:hover {
198 background: @modifier-group-active-color;
199 }
200
201 &[data-type="group"]:hover,
202 &[data-type="separator"]:hover {
203 background: @modifier-group-hover-color;
204 }
205
206 &[data-type="separator"] {
207 &:after {
208 content: '';
209 width: 100%;
210 }
211
212 background: #f5f5f5;
213
214 & > p {
215 padding: @modifier-toolbar-group-vpadding @modifier-toolbar-hgap;
216 }
217 }
218
219 & > p, & > ul {
220 display: table-cell;
221 vertical-align: middle;
222 }
223
224 // Note: this also controls the list of toolbar groups.
225 p {
226 padding-left: @modifier-toolbar-hgap;
227 min-width: 200px;
228
229 span {
230 white-space: nowrap;
231 cursor: default;
232
233 button {
234 font-size: 12.666px;
235 margin-right: 5px;
236 cursor: pointer;
237 background: #fff;
238 .border-radius( 5px );
239 border: 1px solid #bbb;
240 padding: 0 7px;
241 line-height: 12px;
242 height: 20px;
243
244 &:not(.disabled) {
245 &:hover,
246 &:focus {
247 color: #fff;
248 background-color: @sample-top-navigation-background;
249 border-color: transparent;
250 }
251 }
252
253 &.move.disabled {
254 cursor: default;
255 .opacity( 0.2 );
256 }
257 }
258 }
259 }
260
261 // List of toolbar groups.
262 ul {
263 border-collapse: collapse;
264 padding: 0;
265 width: 100%;
266
267 // A single toolbar group.
268 li {
269 display: table-row;
270 list-style-type: none;
271 // Resets slightly increased lists' lh which is bigger than button's height
272 // so it stretches columns.
273 line-height: 1;
274
275 &[data-type="subgroup"] {
276 border-top: 1px solid @modifier-toolbar-group-border-color;
277
278 &:first-child {
279 border-top: 0;
280 }
281
282 [data-type="button"] {
283 .border-radius( 3px );
284 padding: 0 2px;
285
286 &:focus {
287 background: rgba(0, 0, 0, 0.04);
288 }
289
290 input {
291 vertical-align: middle;
292 }
293 }
294 }
295
296 & > p, & > ul {
297 display: table-cell;
298 vertical-align: middle;
299 }
300
301 // List of buttons in a group.
302 ul {
303 padding: 0;
304
305 // A single button in a group.
306 li {
307 padding: 0;
308 display: inline-block;
309 cursor: pointer;
310 margin: @modifier-toolbar-group-vpadding 5px @modifier-toolbar-group-vpadding 0;
311
312 // Enforce styles to save space.
313 .cke_combo_text {
314 cursor: pointer;
315 white-space: nowrap;
316 }
317
318 .cke_toolgroup,
319 .cke_combo_button {
320 cursor: pointer;
321 margin: 0;
322 vertical-align: middle;
323 border: 1px solid #ddd;
324 .font-size( .713 );
325 }
326 }
327 }
328 }
329 }
330 }
331 }
332
333 & > .codemirror-wrapper {
334 overflow-y: auto;
335 }
336
337 // Advanced configurator: list of unused elements.
338 &-hints {
339 float: right;
340 width: 350px;
341 min-width: 150px;
342 overflow-y: auto;
343 margin-left: @sample-standard-hgap;
344
345 h3 {
346 .font-size( 1.13 );
347 padding: .3*@sample-standard-vgap @sample-standard-hgap;
348 background: @sample-box-background-color;
349 border-bottom: 1px solid @sample-box-border-color;
350 margin-top: 0;
351 margin-bottom: @sample-standard-vgap;
352 }
353
354 dl {
355 //margin-top: 0;
356 margin-bottom: @sample-standard-vgap;
357 overflow: hidden;
358
359 .list-header {
360 font-weight: bold;
361 border: 0;
362 padding-bottom: .5*@sample-standard-vgap;
363 }
364
365 & > p {
366 text-align: center;
367 }
368
369 dt {
370 float: left;
371 width: 9em;
372 clear: both;
373 text-align: right;
374 border-top: 1px solid @sample-box-border-color;
375 padding-left: @sample-standard-hgap;
376 padding-right: .1em;
377 .box-sizing( border-box );
378
379 code {
380 background: none;
381 border: none;
382 vertical-align: middle;
383 }
384 }
385
386 dd {
387 margin-left: 10em;
388 clear: right;
389 padding-right: @sample-standard-hgap;
390
391 code {
392 line-height: 2.2em;
393 }
394
395 &:after {
396 content: '\00a0';
397 display: block;
398 clear: left;
399 float: right;
400 height: 0;
401 width: 0;
402 }
403 }
404 }
405 }
406}
407
408.toolbarModifier-hints,
409.configContainer textarea.configCode,
410.CodeMirror {
411 .border-radius( 3px );
412 border: 1px solid #ccc;
413 .font-size( .813 );
414}
415
416.configContainer textarea.configCode,
417.CodeMirror pre,
418.CodeMirror-linenumber {
419 .font-size( .813 );
420 font-family: @sample-font-stack-monospace;
421}
422
423.CodeMirror pre {
424 border: none;
425 padding: 0;
426 margin: 0;
427}
428
429.configContainer textarea.configCode {
430 .box-sizing( border-box );
431 color: @sample-text-color;
432 padding: 10px;
433 width: 100%;
434 min-height: 500px;
435 margin: 0;
436 resize: none;
437 outline: none;
438 -moz-tab-size: 4;
439 tab-size: 4;
440 white-space: pre;
441 word-wrap: normal;
442 overflow: auto;
443}
444
445.CodeMirror-hints.toolbar-modifier {
446 padding: 0;
447 color: @sample-text-color;
448
449 .CodeMirror-hint-active {
450 color: @sample-text-color;
451 background: @modifier-group-active-color;
452 }
453
454 .font-size( .875 );
455 font-family: @sample-font-stack-monospace;
456
457 & > li:hover {
458 background: @modifier-group-hover-color;
459 }
460}
461
462/* Text modifier */
463#toolbarModifierWrapper {
464 margin-bottom: @sample-standard-vgap;
465
466 .invalid .CodeMirror {
467 background: #fff8f8;
468 border-color: red;
469 }
470
471 .CodeMirror {
472 // Autogrow. http://codemirror.net/demo/resize.html
473 height: auto;
474 // Complementory with std's CodeMirror-lines vertical padding.
475 // Not needed when we use lines number, but we can't due to a bug in CM.
476 padding: 0 @sample-standard-vgap/2;
477 }
478}
479
480.staticContainer {
481 position: fixed;
482 top: 0;
483 width: 100%;
484 z-index: 10;
485
486 > .grid-container {
487 max-width: 1044px + 2 * @grid-gutter-width;
488
489 .inner {
490 background: #fff;
491
492 .toolbar {
493 margin-bottom: 0;
494 }
495 }
496 }
497}
498
499// Help button to display information about configurator.
500#help {
501 position: relative;
502 top: -15px;
503 left: -5px;
504
505 &-content {
506 display: none;
507 }
508}
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/LICENSE b/sources/samples/toolbarconfigurator/lib/codemirror/LICENSE
new file mode 100644
index 0000000..d21bbea
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/LICENSE
@@ -0,0 +1,19 @@
1Copyright (C) 2014 by Marijn Haverbeke <marijnh@gmail.com> and others
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19THE SOFTWARE.
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/README.md b/sources/samples/toolbarconfigurator/lib/codemirror/README.md
new file mode 100644
index 0000000..38156a7
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/README.md
@@ -0,0 +1,12 @@
1# CodeMirror
2[![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror)
3[![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror)
4[Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?again)](https://marijnhaverbeke.nl/fund/)
5
6CodeMirror is a JavaScript component that provides a code editor in
7the browser. When a mode is available for the language you are coding
8in, it will color your code, and optionally help with indentation.
9
10The project page is http://codemirror.net
11The manual is at http://codemirror.net/doc/manual.html
12The contributing guidelines are in [CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/codemirror.css b/sources/samples/toolbarconfigurator/lib/codemirror/codemirror.css
new file mode 100644
index 0000000..ceacd13
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/codemirror.css
@@ -0,0 +1,325 @@
1/* BASICS */
2
3.CodeMirror {
4 /* Set height, width, borders, and global font properties here */
5 font-family: monospace;
6 height: 300px;
7 color: black;
8}
9
10/* PADDING */
11
12.CodeMirror-lines {
13 padding: 4px 0; /* Vertical padding around content */
14}
15.CodeMirror pre {
16 padding: 0 4px; /* Horizontal padding of content */
17}
18
19.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
20 background-color: white; /* The little square between H and V scrollbars */
21}
22
23/* GUTTER */
24
25.CodeMirror-gutters {
26 border-right: 1px solid #ddd;
27 background-color: #f7f7f7;
28 white-space: nowrap;
29}
30.CodeMirror-linenumbers {}
31.CodeMirror-linenumber {
32 padding: 0 3px 0 5px;
33 min-width: 20px;
34 text-align: right;
35 color: #999;
36 white-space: nowrap;
37}
38
39.CodeMirror-guttermarker { color: black; }
40.CodeMirror-guttermarker-subtle { color: #999; }
41
42/* CURSOR */
43
44.CodeMirror div.CodeMirror-cursor {
45 border-left: 1px solid black;
46}
47/* Shown when moving in bi-directional text */
48.CodeMirror div.CodeMirror-secondarycursor {
49 border-left: 1px solid silver;
50}
51.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
52 width: auto;
53 border: 0;
54 background: #7e7;
55}
56.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
57 z-index: 1;
58}
59
60.cm-animate-fat-cursor {
61 width: auto;
62 border: 0;
63 -webkit-animation: blink 1.06s steps(1) infinite;
64 -moz-animation: blink 1.06s steps(1) infinite;
65 animation: blink 1.06s steps(1) infinite;
66}
67@-moz-keyframes blink {
68 0% { background: #7e7; }
69 50% { background: none; }
70 100% { background: #7e7; }
71}
72@-webkit-keyframes blink {
73 0% { background: #7e7; }
74 50% { background: none; }
75 100% { background: #7e7; }
76}
77@keyframes blink {
78 0% { background: #7e7; }
79 50% { background: none; }
80 100% { background: #7e7; }
81}
82
83/* Can style cursor different in overwrite (non-insert) mode */
84div.CodeMirror-overwrite div.CodeMirror-cursor {}
85
86.cm-tab { display: inline-block; text-decoration: inherit; }
87
88.CodeMirror-ruler {
89 border-left: 1px solid #ccc;
90 position: absolute;
91}
92
93/* DEFAULT THEME */
94
95.cm-s-default .cm-keyword {color: #708;}
96.cm-s-default .cm-atom {color: #219;}
97.cm-s-default .cm-number {color: #164;}
98.cm-s-default .cm-def {color: #00f;}
99.cm-s-default .cm-variable,
100.cm-s-default .cm-punctuation,
101.cm-s-default .cm-property,
102.cm-s-default .cm-operator {}
103.cm-s-default .cm-variable-2 {color: #05a;}
104.cm-s-default .cm-variable-3 {color: #085;}
105.cm-s-default .cm-comment {color: #a50;}
106.cm-s-default .cm-string {color: #a11;}
107.cm-s-default .cm-string-2 {color: #f50;}
108.cm-s-default .cm-meta {color: #555;}
109.cm-s-default .cm-qualifier {color: #555;}
110.cm-s-default .cm-builtin {color: #30a;}
111.cm-s-default .cm-bracket {color: #997;}
112.cm-s-default .cm-tag {color: #170;}
113.cm-s-default .cm-attribute {color: #00c;}
114.cm-s-default .cm-header {color: blue;}
115.cm-s-default .cm-quote {color: #090;}
116.cm-s-default .cm-hr {color: #999;}
117.cm-s-default .cm-link {color: #00c;}
118
119.cm-negative {color: #d44;}
120.cm-positive {color: #292;}
121.cm-header, .cm-strong {font-weight: bold;}
122.cm-em {font-style: italic;}
123.cm-link {text-decoration: underline;}
124.cm-strikethrough {text-decoration: line-through;}
125
126.cm-s-default .cm-error {color: #f00;}
127.cm-invalidchar {color: #f00;}
128
129.CodeMirror-composing { border-bottom: 2px solid; }
130
131/* Default styles for common addons */
132
133div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
134div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
135.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
136.CodeMirror-activeline-background {background: #e8f2ff;}
137
138/* STOP */
139
140/* The rest of this file contains styles related to the mechanics of
141 the editor. You probably shouldn't touch them. */
142
143.CodeMirror {
144 position: relative;
145 overflow: hidden;
146 background: white;
147}
148
149.CodeMirror-scroll {
150 overflow: scroll !important; /* Things will break if this is overridden */
151 /* 30px is the magic margin used to hide the element's real scrollbars */
152 /* See overflow: hidden in .CodeMirror */
153 margin-bottom: -30px; margin-right: -30px;
154 padding-bottom: 30px;
155 height: 100%;
156 outline: none; /* Prevent dragging from highlighting the element */
157 position: relative;
158}
159.CodeMirror-sizer {
160 position: relative;
161 border-right: 30px solid transparent;
162}
163
164/* The fake, visible scrollbars. Used to force redraw during scrolling
165 before actuall scrolling happens, thus preventing shaking and
166 flickering artifacts. */
167.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
168 position: absolute;
169 z-index: 6;
170 display: none;
171}
172.CodeMirror-vscrollbar {
173 right: 0; top: 0;
174 overflow-x: hidden;
175 overflow-y: scroll;
176}
177.CodeMirror-hscrollbar {
178 bottom: 0; left: 0;
179 overflow-y: hidden;
180 overflow-x: scroll;
181}
182.CodeMirror-scrollbar-filler {
183 right: 0; bottom: 0;
184}
185.CodeMirror-gutter-filler {
186 left: 0; bottom: 0;
187}
188
189.CodeMirror-gutters {
190 position: absolute; left: 0; top: 0;
191 z-index: 3;
192}
193.CodeMirror-gutter {
194 white-space: normal;
195 height: 100%;
196 display: inline-block;
197 margin-bottom: -30px;
198 /* Hack to make IE7 behave */
199 *zoom:1;
200 *display:inline;
201}
202.CodeMirror-gutter-wrapper {
203 position: absolute;
204 z-index: 4;
205 height: 100%;
206}
207.CodeMirror-gutter-elt {
208 position: absolute;
209 cursor: default;
210 z-index: 4;
211}
212.CodeMirror-gutter-wrapper {
213 -webkit-user-select: none;
214 -moz-user-select: none;
215 user-select: none;
216}
217
218.CodeMirror-lines {
219 cursor: text;
220 min-height: 1px; /* prevents collapsing before first draw */
221}
222.CodeMirror pre {
223 /* Reset some styles that the rest of the page might have set */
224 -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
225 border-width: 0;
226 background: transparent;
227 font-family: inherit;
228 font-size: inherit;
229 margin: 0;
230 white-space: pre;
231 word-wrap: normal;
232 line-height: inherit;
233 color: inherit;
234 z-index: 2;
235 position: relative;
236 overflow: visible;
237 -webkit-tap-highlight-color: transparent;
238}
239.CodeMirror-wrap pre {
240 word-wrap: break-word;
241 white-space: pre-wrap;
242 word-break: normal;
243}
244
245.CodeMirror-linebackground {
246 position: absolute;
247 left: 0; right: 0; top: 0; bottom: 0;
248 z-index: 0;
249}
250
251.CodeMirror-linewidget {
252 position: relative;
253 z-index: 2;
254 overflow: auto;
255}
256
257.CodeMirror-widget {}
258
259.CodeMirror-code {
260 outline: none;
261}
262
263/* Force content-box sizing for the elements where we expect it */
264.CodeMirror-scroll,
265.CodeMirror-sizer,
266.CodeMirror-gutter,
267.CodeMirror-gutters,
268.CodeMirror-linenumber {
269 -moz-box-sizing: content-box;
270 box-sizing: content-box;
271}
272
273.CodeMirror-measure {
274 position: absolute;
275 width: 100%;
276 height: 0;
277 overflow: hidden;
278 visibility: hidden;
279}
280.CodeMirror-measure pre { position: static; }
281
282.CodeMirror div.CodeMirror-cursor {
283 position: absolute;
284 border-right: none;
285 width: 0;
286}
287
288div.CodeMirror-cursors {
289 visibility: hidden;
290 position: relative;
291 z-index: 3;
292}
293.CodeMirror-focused div.CodeMirror-cursors {
294 visibility: visible;
295}
296
297.CodeMirror-selected { background: #d9d9d9; }
298.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
299.CodeMirror-crosshair { cursor: crosshair; }
300.CodeMirror ::selection { background: #d7d4f0; }
301.CodeMirror ::-moz-selection { background: #d7d4f0; }
302
303.cm-searching {
304 background: #ffa;
305 background: rgba(255, 255, 0, .4);
306}
307
308/* IE7 hack to prevent it from returning funny offsetTops on the spans */
309.CodeMirror span { *vertical-align: text-bottom; }
310
311/* Used to force a border model for a node */
312.cm-force-border { padding-right: .1px; }
313
314@media print {
315 /* Hide the cursor when printing */
316 .CodeMirror div.CodeMirror-cursors {
317 visibility: hidden;
318 }
319}
320
321/* See issue #2901 */
322.cm-tab-wrap-hack:after { content: ''; }
323
324/* Help users use markselection to safely style text background */
325span.CodeMirror-selectedtext { background: none; }
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/codemirror.js b/sources/samples/toolbarconfigurator/lib/codemirror/codemirror.js
new file mode 100644
index 0000000..37e2685
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/codemirror.js
@@ -0,0 +1,8738 @@
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: http://codemirror.net/LICENSE
3
4// This is CodeMirror (http://codemirror.net), a code editor
5// implemented in JavaScript on top of the browser's DOM.
6//
7// You can find some technical background for some of the code below
8// at http://marijnhaverbeke.nl/blog/#cm-internals .
9
10(function(mod) {
11 if (typeof exports == "object" && typeof module == "object") // CommonJS
12 module.exports = mod();
13 else if (typeof define == "function" && define.amd) // AMD
14 return define([], mod);
15 else // Plain browser env
16 this.CodeMirror = mod();
17})(function() {
18 "use strict";
19
20 // BROWSER SNIFFING
21
22 // Kludges for bugs and behavior differences that can't be feature
23 // detected are enabled based on userAgent etc sniffing.
24
25 var gecko = /gecko\/\d/i.test(navigator.userAgent);
26 var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
27 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
28 var ie = ie_upto10 || ie_11up;
29 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
30 var webkit = /WebKit\//.test(navigator.userAgent);
31 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
32 var chrome = /Chrome\//.test(navigator.userAgent);
33 var presto = /Opera\//.test(navigator.userAgent);
34 var safari = /Apple Computer/.test(navigator.vendor);
35 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
36 var phantom = /PhantomJS/.test(navigator.userAgent);
37
38 var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
39 // This is woefully incomplete. Suggestions for alternative methods welcome.
40 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
41 var mac = ios || /Mac/.test(navigator.platform);
42 var windows = /win/i.test(navigator.platform);
43
44 var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
45 if (presto_version) presto_version = Number(presto_version[1]);
46 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
47 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
48 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
49 var captureRightClick = gecko || (ie && ie_version >= 9);
50
51 // Optimize some code when these features are not used.
52 var sawReadOnlySpans = false, sawCollapsedSpans = false;
53
54 // EDITOR CONSTRUCTOR
55
56 // A CodeMirror instance represents an editor. This is the object
57 // that user code is usually dealing with.
58
59 function CodeMirror(place, options) {
60 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
61
62 this.options = options = options ? copyObj(options) : {};
63 // Determine effective options based on given values and defaults.
64 copyObj(defaults, options, false);
65 setGuttersForLineNumbers(options);
66
67 var doc = options.value;
68 if (typeof doc == "string") doc = new Doc(doc, options.mode);
69 this.doc = doc;
70
71 var input = new CodeMirror.inputStyles[options.inputStyle](this);
72 var display = this.display = new Display(place, doc, input);
73 display.wrapper.CodeMirror = this;
74 updateGutters(this);
75 themeChanged(this);
76 if (options.lineWrapping)
77 this.display.wrapper.className += " CodeMirror-wrap";
78 if (options.autofocus && !mobile) display.input.focus();
79 initScrollbars(this);
80
81 this.state = {
82 keyMaps: [], // stores maps added by addKeyMap
83 overlays: [], // highlighting overlays, as added by addOverlay
84 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
85 overwrite: false,
86 delayingBlurEvent: false,
87 focused: false,
88 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
89 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
90 draggingText: false,
91 highlight: new Delayed(), // stores highlight worker timeout
92 keySeq: null, // Unfinished key sequence
93 specialChars: null
94 };
95
96 var cm = this;
97
98 // Override magic textarea content restore that IE sometimes does
99 // on our hidden textarea on reload
100 if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
101
102 registerEventHandlers(this);
103 ensureGlobalHandlers();
104
105 startOperation(this);
106 this.curOp.forceUpdate = true;
107 attachDoc(this, doc);
108
109 if ((options.autofocus && !mobile) || cm.hasFocus())
110 setTimeout(bind(onFocus, this), 20);
111 else
112 onBlur(this);
113
114 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
115 optionHandlers[opt](this, options[opt], Init);
116 maybeUpdateLineNumberWidth(this);
117 if (options.finishInit) options.finishInit(this);
118 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
119 endOperation(this);
120 // Suppress optimizelegibility in Webkit, since it breaks text
121 // measuring on line wrapping boundaries.
122 if (webkit && options.lineWrapping &&
123 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
124 display.lineDiv.style.textRendering = "auto";
125 }
126
127 // DISPLAY CONSTRUCTOR
128
129 // The display handles the DOM integration, both for input reading
130 // and content drawing. It holds references to DOM nodes and
131 // display-related state.
132
133 function Display(place, doc, input) {
134 var d = this;
135 this.input = input;
136
137 // Covers bottom-right square when both scrollbars are present.
138 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
139 d.scrollbarFiller.setAttribute("cm-not-content", "true");
140 // Covers bottom of gutter when coverGutterNextToScrollbar is on
141 // and h scrollbar is present.
142 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
143 d.gutterFiller.setAttribute("cm-not-content", "true");
144 // Will contain the actual code, positioned to cover the viewport.
145 d.lineDiv = elt("div", null, "CodeMirror-code");
146 // Elements are added to these to represent selection and cursors.
147 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
148 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
149 // A visibility: hidden element used to find the size of things.
150 d.measure = elt("div", null, "CodeMirror-measure");
151 // When lines outside of the viewport are measured, they are drawn in this.
152 d.lineMeasure = elt("div", null, "CodeMirror-measure");
153 // Wraps everything that needs to exist inside the vertically-padded coordinate system
154 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
155 null, "position: relative; outline: none");
156 // Moved around its parent to cover visible view.
157 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
158 // Set to the height of the document, allowing scrolling.
159 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
160 d.sizerWidth = null;
161 // Behavior of elts with overflow: auto and padding is
162 // inconsistent across browsers. This is used to ensure the
163 // scrollable area is big enough.
164 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
165 // Will contain the gutters, if any.
166 d.gutters = elt("div", null, "CodeMirror-gutters");
167 d.lineGutter = null;
168 // Actual scrollable element.
169 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
170 d.scroller.setAttribute("tabIndex", "-1");
171 // The element in which the editor lives.
172 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
173
174 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
175 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
176 if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
177
178 if (place) {
179 if (place.appendChild) place.appendChild(d.wrapper);
180 else place(d.wrapper);
181 }
182
183 // Current rendered range (may be bigger than the view window).
184 d.viewFrom = d.viewTo = doc.first;
185 d.reportedViewFrom = d.reportedViewTo = doc.first;
186 // Information about the rendered lines.
187 d.view = [];
188 d.renderedView = null;
189 // Holds info about a single rendered line when it was rendered
190 // for measurement, while not in view.
191 d.externalMeasured = null;
192 // Empty space (in pixels) above the view
193 d.viewOffset = 0;
194 d.lastWrapHeight = d.lastWrapWidth = 0;
195 d.updateLineNumbers = null;
196
197 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
198 d.scrollbarsClipped = false;
199
200 // Used to only resize the line number gutter when necessary (when
201 // the amount of lines crosses a boundary that makes its width change)
202 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
203 // Set to true when a non-horizontal-scrolling line widget is
204 // added. As an optimization, line widget aligning is skipped when
205 // this is false.
206 d.alignWidgets = false;
207
208 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
209
210 // Tracks the maximum line length so that the horizontal scrollbar
211 // can be kept static when scrolling.
212 d.maxLine = null;
213 d.maxLineLength = 0;
214 d.maxLineChanged = false;
215
216 // Used for measuring wheel scrolling granularity
217 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
218
219 // True when shift is held down.
220 d.shift = false;
221
222 // Used to track whether anything happened since the context menu
223 // was opened.
224 d.selForContextMenu = null;
225
226 d.activeTouch = null;
227
228 input.init(d);
229 }
230
231 // STATE UPDATES
232
233 // Used to get the editor into a consistent state again when options change.
234
235 function loadMode(cm) {
236 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
237 resetModeState(cm);
238 }
239
240 function resetModeState(cm) {
241 cm.doc.iter(function(line) {
242 if (line.stateAfter) line.stateAfter = null;
243 if (line.styles) line.styles = null;
244 });
245 cm.doc.frontier = cm.doc.first;
246 startWorker(cm, 100);
247 cm.state.modeGen++;
248 if (cm.curOp) regChange(cm);
249 }
250
251 function wrappingChanged(cm) {
252 if (cm.options.lineWrapping) {
253 addClass(cm.display.wrapper, "CodeMirror-wrap");
254 cm.display.sizer.style.minWidth = "";
255 cm.display.sizerWidth = null;
256 } else {
257 rmClass(cm.display.wrapper, "CodeMirror-wrap");
258 findMaxLine(cm);
259 }
260 estimateLineHeights(cm);
261 regChange(cm);
262 clearCaches(cm);
263 setTimeout(function(){updateScrollbars(cm);}, 100);
264 }
265
266 // Returns a function that estimates the height of a line, to use as
267 // first approximation until the line becomes visible (and is thus
268 // properly measurable).
269 function estimateHeight(cm) {
270 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
271 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
272 return function(line) {
273 if (lineIsHidden(cm.doc, line)) return 0;
274
275 var widgetsHeight = 0;
276 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
277 if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
278 }
279
280 if (wrapping)
281 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
282 else
283 return widgetsHeight + th;
284 };
285 }
286
287 function estimateLineHeights(cm) {
288 var doc = cm.doc, est = estimateHeight(cm);
289 doc.iter(function(line) {
290 var estHeight = est(line);
291 if (estHeight != line.height) updateLineHeight(line, estHeight);
292 });
293 }
294
295 function themeChanged(cm) {
296 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
297 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
298 clearCaches(cm);
299 }
300
301 function guttersChanged(cm) {
302 updateGutters(cm);
303 regChange(cm);
304 setTimeout(function(){alignHorizontally(cm);}, 20);
305 }
306
307 // Rebuild the gutter elements, ensure the margin to the left of the
308 // code matches their width.
309 function updateGutters(cm) {
310 var gutters = cm.display.gutters, specs = cm.options.gutters;
311 removeChildren(gutters);
312 for (var i = 0; i < specs.length; ++i) {
313 var gutterClass = specs[i];
314 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
315 if (gutterClass == "CodeMirror-linenumbers") {
316 cm.display.lineGutter = gElt;
317 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
318 }
319 }
320 gutters.style.display = i ? "" : "none";
321 updateGutterSpace(cm);
322 }
323
324 function updateGutterSpace(cm) {
325 var width = cm.display.gutters.offsetWidth;
326 cm.display.sizer.style.marginLeft = width + "px";
327 }
328
329 // Compute the character length of a line, taking into account
330 // collapsed ranges (see markText) that might hide parts, and join
331 // other lines onto it.
332 function lineLength(line) {
333 if (line.height == 0) return 0;
334 var len = line.text.length, merged, cur = line;
335 while (merged = collapsedSpanAtStart(cur)) {
336 var found = merged.find(0, true);
337 cur = found.from.line;
338 len += found.from.ch - found.to.ch;
339 }
340 cur = line;
341 while (merged = collapsedSpanAtEnd(cur)) {
342 var found = merged.find(0, true);
343 len -= cur.text.length - found.from.ch;
344 cur = found.to.line;
345 len += cur.text.length - found.to.ch;
346 }
347 return len;
348 }
349
350 // Find the longest line in the document.
351 function findMaxLine(cm) {
352 var d = cm.display, doc = cm.doc;
353 d.maxLine = getLine(doc, doc.first);
354 d.maxLineLength = lineLength(d.maxLine);
355 d.maxLineChanged = true;
356 doc.iter(function(line) {
357 var len = lineLength(line);
358 if (len > d.maxLineLength) {
359 d.maxLineLength = len;
360 d.maxLine = line;
361 }
362 });
363 }
364
365 // Make sure the gutters options contains the element
366 // "CodeMirror-linenumbers" when the lineNumbers option is true.
367 function setGuttersForLineNumbers(options) {
368 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
369 if (found == -1 && options.lineNumbers) {
370 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
371 } else if (found > -1 && !options.lineNumbers) {
372 options.gutters = options.gutters.slice(0);
373 options.gutters.splice(found, 1);
374 }
375 }
376
377 // SCROLLBARS
378
379 // Prepare DOM reads needed to update the scrollbars. Done in one
380 // shot to minimize update/measure roundtrips.
381 function measureForScrollbars(cm) {
382 var d = cm.display, gutterW = d.gutters.offsetWidth;
383 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
384 return {
385 clientHeight: d.scroller.clientHeight,
386 viewHeight: d.wrapper.clientHeight,
387 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
388 viewWidth: d.wrapper.clientWidth,
389 barLeft: cm.options.fixedGutter ? gutterW : 0,
390 docHeight: docH,
391 scrollHeight: docH + scrollGap(cm) + d.barHeight,
392 nativeBarWidth: d.nativeBarWidth,
393 gutterWidth: gutterW
394 };
395 }
396
397 function NativeScrollbars(place, scroll, cm) {
398 this.cm = cm;
399 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
400 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
401 place(vert); place(horiz);
402
403 on(vert, "scroll", function() {
404 if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
405 });
406 on(horiz, "scroll", function() {
407 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
408 });
409
410 this.checkedOverlay = false;
411 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
412 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
413 }
414
415 NativeScrollbars.prototype = copyObj({
416 update: function(measure) {
417 var needsH = measure.scrollWidth > measure.clientWidth + 1;
418 var needsV = measure.scrollHeight > measure.clientHeight + 1;
419 var sWidth = measure.nativeBarWidth;
420
421 if (needsV) {
422 this.vert.style.display = "block";
423 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
424 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
425 // A bug in IE8 can cause this value to be negative, so guard it.
426 this.vert.firstChild.style.height =
427 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
428 } else {
429 this.vert.style.display = "";
430 this.vert.firstChild.style.height = "0";
431 }
432
433 if (needsH) {
434 this.horiz.style.display = "block";
435 this.horiz.style.right = needsV ? sWidth + "px" : "0";
436 this.horiz.style.left = measure.barLeft + "px";
437 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
438 this.horiz.firstChild.style.width =
439 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
440 } else {
441 this.horiz.style.display = "";
442 this.horiz.firstChild.style.width = "0";
443 }
444
445 if (!this.checkedOverlay && measure.clientHeight > 0) {
446 if (sWidth == 0) this.overlayHack();
447 this.checkedOverlay = true;
448 }
449
450 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
451 },
452 setScrollLeft: function(pos) {
453 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
454 },
455 setScrollTop: function(pos) {
456 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
457 },
458 overlayHack: function() {
459 var w = mac && !mac_geMountainLion ? "12px" : "18px";
460 this.horiz.style.minHeight = this.vert.style.minWidth = w;
461 var self = this;
462 var barMouseDown = function(e) {
463 if (e_target(e) != self.vert && e_target(e) != self.horiz)
464 operation(self.cm, onMouseDown)(e);
465 };
466 on(this.vert, "mousedown", barMouseDown);
467 on(this.horiz, "mousedown", barMouseDown);
468 },
469 clear: function() {
470 var parent = this.horiz.parentNode;
471 parent.removeChild(this.horiz);
472 parent.removeChild(this.vert);
473 }
474 }, NativeScrollbars.prototype);
475
476 function NullScrollbars() {}
477
478 NullScrollbars.prototype = copyObj({
479 update: function() { return {bottom: 0, right: 0}; },
480 setScrollLeft: function() {},
481 setScrollTop: function() {},
482 clear: function() {}
483 }, NullScrollbars.prototype);
484
485 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
486
487 function initScrollbars(cm) {
488 if (cm.display.scrollbars) {
489 cm.display.scrollbars.clear();
490 if (cm.display.scrollbars.addClass)
491 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
492 }
493
494 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
495 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
496 // Prevent clicks in the scrollbars from killing focus
497 on(node, "mousedown", function() {
498 if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
499 });
500 node.setAttribute("cm-not-content", "true");
501 }, function(pos, axis) {
502 if (axis == "horizontal") setScrollLeft(cm, pos);
503 else setScrollTop(cm, pos);
504 }, cm);
505 if (cm.display.scrollbars.addClass)
506 addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
507 }
508
509 function updateScrollbars(cm, measure) {
510 if (!measure) measure = measureForScrollbars(cm);
511 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
512 updateScrollbarsInner(cm, measure);
513 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
514 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
515 updateHeightsInViewport(cm);
516 updateScrollbarsInner(cm, measureForScrollbars(cm));
517 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
518 }
519 }
520
521 // Re-synchronize the fake scrollbars with the actual size of the
522 // content.
523 function updateScrollbarsInner(cm, measure) {
524 var d = cm.display;
525 var sizes = d.scrollbars.update(measure);
526
527 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
528 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
529
530 if (sizes.right && sizes.bottom) {
531 d.scrollbarFiller.style.display = "block";
532 d.scrollbarFiller.style.height = sizes.bottom + "px";
533 d.scrollbarFiller.style.width = sizes.right + "px";
534 } else d.scrollbarFiller.style.display = "";
535 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
536 d.gutterFiller.style.display = "block";
537 d.gutterFiller.style.height = sizes.bottom + "px";
538 d.gutterFiller.style.width = measure.gutterWidth + "px";
539 } else d.gutterFiller.style.display = "";
540 }
541
542 // Compute the lines that are visible in a given viewport (defaults
543 // the the current scroll position). viewport may contain top,
544 // height, and ensure (see op.scrollToPos) properties.
545 function visibleLines(display, doc, viewport) {
546 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
547 top = Math.floor(top - paddingTop(display));
548 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
549
550 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
551 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
552 // forces those lines into the viewport (if possible).
553 if (viewport && viewport.ensure) {
554 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
555 if (ensureFrom < from) {
556 from = ensureFrom;
557 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
558 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
559 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
560 to = ensureTo;
561 }
562 }
563 return {from: from, to: Math.max(to, from + 1)};
564 }
565
566 // LINE NUMBERS
567
568 // Re-align line numbers and gutter marks to compensate for
569 // horizontal scrolling.
570 function alignHorizontally(cm) {
571 var display = cm.display, view = display.view;
572 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
573 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
574 var gutterW = display.gutters.offsetWidth, left = comp + "px";
575 for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
576 if (cm.options.fixedGutter && view[i].gutter)
577 view[i].gutter.style.left = left;
578 var align = view[i].alignable;
579 if (align) for (var j = 0; j < align.length; j++)
580 align[j].style.left = left;
581 }
582 if (cm.options.fixedGutter)
583 display.gutters.style.left = (comp + gutterW) + "px";
584 }
585
586 // Used to ensure that the line number gutter is still the right
587 // size for the current document size. Returns true when an update
588 // is needed.
589 function maybeUpdateLineNumberWidth(cm) {
590 if (!cm.options.lineNumbers) return false;
591 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
592 if (last.length != display.lineNumChars) {
593 var test = display.measure.appendChild(elt("div", [elt("div", last)],
594 "CodeMirror-linenumber CodeMirror-gutter-elt"));
595 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
596 display.lineGutter.style.width = "";
597 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
598 display.lineNumWidth = display.lineNumInnerWidth + padding;
599 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
600 display.lineGutter.style.width = display.lineNumWidth + "px";
601 updateGutterSpace(cm);
602 return true;
603 }
604 return false;
605 }
606
607 function lineNumberFor(options, i) {
608 return String(options.lineNumberFormatter(i + options.firstLineNumber));
609 }
610
611 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
612 // but using getBoundingClientRect to get a sub-pixel-accurate
613 // result.
614 function compensateForHScroll(display) {
615 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
616 }
617
618 // DISPLAY DRAWING
619
620 function DisplayUpdate(cm, viewport, force) {
621 var display = cm.display;
622
623 this.viewport = viewport;
624 // Store some values that we'll need later (but don't want to force a relayout for)
625 this.visible = visibleLines(display, cm.doc, viewport);
626 this.editorIsHidden = !display.wrapper.offsetWidth;
627 this.wrapperHeight = display.wrapper.clientHeight;
628 this.wrapperWidth = display.wrapper.clientWidth;
629 this.oldDisplayWidth = displayWidth(cm);
630 this.force = force;
631 this.dims = getDimensions(cm);
632 this.events = [];
633 }
634
635 DisplayUpdate.prototype.signal = function(emitter, type) {
636 if (hasHandler(emitter, type))
637 this.events.push(arguments);
638 };
639 DisplayUpdate.prototype.finish = function() {
640 for (var i = 0; i < this.events.length; i++)
641 signal.apply(null, this.events[i]);
642 };
643
644 function maybeClipScrollbars(cm) {
645 var display = cm.display;
646 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
647 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
648 display.heightForcer.style.height = scrollGap(cm) + "px";
649 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
650 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
651 display.scrollbarsClipped = true;
652 }
653 }
654
655 // Does the actual updating of the line display. Bails out
656 // (returning false) when there is nothing to be done and forced is
657 // false.
658 function updateDisplayIfNeeded(cm, update) {
659 var display = cm.display, doc = cm.doc;
660
661 if (update.editorIsHidden) {
662 resetView(cm);
663 return false;
664 }
665
666 // Bail out if the visible area is already rendered and nothing changed.
667 if (!update.force &&
668 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
669 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
670 display.renderedView == display.view && countDirtyView(cm) == 0)
671 return false;
672
673 if (maybeUpdateLineNumberWidth(cm)) {
674 resetView(cm);
675 update.dims = getDimensions(cm);
676 }
677
678 // Compute a suitable new viewport (from & to)
679 var end = doc.first + doc.size;
680 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
681 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
682 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
683 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
684 if (sawCollapsedSpans) {
685 from = visualLineNo(cm.doc, from);
686 to = visualLineEndNo(cm.doc, to);
687 }
688
689 var different = from != display.viewFrom || to != display.viewTo ||
690 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
691 adjustView(cm, from, to);
692
693 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
694 // Position the mover div to align with the current scroll position
695 cm.display.mover.style.top = display.viewOffset + "px";
696
697 var toUpdate = countDirtyView(cm);
698 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
699 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
700 return false;
701
702 // For big changes, we hide the enclosing element during the
703 // update, since that speeds up the operations on most browsers.
704 var focused = activeElt();
705 if (toUpdate > 4) display.lineDiv.style.display = "none";
706 patchDisplay(cm, display.updateLineNumbers, update.dims);
707 if (toUpdate > 4) display.lineDiv.style.display = "";
708 display.renderedView = display.view;
709 // There might have been a widget with a focused element that got
710 // hidden or updated, if so re-focus it.
711 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
712
713 // Prevent selection and cursors from interfering with the scroll
714 // width and height.
715 removeChildren(display.cursorDiv);
716 removeChildren(display.selectionDiv);
717 display.gutters.style.height = 0;
718
719 if (different) {
720 display.lastWrapHeight = update.wrapperHeight;
721 display.lastWrapWidth = update.wrapperWidth;
722 startWorker(cm, 400);
723 }
724
725 display.updateLineNumbers = null;
726
727 return true;
728 }
729
730 function postUpdateDisplay(cm, update) {
731 var force = update.force, viewport = update.viewport;
732 for (var first = true;; first = false) {
733 if (first && cm.options.lineWrapping && update.oldDisplayWidth != displayWidth(cm)) {
734 force = true;
735 } else {
736 force = false;
737 // Clip forced viewport to actual scrollable area.
738 if (viewport && viewport.top != null)
739 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
740 // Updated line heights might result in the drawn area not
741 // actually covering the viewport. Keep looping until it does.
742 update.visible = visibleLines(cm.display, cm.doc, viewport);
743 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
744 break;
745 }
746 if (!updateDisplayIfNeeded(cm, update)) break;
747 updateHeightsInViewport(cm);
748 var barMeasure = measureForScrollbars(cm);
749 updateSelection(cm);
750 setDocumentHeight(cm, barMeasure);
751 updateScrollbars(cm, barMeasure);
752 }
753
754 update.signal(cm, "update", cm);
755 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
756 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
757 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
758 }
759 }
760
761 function updateDisplaySimple(cm, viewport) {
762 var update = new DisplayUpdate(cm, viewport);
763 if (updateDisplayIfNeeded(cm, update)) {
764 updateHeightsInViewport(cm);
765 postUpdateDisplay(cm, update);
766 var barMeasure = measureForScrollbars(cm);
767 updateSelection(cm);
768 setDocumentHeight(cm, barMeasure);
769 updateScrollbars(cm, barMeasure);
770 update.finish();
771 }
772 }
773
774 function setDocumentHeight(cm, measure) {
775 cm.display.sizer.style.minHeight = measure.docHeight + "px";
776 var total = measure.docHeight + cm.display.barHeight;
777 cm.display.heightForcer.style.top = total + "px";
778 cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
779 }
780
781 // Read the actual heights of the rendered lines, and update their
782 // stored heights to match.
783 function updateHeightsInViewport(cm) {
784 var display = cm.display;
785 var prevBottom = display.lineDiv.offsetTop;
786 for (var i = 0; i < display.view.length; i++) {
787 var cur = display.view[i], height;
788 if (cur.hidden) continue;
789 if (ie && ie_version < 8) {
790 var bot = cur.node.offsetTop + cur.node.offsetHeight;
791 height = bot - prevBottom;
792 prevBottom = bot;
793 } else {
794 var box = cur.node.getBoundingClientRect();
795 height = box.bottom - box.top;
796 }
797 var diff = cur.line.height - height;
798 if (height < 2) height = textHeight(display);
799 if (diff > .001 || diff < -.001) {
800 updateLineHeight(cur.line, height);
801 updateWidgetHeight(cur.line);
802 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
803 updateWidgetHeight(cur.rest[j]);
804 }
805 }
806 }
807
808 // Read and store the height of line widgets associated with the
809 // given line.
810 function updateWidgetHeight(line) {
811 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
812 line.widgets[i].height = line.widgets[i].node.offsetHeight;
813 }
814
815 // Do a bulk-read of the DOM positions and sizes needed to draw the
816 // view, so that we don't interleave reading and writing to the DOM.
817 function getDimensions(cm) {
818 var d = cm.display, left = {}, width = {};
819 var gutterLeft = d.gutters.clientLeft;
820 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
821 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
822 width[cm.options.gutters[i]] = n.clientWidth;
823 }
824 return {fixedPos: compensateForHScroll(d),
825 gutterTotalWidth: d.gutters.offsetWidth,
826 gutterLeft: left,
827 gutterWidth: width,
828 wrapperWidth: d.wrapper.clientWidth};
829 }
830
831 // Sync the actual display DOM structure with display.view, removing
832 // nodes for lines that are no longer in view, and creating the ones
833 // that are not there yet, and updating the ones that are out of
834 // date.
835 function patchDisplay(cm, updateNumbersFrom, dims) {
836 var display = cm.display, lineNumbers = cm.options.lineNumbers;
837 var container = display.lineDiv, cur = container.firstChild;
838
839 function rm(node) {
840 var next = node.nextSibling;
841 // Works around a throw-scroll bug in OS X Webkit
842 if (webkit && mac && cm.display.currentWheelTarget == node)
843 node.style.display = "none";
844 else
845 node.parentNode.removeChild(node);
846 return next;
847 }
848
849 var view = display.view, lineN = display.viewFrom;
850 // Loop over the elements in the view, syncing cur (the DOM nodes
851 // in display.lineDiv) with the view as we go.
852 for (var i = 0; i < view.length; i++) {
853 var lineView = view[i];
854 if (lineView.hidden) {
855 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
856 var node = buildLineElement(cm, lineView, lineN, dims);
857 container.insertBefore(node, cur);
858 } else { // Already drawn
859 while (cur != lineView.node) cur = rm(cur);
860 var updateNumber = lineNumbers && updateNumbersFrom != null &&
861 updateNumbersFrom <= lineN && lineView.lineNumber;
862 if (lineView.changes) {
863 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
864 updateLineForChanges(cm, lineView, lineN, dims);
865 }
866 if (updateNumber) {
867 removeChildren(lineView.lineNumber);
868 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
869 }
870 cur = lineView.node.nextSibling;
871 }
872 lineN += lineView.size;
873 }
874 while (cur) cur = rm(cur);
875 }
876
877 // When an aspect of a line changes, a string is added to
878 // lineView.changes. This updates the relevant part of the line's
879 // DOM structure.
880 function updateLineForChanges(cm, lineView, lineN, dims) {
881 for (var j = 0; j < lineView.changes.length; j++) {
882 var type = lineView.changes[j];
883 if (type == "text") updateLineText(cm, lineView);
884 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
885 else if (type == "class") updateLineClasses(lineView);
886 else if (type == "widget") updateLineWidgets(cm, lineView, dims);
887 }
888 lineView.changes = null;
889 }
890
891 // Lines with gutter elements, widgets or a background class need to
892 // be wrapped, and have the extra elements added to the wrapper div
893 function ensureLineWrapped(lineView) {
894 if (lineView.node == lineView.text) {
895 lineView.node = elt("div", null, null, "position: relative");
896 if (lineView.text.parentNode)
897 lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
898 lineView.node.appendChild(lineView.text);
899 if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
900 }
901 return lineView.node;
902 }
903
904 function updateLineBackground(lineView) {
905 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
906 if (cls) cls += " CodeMirror-linebackground";
907 if (lineView.background) {
908 if (cls) lineView.background.className = cls;
909 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
910 } else if (cls) {
911 var wrap = ensureLineWrapped(lineView);
912 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
913 }
914 }
915
916 // Wrapper around buildLineContent which will reuse the structure
917 // in display.externalMeasured when possible.
918 function getLineContent(cm, lineView) {
919 var ext = cm.display.externalMeasured;
920 if (ext && ext.line == lineView.line) {
921 cm.display.externalMeasured = null;
922 lineView.measure = ext.measure;
923 return ext.built;
924 }
925 return buildLineContent(cm, lineView);
926 }
927
928 // Redraw the line's text. Interacts with the background and text
929 // classes because the mode may output tokens that influence these
930 // classes.
931 function updateLineText(cm, lineView) {
932 var cls = lineView.text.className;
933 var built = getLineContent(cm, lineView);
934 if (lineView.text == lineView.node) lineView.node = built.pre;
935 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
936 lineView.text = built.pre;
937 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
938 lineView.bgClass = built.bgClass;
939 lineView.textClass = built.textClass;
940 updateLineClasses(lineView);
941 } else if (cls) {
942 lineView.text.className = cls;
943 }
944 }
945
946 function updateLineClasses(lineView) {
947 updateLineBackground(lineView);
948 if (lineView.line.wrapClass)
949 ensureLineWrapped(lineView).className = lineView.line.wrapClass;
950 else if (lineView.node != lineView.text)
951 lineView.node.className = "";
952 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
953 lineView.text.className = textClass || "";
954 }
955
956 function updateLineGutter(cm, lineView, lineN, dims) {
957 if (lineView.gutter) {
958 lineView.node.removeChild(lineView.gutter);
959 lineView.gutter = null;
960 }
961 var markers = lineView.line.gutterMarkers;
962 if (cm.options.lineNumbers || markers) {
963 var wrap = ensureLineWrapped(lineView);
964 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
965 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
966 "px; width: " + dims.gutterTotalWidth + "px");
967 cm.display.input.setUneditable(gutterWrap);
968 wrap.insertBefore(gutterWrap, lineView.text);
969 if (lineView.line.gutterClass)
970 gutterWrap.className += " " + lineView.line.gutterClass;
971 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
972 lineView.lineNumber = gutterWrap.appendChild(
973 elt("div", lineNumberFor(cm.options, lineN),
974 "CodeMirror-linenumber CodeMirror-gutter-elt",
975 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
976 + cm.display.lineNumInnerWidth + "px"));
977 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
978 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
979 if (found)
980 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
981 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
982 }
983 }
984 }
985
986 function updateLineWidgets(cm, lineView, dims) {
987 if (lineView.alignable) lineView.alignable = null;
988 for (var node = lineView.node.firstChild, next; node; node = next) {
989 var next = node.nextSibling;
990 if (node.className == "CodeMirror-linewidget")
991 lineView.node.removeChild(node);
992 }
993 insertLineWidgets(cm, lineView, dims);
994 }
995
996 // Build a line's DOM representation from scratch
997 function buildLineElement(cm, lineView, lineN, dims) {
998 var built = getLineContent(cm, lineView);
999 lineView.text = lineView.node = built.pre;
1000 if (built.bgClass) lineView.bgClass = built.bgClass;
1001 if (built.textClass) lineView.textClass = built.textClass;
1002
1003 updateLineClasses(lineView);
1004 updateLineGutter(cm, lineView, lineN, dims);
1005 insertLineWidgets(cm, lineView, dims);
1006 return lineView.node;
1007 }
1008
1009 // A lineView may contain multiple logical lines (when merged by
1010 // collapsed spans). The widgets for all of them need to be drawn.
1011 function insertLineWidgets(cm, lineView, dims) {
1012 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1013 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1014 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1015 }
1016
1017 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1018 if (!line.widgets) return;
1019 var wrap = ensureLineWrapped(lineView);
1020 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1021 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1022 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
1023 positionLineWidget(widget, node, lineView, dims);
1024 cm.display.input.setUneditable(node);
1025 if (allowAbove && widget.above)
1026 wrap.insertBefore(node, lineView.gutter || lineView.text);
1027 else
1028 wrap.appendChild(node);
1029 signalLater(widget, "redraw");
1030 }
1031 }
1032
1033 function positionLineWidget(widget, node, lineView, dims) {
1034 if (widget.noHScroll) {
1035 (lineView.alignable || (lineView.alignable = [])).push(node);
1036 var width = dims.wrapperWidth;
1037 node.style.left = dims.fixedPos + "px";
1038 if (!widget.coverGutter) {
1039 width -= dims.gutterTotalWidth;
1040 node.style.paddingLeft = dims.gutterTotalWidth + "px";
1041 }
1042 node.style.width = width + "px";
1043 }
1044 if (widget.coverGutter) {
1045 node.style.zIndex = 5;
1046 node.style.position = "relative";
1047 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1048 }
1049 }
1050
1051 // POSITION OBJECT
1052
1053 // A Pos instance represents a position within the text.
1054 var Pos = CodeMirror.Pos = function(line, ch) {
1055 if (!(this instanceof Pos)) return new Pos(line, ch);
1056 this.line = line; this.ch = ch;
1057 };
1058
1059 // Compare two positions, return 0 if they are the same, a negative
1060 // number when a is less, and a positive number otherwise.
1061 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1062
1063 function copyPos(x) {return Pos(x.line, x.ch);}
1064 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1065 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1066
1067 // INPUT HANDLING
1068
1069 function ensureFocus(cm) {
1070 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1071 }
1072
1073 function isReadOnly(cm) {
1074 return cm.options.readOnly || cm.doc.cantEdit;
1075 }
1076
1077 // This will be set to an array of strings when copying, so that,
1078 // when pasting, we know what kind of selections the copied text
1079 // was made out of.
1080 var lastCopied = null;
1081
1082 function applyTextInput(cm, inserted, deleted, sel, origin) {
1083 var doc = cm.doc;
1084 cm.display.shift = false;
1085 if (!sel) sel = doc.sel;
1086
1087 var textLines = splitLines(inserted), multiPaste = null;
1088 // When pasing N lines into N selections, insert one line per selection
1089 if (cm.state.pasteIncoming && sel.ranges.length > 1) {
1090 if (lastCopied && lastCopied.join("\n") == inserted)
1091 multiPaste = sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
1092 else if (textLines.length == sel.ranges.length)
1093 multiPaste = map(textLines, function(l) { return [l]; });
1094 }
1095
1096 // Normal behavior is to insert the new text into every selection
1097 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1098 var range = sel.ranges[i];
1099 var from = range.from(), to = range.to();
1100 if (range.empty()) {
1101 if (deleted && deleted > 0) // Handle deletion
1102 from = Pos(from.line, from.ch - deleted);
1103 else if (cm.state.overwrite && !cm.state.pasteIncoming) // Handle overwrite
1104 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1105 }
1106 var updateInput = cm.curOp.updateInput;
1107 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
1108 origin: origin || (cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
1109 makeChange(cm.doc, changeEvent);
1110 signalLater(cm, "inputRead", cm, changeEvent);
1111 // When an 'electric' character is inserted, immediately trigger a reindent
1112 if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
1113 cm.options.smartIndent && range.head.ch < 100 &&
1114 (!i || sel.ranges[i - 1].head.line != range.head.line)) {
1115 var mode = cm.getModeAt(range.head);
1116 var end = changeEnd(changeEvent);
1117 var indented = false;
1118 if (mode.electricChars) {
1119 for (var j = 0; j < mode.electricChars.length; j++)
1120 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1121 indented = indentLine(cm, end.line, "smart");
1122 break;
1123 }
1124 } else if (mode.electricInput) {
1125 if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
1126 indented = indentLine(cm, end.line, "smart");
1127 }
1128 if (indented) signalLater(cm, "electricInput", cm, end.line);
1129 }
1130 }
1131 ensureCursorVisible(cm);
1132 cm.curOp.updateInput = updateInput;
1133 cm.curOp.typing = true;
1134 cm.state.pasteIncoming = cm.state.cutIncoming = false;
1135 }
1136
1137 function copyableRanges(cm) {
1138 var text = [], ranges = [];
1139 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1140 var line = cm.doc.sel.ranges[i].head.line;
1141 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1142 ranges.push(lineRange);
1143 text.push(cm.getRange(lineRange.anchor, lineRange.head));
1144 }
1145 return {text: text, ranges: ranges};
1146 }
1147
1148 function disableBrowserMagic(field) {
1149 field.setAttribute("autocorrect", "off");
1150 field.setAttribute("autocapitalize", "off");
1151 field.setAttribute("spellcheck", "false");
1152 }
1153
1154 // TEXTAREA INPUT STYLE
1155
1156 function TextareaInput(cm) {
1157 this.cm = cm;
1158 // See input.poll and input.reset
1159 this.prevInput = "";
1160
1161 // Flag that indicates whether we expect input to appear real soon
1162 // now (after some event like 'keypress' or 'input') and are
1163 // polling intensively.
1164 this.pollingFast = false;
1165 // Self-resetting timeout for the poller
1166 this.polling = new Delayed();
1167 // Tracks when input.reset has punted to just putting a short
1168 // string into the textarea instead of the full selection.
1169 this.inaccurateSelection = false;
1170 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
1171 this.hasSelection = false;
1172 this.composing = null;
1173 };
1174
1175 function hiddenTextarea() {
1176 var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
1177 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1178 // The textarea is kept positioned near the cursor to prevent the
1179 // fact that it'll be scrolled into view on input from scrolling
1180 // our fake cursor out of view. On webkit, when wrap=off, paste is
1181 // very slow. So make the area wide instead.
1182 if (webkit) te.style.width = "1000px";
1183 else te.setAttribute("wrap", "off");
1184 // If border: 0; -- iOS fails to open keyboard (issue #1287)
1185 if (ios) te.style.border = "1px solid black";
1186 disableBrowserMagic(te);
1187 return div;
1188 }
1189
1190 TextareaInput.prototype = copyObj({
1191 init: function(display) {
1192 var input = this, cm = this.cm;
1193
1194 // Wraps and hides input textarea
1195 var div = this.wrapper = hiddenTextarea();
1196 // The semihidden textarea that is focused when the editor is
1197 // focused, and receives input.
1198 var te = this.textarea = div.firstChild;
1199 display.wrapper.insertBefore(div, display.wrapper.firstChild);
1200
1201 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1202 if (ios) te.style.width = "0px";
1203
1204 on(te, "input", function() {
1205 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
1206 input.poll();
1207 });
1208
1209 on(te, "paste", function() {
1210 // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
1211 // Add a char to the end of textarea before paste occur so that
1212 // selection doesn't span to the end of textarea.
1213 if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
1214 var start = te.selectionStart, end = te.selectionEnd;
1215 te.value += "$";
1216 // The selection end needs to be set before the start, otherwise there
1217 // can be an intermediate non-empty selection between the two, which
1218 // can override the middle-click paste buffer on linux and cause the
1219 // wrong thing to get pasted.
1220 te.selectionEnd = end;
1221 te.selectionStart = start;
1222 cm.state.fakedLastChar = true;
1223 }
1224 cm.state.pasteIncoming = true;
1225 input.fastPoll();
1226 });
1227
1228 function prepareCopyCut(e) {
1229 if (cm.somethingSelected()) {
1230 lastCopied = cm.getSelections();
1231 if (input.inaccurateSelection) {
1232 input.prevInput = "";
1233 input.inaccurateSelection = false;
1234 te.value = lastCopied.join("\n");
1235 selectInput(te);
1236 }
1237 } else if (!cm.options.lineWiseCopyCut) {
1238 return;
1239 } else {
1240 var ranges = copyableRanges(cm);
1241 lastCopied = ranges.text;
1242 if (e.type == "cut") {
1243 cm.setSelections(ranges.ranges, null, sel_dontScroll);
1244 } else {
1245 input.prevInput = "";
1246 te.value = ranges.text.join("\n");
1247 selectInput(te);
1248 }
1249 }
1250 if (e.type == "cut") cm.state.cutIncoming = true;
1251 }
1252 on(te, "cut", prepareCopyCut);
1253 on(te, "copy", prepareCopyCut);
1254
1255 on(display.scroller, "paste", function(e) {
1256 if (eventInWidget(display, e)) return;
1257 cm.state.pasteIncoming = true;
1258 input.focus();
1259 });
1260
1261 // Prevent normal selection in the editor (we handle our own)
1262 on(display.lineSpace, "selectstart", function(e) {
1263 if (!eventInWidget(display, e)) e_preventDefault(e);
1264 });
1265
1266 on(te, "compositionstart", function() {
1267 var start = cm.getCursor("from");
1268 input.composing = {
1269 start: start,
1270 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
1271 };
1272 });
1273 on(te, "compositionend", function() {
1274 if (input.composing) {
1275 input.poll();
1276 input.composing.range.clear();
1277 input.composing = null;
1278 }
1279 });
1280 },
1281
1282 prepareSelection: function() {
1283 // Redraw the selection and/or cursor
1284 var cm = this.cm, display = cm.display, doc = cm.doc;
1285 var result = prepareSelection(cm);
1286
1287 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1288 if (cm.options.moveInputWithCursor) {
1289 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1290 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1291 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1292 headPos.top + lineOff.top - wrapOff.top));
1293 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1294 headPos.left + lineOff.left - wrapOff.left));
1295 }
1296
1297 return result;
1298 },
1299
1300 showSelection: function(drawn) {
1301 var cm = this.cm, display = cm.display;
1302 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1303 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1304 if (drawn.teTop != null) {
1305 this.wrapper.style.top = drawn.teTop + "px";
1306 this.wrapper.style.left = drawn.teLeft + "px";
1307 }
1308 },
1309
1310 // Reset the input to correspond to the selection (or to be empty,
1311 // when not typing and nothing is selected)
1312 reset: function(typing) {
1313 if (this.contextMenuPending) return;
1314 var minimal, selected, cm = this.cm, doc = cm.doc;
1315 if (cm.somethingSelected()) {
1316 this.prevInput = "";
1317 var range = doc.sel.primary();
1318 minimal = hasCopyEvent &&
1319 (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1320 var content = minimal ? "-" : selected || cm.getSelection();
1321 this.textarea.value = content;
1322 if (cm.state.focused) selectInput(this.textarea);
1323 if (ie && ie_version >= 9) this.hasSelection = content;
1324 } else if (!typing) {
1325 this.prevInput = this.textarea.value = "";
1326 if (ie && ie_version >= 9) this.hasSelection = null;
1327 }
1328 this.inaccurateSelection = minimal;
1329 },
1330
1331 getField: function() { return this.textarea; },
1332
1333 supportsTouch: function() { return false; },
1334
1335 focus: function() {
1336 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
1337 try { this.textarea.focus(); }
1338 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
1339 }
1340 },
1341
1342 blur: function() { this.textarea.blur(); },
1343
1344 resetPosition: function() {
1345 this.wrapper.style.top = this.wrapper.style.left = 0;
1346 },
1347
1348 receivedFocus: function() { this.slowPoll(); },
1349
1350 // Poll for input changes, using the normal rate of polling. This
1351 // runs as long as the editor is focused.
1352 slowPoll: function() {
1353 var input = this;
1354 if (input.pollingFast) return;
1355 input.polling.set(this.cm.options.pollInterval, function() {
1356 input.poll();
1357 if (input.cm.state.focused) input.slowPoll();
1358 });
1359 },
1360
1361 // When an event has just come in that is likely to add or change
1362 // something in the input textarea, we poll faster, to ensure that
1363 // the change appears on the screen quickly.
1364 fastPoll: function() {
1365 var missed = false, input = this;
1366 input.pollingFast = true;
1367 function p() {
1368 var changed = input.poll();
1369 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1370 else {input.pollingFast = false; input.slowPoll();}
1371 }
1372 input.polling.set(20, p);
1373 },
1374
1375 // Read input from the textarea, and update the document to match.
1376 // When something is selected, it is present in the textarea, and
1377 // selected (unless it is huge, in which case a placeholder is
1378 // used). When nothing is selected, the cursor sits after previously
1379 // seen text (can be empty), which is stored in prevInput (we must
1380 // not reset the textarea when typing, because that breaks IME).
1381 poll: function() {
1382 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1383 // Since this is called a *lot*, try to bail out as cheaply as
1384 // possible when it is clear that nothing happened. hasSelection
1385 // will be the case when there is a lot of text in the textarea,
1386 // in which case reading its value would be expensive.
1387 if (!cm.state.focused || (hasSelection(input) && !prevInput) ||
1388 isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
1389 return false;
1390 // See paste handler for more on the fakedLastChar kludge
1391 if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
1392 input.value = input.value.substring(0, input.value.length - 1);
1393 cm.state.fakedLastChar = false;
1394 }
1395 var text = input.value;
1396 // If nothing changed, bail.
1397 if (text == prevInput && !cm.somethingSelected()) return false;
1398 // Work around nonsensical selection resetting in IE9/10, and
1399 // inexplicable appearance of private area unicode characters on
1400 // some key combos in Mac (#2689).
1401 if (ie && ie_version >= 9 && this.hasSelection === text ||
1402 mac && /[\uf700-\uf7ff]/.test(text)) {
1403 cm.display.input.reset();
1404 return false;
1405 }
1406
1407 if (cm.doc.sel == cm.display.selForContextMenu) {
1408 var first = text.charCodeAt(0);
1409 if (first == 0x200b && !prevInput) prevInput = "\u200b";
1410 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1411 }
1412 // Find the part of the input that is actually new
1413 var same = 0, l = Math.min(prevInput.length, text.length);
1414 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1415
1416 var self = this;
1417 runInOp(cm, function() {
1418 applyTextInput(cm, text.slice(same), prevInput.length - same,
1419 null, self.composing ? "*compose" : null);
1420
1421 // Don't leave long text in the textarea, since it makes further polling slow
1422 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
1423 else self.prevInput = text;
1424
1425 if (self.composing) {
1426 self.composing.range.clear();
1427 self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
1428 {className: "CodeMirror-composing"});
1429 }
1430 });
1431 return true;
1432 },
1433
1434 ensurePolled: function() {
1435 if (this.pollingFast && this.poll()) this.pollingFast = false;
1436 },
1437
1438 onKeyPress: function() {
1439 if (ie && ie_version >= 9) this.hasSelection = null;
1440 this.fastPoll();
1441 },
1442
1443 onContextMenu: function(e) {
1444 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
1445 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1446 if (!pos || presto) return; // Opera is difficult.
1447
1448 // Reset the current text selection only if the click is done outside of the selection
1449 // and 'resetSelectionOnContextMenu' option is true.
1450 var reset = cm.options.resetSelectionOnContextMenu;
1451 if (reset && cm.doc.sel.contains(pos) == -1)
1452 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
1453
1454 var oldCSS = te.style.cssText;
1455 input.wrapper.style.position = "absolute";
1456 te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1457 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
1458 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1459 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1460 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
1461 display.input.focus();
1462 if (webkit) window.scrollTo(null, oldScrollY);
1463 display.input.reset();
1464 // Adds "Select all" to context menu in FF
1465 if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1466 input.contextMenuPending = true;
1467 display.selForContextMenu = cm.doc.sel;
1468 clearTimeout(display.detectingSelectAll);
1469
1470 // Select-all will be greyed out if there's nothing to select, so
1471 // this adds a zero-width space so that we can later check whether
1472 // it got selected.
1473 function prepareSelectAllHack() {
1474 if (te.selectionStart != null) {
1475 var selected = cm.somethingSelected();
1476 var extval = "\u200b" + (selected ? te.value : "");
1477 te.value = "\u21da"; // Used to catch context-menu undo
1478 te.value = extval;
1479 input.prevInput = selected ? "" : "\u200b";
1480 te.selectionStart = 1; te.selectionEnd = extval.length;
1481 // Re-set this, in case some other handler touched the
1482 // selection in the meantime.
1483 display.selForContextMenu = cm.doc.sel;
1484 }
1485 }
1486 function rehide() {
1487 input.contextMenuPending = false;
1488 input.wrapper.style.position = "relative";
1489 te.style.cssText = oldCSS;
1490 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
1491
1492 // Try to detect the user choosing select-all
1493 if (te.selectionStart != null) {
1494 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1495 var i = 0, poll = function() {
1496 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
1497 te.selectionEnd > 0 && input.prevInput == "\u200b")
1498 operation(cm, commands.selectAll)(cm);
1499 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
1500 else display.input.reset();
1501 };
1502 display.detectingSelectAll = setTimeout(poll, 200);
1503 }
1504 }
1505
1506 if (ie && ie_version >= 9) prepareSelectAllHack();
1507 if (captureRightClick) {
1508 e_stop(e);
1509 var mouseup = function() {
1510 off(window, "mouseup", mouseup);
1511 setTimeout(rehide, 20);
1512 };
1513 on(window, "mouseup", mouseup);
1514 } else {
1515 setTimeout(rehide, 50);
1516 }
1517 },
1518
1519 setUneditable: nothing,
1520
1521 needsContentAttribute: false
1522 }, TextareaInput.prototype);
1523
1524 // CONTENTEDITABLE INPUT STYLE
1525
1526 function ContentEditableInput(cm) {
1527 this.cm = cm;
1528 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
1529 this.polling = new Delayed();
1530 this.gracePeriod = false;
1531 }
1532
1533 ContentEditableInput.prototype = copyObj({
1534 init: function(display) {
1535 var input = this, cm = input.cm;
1536 var div = input.div = display.lineDiv;
1537 div.contentEditable = "true";
1538 disableBrowserMagic(div);
1539
1540 on(div, "paste", function(e) {
1541 var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1542 if (pasted) {
1543 e.preventDefault();
1544 cm.replaceSelection(pasted, null, "paste");
1545 }
1546 });
1547
1548 on(div, "compositionstart", function(e) {
1549 var data = e.data;
1550 input.composing = {sel: cm.doc.sel, data: data, startData: data};
1551 if (!data) return;
1552 var prim = cm.doc.sel.primary();
1553 var line = cm.getLine(prim.head.line);
1554 var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1555 if (found > -1 && found <= prim.head.ch)
1556 input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1557 Pos(prim.head.line, found + data.length));
1558 });
1559 on(div, "compositionupdate", function(e) {
1560 input.composing.data = e.data;
1561 });
1562 on(div, "compositionend", function(e) {
1563 var ours = input.composing;
1564 if (!ours) return;
1565 if (e.data != ours.startData && !/\u200b/.test(e.data))
1566 ours.data = e.data;
1567 // Need a small delay to prevent other code (input event,
1568 // selection polling) from doing damage when fired right after
1569 // compositionend.
1570 setTimeout(function() {
1571 if (!ours.handled)
1572 input.applyComposition(ours);
1573 if (input.composing == ours)
1574 input.composing = null;
1575 }, 50);
1576 });
1577
1578 on(div, "touchstart", function() {
1579 input.forceCompositionEnd();
1580 });
1581
1582 on(div, "input", function() {
1583 if (input.composing) return;
1584 if (!input.pollContent())
1585 runInOp(input.cm, function() {regChange(cm);});
1586 });
1587
1588 function onCopyCut(e) {
1589 if (cm.somethingSelected()) {
1590 lastCopied = cm.getSelections();
1591 if (e.type == "cut") cm.replaceSelection("", null, "cut");
1592 } else if (!cm.options.lineWiseCopyCut) {
1593 return;
1594 } else {
1595 var ranges = copyableRanges(cm);
1596 lastCopied = ranges.text;
1597 if (e.type == "cut") {
1598 cm.operation(function() {
1599 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1600 cm.replaceSelection("", null, "cut");
1601 });
1602 }
1603 }
1604 // iOS exposes the clipboard API, but seems to discard content inserted into it
1605 if (e.clipboardData && !ios) {
1606 e.preventDefault();
1607 e.clipboardData.clearData();
1608 e.clipboardData.setData("text/plain", lastCopied.join("\n"));
1609 } else {
1610 // Old-fashioned briefly-focus-a-textarea hack
1611 var kludge = hiddenTextarea(), te = kludge.firstChild;
1612 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
1613 te.value = lastCopied.join("\n");
1614 var hadFocus = document.activeElement;
1615 selectInput(te);
1616 setTimeout(function() {
1617 cm.display.lineSpace.removeChild(kludge);
1618 hadFocus.focus();
1619 }, 50);
1620 }
1621 }
1622 on(div, "copy", onCopyCut);
1623 on(div, "cut", onCopyCut);
1624 },
1625
1626 prepareSelection: function() {
1627 var result = prepareSelection(this.cm, false);
1628 result.focus = this.cm.state.focused;
1629 return result;
1630 },
1631
1632 showSelection: function(info) {
1633 if (!info || !this.cm.display.view.length) return;
1634 if (info.focus) this.showPrimarySelection();
1635 this.showMultipleSelections(info);
1636 },
1637
1638 showPrimarySelection: function() {
1639 var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1640 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1641 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1642 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1643 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
1644 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
1645 return;
1646
1647 var start = posToDOM(this.cm, prim.from());
1648 var end = posToDOM(this.cm, prim.to());
1649 if (!start && !end) return;
1650
1651 var view = this.cm.display.view;
1652 var old = sel.rangeCount && sel.getRangeAt(0);
1653 if (!start) {
1654 start = {node: view[0].measure.map[2], offset: 0};
1655 } else if (!end) { // FIXME dangerously hacky
1656 var measure = view[view.length - 1].measure;
1657 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
1658 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
1659 }
1660
1661 try { var rng = range(start.node, start.offset, end.offset, end.node); }
1662 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
1663 if (rng) {
1664 sel.removeAllRanges();
1665 sel.addRange(rng);
1666 if (old && sel.anchorNode == null) sel.addRange(old);
1667 else if (gecko) this.startGracePeriod();
1668 }
1669 this.rememberSelection();
1670 },
1671
1672 startGracePeriod: function() {
1673 var input = this;
1674 clearTimeout(this.gracePeriod);
1675 this.gracePeriod = setTimeout(function() {
1676 input.gracePeriod = false;
1677 if (input.selectionChanged())
1678 input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
1679 }, 20);
1680 },
1681
1682 showMultipleSelections: function(info) {
1683 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1684 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1685 },
1686
1687 rememberSelection: function() {
1688 var sel = window.getSelection();
1689 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
1690 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
1691 },
1692
1693 selectionInEditor: function() {
1694 var sel = window.getSelection();
1695 if (!sel.rangeCount) return false;
1696 var node = sel.getRangeAt(0).commonAncestorContainer;
1697 return contains(this.div, node);
1698 },
1699
1700 focus: function() {
1701 if (this.cm.options.readOnly != "nocursor") this.div.focus();
1702 },
1703 blur: function() { this.div.blur(); },
1704 getField: function() { return this.div; },
1705
1706 supportsTouch: function() { return true; },
1707
1708 receivedFocus: function() {
1709 var input = this;
1710 if (this.selectionInEditor())
1711 this.pollSelection();
1712 else
1713 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
1714
1715 function poll() {
1716 if (input.cm.state.focused) {
1717 input.pollSelection();
1718 input.polling.set(input.cm.options.pollInterval, poll);
1719 }
1720 }
1721 this.polling.set(this.cm.options.pollInterval, poll);
1722 },
1723
1724 selectionChanged: function() {
1725 var sel = window.getSelection();
1726 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
1727 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
1728 },
1729
1730 pollSelection: function() {
1731 if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1732 var sel = window.getSelection(), cm = this.cm;
1733 this.rememberSelection();
1734 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1735 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1736 if (anchor && head) runInOp(cm, function() {
1737 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1738 if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1739 });
1740 }
1741 },
1742
1743 pollContent: function() {
1744 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1745 var from = sel.from(), to = sel.to();
1746 if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
1747
1748 var fromIndex;
1749 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
1750 var fromLine = lineNo(display.view[0].line);
1751 var fromNode = display.view[0].node;
1752 } else {
1753 var fromLine = lineNo(display.view[fromIndex].line);
1754 var fromNode = display.view[fromIndex - 1].node.nextSibling;
1755 }
1756 var toIndex = findViewIndex(cm, to.line);
1757 if (toIndex == display.view.length - 1) {
1758 var toLine = display.viewTo - 1;
1759 var toNode = display.view[toIndex].node;
1760 } else {
1761 var toLine = lineNo(display.view[toIndex + 1].line) - 1;
1762 var toNode = display.view[toIndex + 1].node.previousSibling;
1763 }
1764
1765 var newText = splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
1766 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
1767 while (newText.length > 1 && oldText.length > 1) {
1768 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
1769 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
1770 else break;
1771 }
1772
1773 var cutFront = 0, cutEnd = 0;
1774 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
1775 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
1776 ++cutFront;
1777 var newBot = lst(newText), oldBot = lst(oldText);
1778 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
1779 oldBot.length - (oldText.length == 1 ? cutFront : 0));
1780 while (cutEnd < maxCutEnd &&
1781 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
1782 ++cutEnd;
1783
1784 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1785 newText[0] = newText[0].slice(cutFront);
1786
1787 var chFrom = Pos(fromLine, cutFront);
1788 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1789 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
1790 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1791 return true;
1792 }
1793 },
1794
1795 ensurePolled: function() {
1796 this.forceCompositionEnd();
1797 },
1798 reset: function() {
1799 this.forceCompositionEnd();
1800 },
1801 forceCompositionEnd: function() {
1802 if (!this.composing || this.composing.handled) return;
1803 this.applyComposition(this.composing);
1804 this.composing.handled = true;
1805 this.div.blur();
1806 this.div.focus();
1807 },
1808 applyComposition: function(composing) {
1809 if (composing.data && composing.data != composing.startData)
1810 operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
1811 },
1812
1813 setUneditable: function(node) {
1814 node.setAttribute("contenteditable", "false");
1815 },
1816
1817 onKeyPress: function(e) {
1818 e.preventDefault();
1819 operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
1820 },
1821
1822 onContextMenu: nothing,
1823 resetPosition: nothing,
1824
1825 needsContentAttribute: true
1826 }, ContentEditableInput.prototype);
1827
1828 function posToDOM(cm, pos) {
1829 var view = findViewForLine(cm, pos.line);
1830 if (!view || view.hidden) return null;
1831 var line = getLine(cm.doc, pos.line);
1832 var info = mapFromLineView(view, line, pos.line);
1833
1834 var order = getOrder(line), side = "left";
1835 if (order) {
1836 var partPos = getBidiPartAt(order, pos.ch);
1837 side = partPos % 2 ? "right" : "left";
1838 }
1839 var result = nodeAndOffsetInLineMap(info.map, pos.ch, "left");
1840 result.offset = result.collapse == "right" ? result.end : result.start;
1841 return result;
1842 }
1843
1844 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1845
1846 function domToPos(cm, node, offset) {
1847 var lineNode;
1848 if (node == cm.display.lineDiv) {
1849 lineNode = cm.display.lineDiv.childNodes[offset];
1850 if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
1851 node = null; offset = 0;
1852 } else {
1853 for (lineNode = node;; lineNode = lineNode.parentNode) {
1854 if (!lineNode || lineNode == cm.display.lineDiv) return null;
1855 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
1856 }
1857 }
1858 for (var i = 0; i < cm.display.view.length; i++) {
1859 var lineView = cm.display.view[i];
1860 if (lineView.node == lineNode)
1861 return locateNodeInLineView(lineView, node, offset);
1862 }
1863 }
1864
1865 function locateNodeInLineView(lineView, node, offset) {
1866 var wrapper = lineView.text.firstChild, bad = false;
1867 if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
1868 if (node == wrapper) {
1869 bad = true;
1870 node = wrapper.childNodes[offset];
1871 offset = 0;
1872 if (!node) {
1873 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1874 return badPos(Pos(lineNo(line), line.text.length), bad);
1875 }
1876 }
1877
1878 var textNode = node.nodeType == 3 ? node : null, topNode = node;
1879 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
1880 textNode = node.firstChild;
1881 if (offset) offset = textNode.nodeValue.length;
1882 }
1883 while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1884 var measure = lineView.measure, maps = measure.maps;
1885
1886 function find(textNode, topNode, offset) {
1887 for (var i = -1; i < (maps ? maps.length : 0); i++) {
1888 var map = i < 0 ? measure.map : maps[i];
1889 for (var j = 0; j < map.length; j += 3) {
1890 var curNode = map[j + 2];
1891 if (curNode == textNode || curNode == topNode) {
1892 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1893 var ch = map[j] + offset;
1894 if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
1895 return Pos(line, ch);
1896 }
1897 }
1898 }
1899 }
1900 var found = find(textNode, topNode, offset);
1901 if (found) return badPos(found, bad);
1902
1903 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
1904 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
1905 found = find(after, after.firstChild, 0);
1906 if (found)
1907 return badPos(Pos(found.line, found.ch - dist), bad);
1908 else
1909 dist += after.textContent.length;
1910 }
1911 for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
1912 found = find(before, before.firstChild, -1);
1913 if (found)
1914 return badPos(Pos(found.line, found.ch + dist), bad);
1915 else
1916 dist += after.textContent.length;
1917 }
1918 }
1919
1920 function domTextBetween(cm, from, to, fromLine, toLine) {
1921 var text = "", closing = false;
1922 function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1923 function walk(node) {
1924 if (node.nodeType == 1) {
1925 var cmText = node.getAttribute("cm-text");
1926 if (cmText != null) {
1927 if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1928 text += cmText;
1929 return;
1930 }
1931 var markerID = node.getAttribute("cm-marker"), range;
1932 if (markerID) {
1933 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
1934 if (found.length && (range = found[0].find()))
1935 text += getBetween(cm.doc, range.from, range.to).join("\n");
1936 return;
1937 }
1938 if (node.getAttribute("contenteditable") == "false") return;
1939 for (var i = 0; i < node.childNodes.length; i++)
1940 walk(node.childNodes[i]);
1941 if (/^(pre|div|p)$/i.test(node.nodeName))
1942 closing = true;
1943 } else if (node.nodeType == 3) {
1944 var val = node.nodeValue;
1945 if (!val) return;
1946 if (closing) {
1947 text += "\n";
1948 closing = false;
1949 }
1950 text += val;
1951 }
1952 }
1953 for (;;) {
1954 walk(from);
1955 if (from == to) break;
1956 from = from.nextSibling;
1957 }
1958 return text;
1959 }
1960
1961 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
1962
1963 // SELECTION / CURSOR
1964
1965 // Selection objects are immutable. A new one is created every time
1966 // the selection changes. A selection is one or more non-overlapping
1967 // (and non-touching) ranges, sorted, and an integer that indicates
1968 // which one is the primary selection (the one that's scrolled into
1969 // view, that getCursor returns, etc).
1970 function Selection(ranges, primIndex) {
1971 this.ranges = ranges;
1972 this.primIndex = primIndex;
1973 }
1974
1975 Selection.prototype = {
1976 primary: function() { return this.ranges[this.primIndex]; },
1977 equals: function(other) {
1978 if (other == this) return true;
1979 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
1980 for (var i = 0; i < this.ranges.length; i++) {
1981 var here = this.ranges[i], there = other.ranges[i];
1982 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
1983 }
1984 return true;
1985 },
1986 deepCopy: function() {
1987 for (var out = [], i = 0; i < this.ranges.length; i++)
1988 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
1989 return new Selection(out, this.primIndex);
1990 },
1991 somethingSelected: function() {
1992 for (var i = 0; i < this.ranges.length; i++)
1993 if (!this.ranges[i].empty()) return true;
1994 return false;
1995 },
1996 contains: function(pos, end) {
1997 if (!end) end = pos;
1998 for (var i = 0; i < this.ranges.length; i++) {
1999 var range = this.ranges[i];
2000 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
2001 return i;
2002 }
2003 return -1;
2004 }
2005 };
2006
2007 function Range(anchor, head) {
2008 this.anchor = anchor; this.head = head;
2009 }
2010
2011 Range.prototype = {
2012 from: function() { return minPos(this.anchor, this.head); },
2013 to: function() { return maxPos(this.anchor, this.head); },
2014 empty: function() {
2015 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
2016 }
2017 };
2018
2019 // Take an unsorted, potentially overlapping set of ranges, and
2020 // build a selection out of it. 'Consumes' ranges array (modifying
2021 // it).
2022 function normalizeSelection(ranges, primIndex) {
2023 var prim = ranges[primIndex];
2024 ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
2025 primIndex = indexOf(ranges, prim);
2026 for (var i = 1; i < ranges.length; i++) {
2027 var cur = ranges[i], prev = ranges[i - 1];
2028 if (cmp(prev.to(), cur.from()) >= 0) {
2029 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
2030 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
2031 if (i <= primIndex) --primIndex;
2032 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
2033 }
2034 }
2035 return new Selection(ranges, primIndex);
2036 }
2037
2038 function simpleSelection(anchor, head) {
2039 return new Selection([new Range(anchor, head || anchor)], 0);
2040 }
2041
2042 // Most of the external API clips given positions to make sure they
2043 // actually exist within the document.
2044 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2045 function clipPos(doc, pos) {
2046 if (pos.line < doc.first) return Pos(doc.first, 0);
2047 var last = doc.first + doc.size - 1;
2048 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2049 return clipToLen(pos, getLine(doc, pos.line).text.length);
2050 }
2051 function clipToLen(pos, linelen) {
2052 var ch = pos.ch;
2053 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2054 else if (ch < 0) return Pos(pos.line, 0);
2055 else return pos;
2056 }
2057 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2058 function clipPosArray(doc, array) {
2059 for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
2060 return out;
2061 }
2062
2063 // SELECTION UPDATES
2064
2065 // The 'scroll' parameter given to many of these indicated whether
2066 // the new cursor position should be scrolled into view after
2067 // modifying the selection.
2068
2069 // If shift is held or the extend flag is set, extends a range to
2070 // include a given position (and optionally a second position).
2071 // Otherwise, simply returns the range between the given positions.
2072 // Used for cursor motion and such.
2073 function extendRange(doc, range, head, other) {
2074 if (doc.cm && doc.cm.display.shift || doc.extend) {
2075 var anchor = range.anchor;
2076 if (other) {
2077 var posBefore = cmp(head, anchor) < 0;
2078 if (posBefore != (cmp(other, anchor) < 0)) {
2079 anchor = head;
2080 head = other;
2081 } else if (posBefore != (cmp(head, other) < 0)) {
2082 head = other;
2083 }
2084 }
2085 return new Range(anchor, head);
2086 } else {
2087 return new Range(other || head, head);
2088 }
2089 }
2090
2091 // Extend the primary selection range, discard the rest.
2092 function extendSelection(doc, head, other, options) {
2093 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
2094 }
2095
2096 // Extend all selections (pos is an array of selections with length
2097 // equal the number of selections)
2098 function extendSelections(doc, heads, options) {
2099 for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
2100 out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
2101 var newSel = normalizeSelection(out, doc.sel.primIndex);
2102 setSelection(doc, newSel, options);
2103 }
2104
2105 // Updates a single range in the selection.
2106 function replaceOneSelection(doc, i, range, options) {
2107 var ranges = doc.sel.ranges.slice(0);
2108 ranges[i] = range;
2109 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
2110 }
2111
2112 // Reset the selection to a single range.
2113 function setSimpleSelection(doc, anchor, head, options) {
2114 setSelection(doc, simpleSelection(anchor, head), options);
2115 }
2116
2117 // Give beforeSelectionChange handlers a change to influence a
2118 // selection update.
2119 function filterSelectionChange(doc, sel) {
2120 var obj = {
2121 ranges: sel.ranges,
2122 update: function(ranges) {
2123 this.ranges = [];
2124 for (var i = 0; i < ranges.length; i++)
2125 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
2126 clipPos(doc, ranges[i].head));
2127 }
2128 };
2129 signal(doc, "beforeSelectionChange", doc, obj);
2130 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2131 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
2132 else return sel;
2133 }
2134
2135 function setSelectionReplaceHistory(doc, sel, options) {
2136 var done = doc.history.done, last = lst(done);
2137 if (last && last.ranges) {
2138 done[done.length - 1] = sel;
2139 setSelectionNoUndo(doc, sel, options);
2140 } else {
2141 setSelection(doc, sel, options);
2142 }
2143 }
2144
2145 // Set a new selection.
2146 function setSelection(doc, sel, options) {
2147 setSelectionNoUndo(doc, sel, options);
2148 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
2149 }
2150
2151 function setSelectionNoUndo(doc, sel, options) {
2152 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
2153 sel = filterSelectionChange(doc, sel);
2154
2155 var bias = options && options.bias ||
2156 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
2157 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
2158
2159 if (!(options && options.scroll === false) && doc.cm)
2160 ensureCursorVisible(doc.cm);
2161 }
2162
2163 function setSelectionInner(doc, sel) {
2164 if (sel.equals(doc.sel)) return;
2165
2166 doc.sel = sel;
2167
2168 if (doc.cm) {
2169 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2170 signalCursorActivity(doc.cm);
2171 }
2172 signalLater(doc, "cursorActivity", doc);
2173 }
2174
2175 // Verify that the selection does not partially select any atomic
2176 // marked ranges.
2177 function reCheckSelection(doc) {
2178 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
2179 }
2180
2181 // Return a selection that does not partially select any atomic
2182 // ranges.
2183 function skipAtomicInSelection(doc, sel, bias, mayClear) {
2184 var out;
2185 for (var i = 0; i < sel.ranges.length; i++) {
2186 var range = sel.ranges[i];
2187 var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
2188 var newHead = skipAtomic(doc, range.head, bias, mayClear);
2189 if (out || newAnchor != range.anchor || newHead != range.head) {
2190 if (!out) out = sel.ranges.slice(0, i);
2191 out[i] = new Range(newAnchor, newHead);
2192 }
2193 }
2194 return out ? normalizeSelection(out, sel.primIndex) : sel;
2195 }
2196
2197 // Ensure a given position is not inside an atomic range.
2198 function skipAtomic(doc, pos, bias, mayClear) {
2199 var flipped = false, curPos = pos;
2200 var dir = bias || 1;
2201 doc.cantEdit = false;
2202 search: for (;;) {
2203 var line = getLine(doc, curPos.line);
2204 if (line.markedSpans) {
2205 for (var i = 0; i < line.markedSpans.length; ++i) {
2206 var sp = line.markedSpans[i], m = sp.marker;
2207 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2208 (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2209 if (mayClear) {
2210 signal(m, "beforeCursorEnter");
2211 if (m.explicitlyCleared) {
2212 if (!line.markedSpans) break;
2213 else {--i; continue;}
2214 }
2215 }
2216 if (!m.atomic) continue;
2217 var newPos = m.find(dir < 0 ? -1 : 1);
2218 if (cmp(newPos, curPos) == 0) {
2219 newPos.ch += dir;
2220 if (newPos.ch < 0) {
2221 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2222 else newPos = null;
2223 } else if (newPos.ch > line.text.length) {
2224 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2225 else newPos = null;
2226 }
2227 if (!newPos) {
2228 if (flipped) {
2229 // Driven in a corner -- no valid cursor position found at all
2230 // -- try again *with* clearing, if we didn't already
2231 if (!mayClear) return skipAtomic(doc, pos, bias, true);
2232 // Otherwise, turn off editing until further notice, and return the start of the doc
2233 doc.cantEdit = true;
2234 return Pos(doc.first, 0);
2235 }
2236 flipped = true; newPos = pos; dir = -dir;
2237 }
2238 }
2239 curPos = newPos;
2240 continue search;
2241 }
2242 }
2243 }
2244 return curPos;
2245 }
2246 }
2247
2248 // SELECTION DRAWING
2249
2250 function updateSelection(cm) {
2251 cm.display.input.showSelection(cm.display.input.prepareSelection());
2252 }
2253
2254 function prepareSelection(cm, primary) {
2255 var doc = cm.doc, result = {};
2256 var curFragment = result.cursors = document.createDocumentFragment();
2257 var selFragment = result.selection = document.createDocumentFragment();
2258
2259 for (var i = 0; i < doc.sel.ranges.length; i++) {
2260 if (primary === false && i == doc.sel.primIndex) continue;
2261 var range = doc.sel.ranges[i];
2262 var collapsed = range.empty();
2263 if (collapsed || cm.options.showCursorWhenSelecting)
2264 drawSelectionCursor(cm, range, curFragment);
2265 if (!collapsed)
2266 drawSelectionRange(cm, range, selFragment);
2267 }
2268 return result;
2269 }
2270
2271 // Draws a cursor for the given range
2272 function drawSelectionCursor(cm, range, output) {
2273 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
2274
2275 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2276 cursor.style.left = pos.left + "px";
2277 cursor.style.top = pos.top + "px";
2278 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
2279
2280 if (pos.other) {
2281 // Secondary cursor, shown when on a 'jump' in bi-directional text
2282 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
2283 otherCursor.style.display = "";
2284 otherCursor.style.left = pos.other.left + "px";
2285 otherCursor.style.top = pos.other.top + "px";
2286 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
2287 }
2288 }
2289
2290 // Draws the given range as a highlighted selection
2291 function drawSelectionRange(cm, range, output) {
2292 var display = cm.display, doc = cm.doc;
2293 var fragment = document.createDocumentFragment();
2294 var padding = paddingH(cm.display), leftSide = padding.left;
2295 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
2296
2297 function add(left, top, width, bottom) {
2298 if (top < 0) top = 0;
2299 top = Math.round(top);
2300 bottom = Math.round(bottom);
2301 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
2302 "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
2303 "px; height: " + (bottom - top) + "px"));
2304 }
2305
2306 function drawForLine(line, fromArg, toArg) {
2307 var lineObj = getLine(doc, line);
2308 var lineLen = lineObj.text.length;
2309 var start, end;
2310 function coords(ch, bias) {
2311 return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2312 }
2313
2314 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
2315 var leftPos = coords(from, "left"), rightPos, left, right;
2316 if (from == to) {
2317 rightPos = leftPos;
2318 left = right = leftPos.left;
2319 } else {
2320 rightPos = coords(to - 1, "right");
2321 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
2322 left = leftPos.left;
2323 right = rightPos.right;
2324 }
2325 if (fromArg == null && from == 0) left = leftSide;
2326 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2327 add(left, leftPos.top, null, leftPos.bottom);
2328 left = leftSide;
2329 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
2330 }
2331 if (toArg == null && to == lineLen) right = rightSide;
2332 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2333 start = leftPos;
2334 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2335 end = rightPos;
2336 if (left < leftSide + 1) left = leftSide;
2337 add(left, rightPos.top, right - left, rightPos.bottom);
2338 });
2339 return {start: start, end: end};
2340 }
2341
2342 var sFrom = range.from(), sTo = range.to();
2343 if (sFrom.line == sTo.line) {
2344 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
2345 } else {
2346 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
2347 var singleVLine = visualLine(fromLine) == visualLine(toLine);
2348 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
2349 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
2350 if (singleVLine) {
2351 if (leftEnd.top < rightStart.top - 2) {
2352 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
2353 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
2354 } else {
2355 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
2356 }
2357 }
2358 if (leftEnd.bottom < rightStart.top)
2359 add(leftSide, leftEnd.bottom, null, rightStart.top);
2360 }
2361
2362 output.appendChild(fragment);
2363 }
2364
2365 // Cursor-blinking
2366 function restartBlink(cm) {
2367 if (!cm.state.focused) return;
2368 var display = cm.display;
2369 clearInterval(display.blinker);
2370 var on = true;
2371 display.cursorDiv.style.visibility = "";
2372 if (cm.options.cursorBlinkRate > 0)
2373 display.blinker = setInterval(function() {
2374 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
2375 }, cm.options.cursorBlinkRate);
2376 else if (cm.options.cursorBlinkRate < 0)
2377 display.cursorDiv.style.visibility = "hidden";
2378 }
2379
2380 // HIGHLIGHT WORKER
2381
2382 function startWorker(cm, time) {
2383 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
2384 cm.state.highlight.set(time, bind(highlightWorker, cm));
2385 }
2386
2387 function highlightWorker(cm) {
2388 var doc = cm.doc;
2389 if (doc.frontier < doc.first) doc.frontier = doc.first;
2390 if (doc.frontier >= cm.display.viewTo) return;
2391 var end = +new Date + cm.options.workTime;
2392 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
2393 var changedLines = [];
2394
2395 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
2396 if (doc.frontier >= cm.display.viewFrom) { // Visible
2397 var oldStyles = line.styles;
2398 var highlighted = highlightLine(cm, line, state, true);
2399 line.styles = highlighted.styles;
2400 var oldCls = line.styleClasses, newCls = highlighted.classes;
2401 if (newCls) line.styleClasses = newCls;
2402 else if (oldCls) line.styleClasses = null;
2403 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
2404 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
2405 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
2406 if (ischange) changedLines.push(doc.frontier);
2407 line.stateAfter = copyState(doc.mode, state);
2408 } else {
2409 processLine(cm, line.text, state);
2410 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
2411 }
2412 ++doc.frontier;
2413 if (+new Date > end) {
2414 startWorker(cm, cm.options.workDelay);
2415 return true;
2416 }
2417 });
2418 if (changedLines.length) runInOp(cm, function() {
2419 for (var i = 0; i < changedLines.length; i++)
2420 regLineChange(cm, changedLines[i], "text");
2421 });
2422 }
2423
2424 // Finds the line to start with when starting a parse. Tries to
2425 // find a line with a stateAfter, so that it can start with a
2426 // valid state. If that fails, it returns the line with the
2427 // smallest indentation, which tends to need the least context to
2428 // parse correctly.
2429 function findStartLine(cm, n, precise) {
2430 var minindent, minline, doc = cm.doc;
2431 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2432 for (var search = n; search > lim; --search) {
2433 if (search <= doc.first) return doc.first;
2434 var line = getLine(doc, search - 1);
2435 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
2436 var indented = countColumn(line.text, null, cm.options.tabSize);
2437 if (minline == null || minindent > indented) {
2438 minline = search - 1;
2439 minindent = indented;
2440 }
2441 }
2442 return minline;
2443 }
2444
2445 function getStateBefore(cm, n, precise) {
2446 var doc = cm.doc, display = cm.display;
2447 if (!doc.mode.startState) return true;
2448 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
2449 if (!state) state = startState(doc.mode);
2450 else state = copyState(doc.mode, state);
2451 doc.iter(pos, n, function(line) {
2452 processLine(cm, line.text, state);
2453 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
2454 line.stateAfter = save ? copyState(doc.mode, state) : null;
2455 ++pos;
2456 });
2457 if (precise) doc.frontier = pos;
2458 return state;
2459 }
2460
2461 // POSITION MEASUREMENT
2462
2463 function paddingTop(display) {return display.lineSpace.offsetTop;}
2464 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
2465 function paddingH(display) {
2466 if (display.cachedPaddingH) return display.cachedPaddingH;
2467 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2468 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2469 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2470 if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2471 return data;
2472 }
2473
2474 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2475 function displayWidth(cm) {
2476 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
2477 }
2478 function displayHeight(cm) {
2479 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
2480 }
2481
2482 // Ensure the lineView.wrapping.heights array is populated. This is
2483 // an array of bottom offsets for the lines that make up a drawn
2484 // line. When lineWrapping is on, there might be more than one
2485 // height.
2486 function ensureLineHeights(cm, lineView, rect) {
2487 var wrapping = cm.options.lineWrapping;
2488 var curWidth = wrapping && displayWidth(cm);
2489 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2490 var heights = lineView.measure.heights = [];
2491 if (wrapping) {
2492 lineView.measure.width = curWidth;
2493 var rects = lineView.text.firstChild.getClientRects();
2494 for (var i = 0; i < rects.length - 1; i++) {
2495 var cur = rects[i], next = rects[i + 1];
2496 if (Math.abs(cur.bottom - next.bottom) > 2)
2497 heights.push((cur.bottom + next.top) / 2 - rect.top);
2498 }
2499 }
2500 heights.push(rect.bottom - rect.top);
2501 }
2502 }
2503
2504 // Find a line map (mapping character offsets to text nodes) and a
2505 // measurement cache for the given line number. (A line view might
2506 // contain multiple lines when collapsed ranges are present.)
2507 function mapFromLineView(lineView, line, lineN) {
2508 if (lineView.line == line)
2509 return {map: lineView.measure.map, cache: lineView.measure.cache};
2510 for (var i = 0; i < lineView.rest.length; i++)
2511 if (lineView.rest[i] == line)
2512 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
2513 for (var i = 0; i < lineView.rest.length; i++)
2514 if (lineNo(lineView.rest[i]) > lineN)
2515 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
2516 }
2517
2518 // Render a line into the hidden node display.externalMeasured. Used
2519 // when measurement is needed for a line that's not in the viewport.
2520 function updateExternalMeasurement(cm, line) {
2521 line = visualLine(line);
2522 var lineN = lineNo(line);
2523 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2524 view.lineN = lineN;
2525 var built = view.built = buildLineContent(cm, view);
2526 view.text = built.pre;
2527 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2528 return view;
2529 }
2530
2531 // Get a {top, bottom, left, right} box (in line-local coordinates)
2532 // for a given character.
2533 function measureChar(cm, line, ch, bias) {
2534 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2535 }
2536
2537 // Find a line view that corresponds to the given line number.
2538 function findViewForLine(cm, lineN) {
2539 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2540 return cm.display.view[findViewIndex(cm, lineN)];
2541 var ext = cm.display.externalMeasured;
2542 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2543 return ext;
2544 }
2545
2546 // Measurement can be split in two steps, the set-up work that
2547 // applies to the whole line, and the measurement of the actual
2548 // character. Functions like coordsChar, that need to do a lot of
2549 // measurements in a row, can thus ensure that the set-up work is
2550 // only done once.
2551 function prepareMeasureForLine(cm, line) {
2552 var lineN = lineNo(line);
2553 var view = findViewForLine(cm, lineN);
2554 if (view && !view.text)
2555 view = null;
2556 else if (view && view.changes)
2557 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2558 if (!view)
2559 view = updateExternalMeasurement(cm, line);
2560
2561 var info = mapFromLineView(view, line, lineN);
2562 return {
2563 line: line, view: view, rect: null,
2564 map: info.map, cache: info.cache, before: info.before,
2565 hasHeights: false
2566 };
2567 }
2568
2569 // Given a prepared measurement object, measures the position of an
2570 // actual character (or fetches it from the cache).
2571 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2572 if (prepared.before) ch = -1;
2573 var key = ch + (bias || ""), found;
2574 if (prepared.cache.hasOwnProperty(key)) {
2575 found = prepared.cache[key];
2576 } else {
2577 if (!prepared.rect)
2578 prepared.rect = prepared.view.text.getBoundingClientRect();
2579 if (!prepared.hasHeights) {
2580 ensureLineHeights(cm, prepared.view, prepared.rect);
2581 prepared.hasHeights = true;
2582 }
2583 found = measureCharInner(cm, prepared, ch, bias);
2584 if (!found.bogus) prepared.cache[key] = found;
2585 }
2586 return {left: found.left, right: found.right,
2587 top: varHeight ? found.rtop : found.top,
2588 bottom: varHeight ? found.rbottom : found.bottom};
2589 }
2590
2591 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2592
2593 function nodeAndOffsetInLineMap(map, ch, bias) {
2594 var node, start, end, collapse;
2595 // First, search the line map for the text node corresponding to,
2596 // or closest to, the target character.
2597 for (var i = 0; i < map.length; i += 3) {
2598 var mStart = map[i], mEnd = map[i + 1];
2599 if (ch < mStart) {
2600 start = 0; end = 1;
2601 collapse = "left";
2602 } else if (ch < mEnd) {
2603 start = ch - mStart;
2604 end = start + 1;
2605 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2606 end = mEnd - mStart;
2607 start = end - 1;
2608 if (ch >= mEnd) collapse = "right";
2609 }
2610 if (start != null) {
2611 node = map[i + 2];
2612 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2613 collapse = bias;
2614 if (bias == "left" && start == 0)
2615 while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2616 node = map[(i -= 3) + 2];
2617 collapse = "left";
2618 }
2619 if (bias == "right" && start == mEnd - mStart)
2620 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2621 node = map[(i += 3) + 2];
2622 collapse = "right";
2623 }
2624 break;
2625 }
2626 }
2627 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
2628 }
2629
2630 function measureCharInner(cm, prepared, ch, bias) {
2631 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2632 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2633
2634 var rect;
2635 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2636 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2637 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
2638 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
2639 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
2640 rect = node.parentNode.getBoundingClientRect();
2641 } else if (ie && cm.options.lineWrapping) {
2642 var rects = range(node, start, end).getClientRects();
2643 if (rects.length)
2644 rect = rects[bias == "right" ? rects.length - 1 : 0];
2645 else
2646 rect = nullRect;
2647 } else {
2648 rect = range(node, start, end).getBoundingClientRect() || nullRect;
2649 }
2650 if (rect.left || rect.right || start == 0) break;
2651 end = start;
2652 start = start - 1;
2653 collapse = "right";
2654 }
2655 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
2656 } else { // If it is a widget, simply get the box for the whole widget.
2657 if (start > 0) collapse = bias = "right";
2658 var rects;
2659 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2660 rect = rects[bias == "right" ? rects.length - 1 : 0];
2661 else
2662 rect = node.getBoundingClientRect();
2663 }
2664 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2665 var rSpan = node.parentNode.getClientRects()[0];
2666 if (rSpan)
2667 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
2668 else
2669 rect = nullRect;
2670 }
2671
2672 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2673 var mid = (rtop + rbot) / 2;
2674 var heights = prepared.view.measure.heights;
2675 for (var i = 0; i < heights.length - 1; i++)
2676 if (mid < heights[i]) break;
2677 var top = i ? heights[i - 1] : 0, bot = heights[i];
2678 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2679 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2680 top: top, bottom: bot};
2681 if (!rect.left && !rect.right) result.bogus = true;
2682 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2683
2684 return result;
2685 }
2686
2687 // Work around problem with bounding client rects on ranges being
2688 // returned incorrectly when zoomed on IE10 and below.
2689 function maybeUpdateRectForZooming(measure, rect) {
2690 if (!window.screen || screen.logicalXDPI == null ||
2691 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2692 return rect;
2693 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2694 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2695 return {left: rect.left * scaleX, right: rect.right * scaleX,
2696 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2697 }
2698
2699 function clearLineMeasurementCacheFor(lineView) {
2700 if (lineView.measure) {
2701 lineView.measure.cache = {};
2702 lineView.measure.heights = null;
2703 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2704 lineView.measure.caches[i] = {};
2705 }
2706 }
2707
2708 function clearLineMeasurementCache(cm) {
2709 cm.display.externalMeasure = null;
2710 removeChildren(cm.display.lineMeasure);
2711 for (var i = 0; i < cm.display.view.length; i++)
2712 clearLineMeasurementCacheFor(cm.display.view[i]);
2713 }
2714
2715 function clearCaches(cm) {
2716 clearLineMeasurementCache(cm);
2717 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2718 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2719 cm.display.lineNumChars = null;
2720 }
2721
2722 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
2723 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
2724
2725 // Converts a {top, bottom, left, right} box from line-local
2726 // coordinates into another coordinate system. Context may be one of
2727 // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2728 // or "page".
2729 function intoCoordSystem(cm, lineObj, rect, context) {
2730 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
2731 var size = widgetHeight(lineObj.widgets[i]);
2732 rect.top += size; rect.bottom += size;
2733 }
2734 if (context == "line") return rect;
2735 if (!context) context = "local";
2736 var yOff = heightAtLine(lineObj);
2737 if (context == "local") yOff += paddingTop(cm.display);
2738 else yOff -= cm.display.viewOffset;
2739 if (context == "page" || context == "window") {
2740 var lOff = cm.display.lineSpace.getBoundingClientRect();
2741 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2742 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2743 rect.left += xOff; rect.right += xOff;
2744 }
2745 rect.top += yOff; rect.bottom += yOff;
2746 return rect;
2747 }
2748
2749 // Coverts a box from "div" coords to another coordinate system.
2750 // Context may be "window", "page", "div", or "local"/null.
2751 function fromCoordSystem(cm, coords, context) {
2752 if (context == "div") return coords;
2753 var left = coords.left, top = coords.top;
2754 // First move into "page" coordinate system
2755 if (context == "page") {
2756 left -= pageScrollX();
2757 top -= pageScrollY();
2758 } else if (context == "local" || !context) {
2759 var localBox = cm.display.sizer.getBoundingClientRect();
2760 left += localBox.left;
2761 top += localBox.top;
2762 }
2763
2764 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2765 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2766 }
2767
2768 function charCoords(cm, pos, context, lineObj, bias) {
2769 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2770 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2771 }
2772
2773 // Returns a box for a given cursor position, which may have an
2774 // 'other' property containing the position of the secondary cursor
2775 // on a bidi boundary.
2776 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2777 lineObj = lineObj || getLine(cm.doc, pos.line);
2778 if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2779 function get(ch, right) {
2780 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2781 if (right) m.left = m.right; else m.right = m.left;
2782 return intoCoordSystem(cm, lineObj, m, context);
2783 }
2784 function getBidi(ch, partPos) {
2785 var part = order[partPos], right = part.level % 2;
2786 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2787 part = order[--partPos];
2788 ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2789 right = true;
2790 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2791 part = order[++partPos];
2792 ch = bidiLeft(part) - part.level % 2;
2793 right = false;
2794 }
2795 if (right && ch == part.to && ch > part.from) return get(ch - 1);
2796 return get(ch, right);
2797 }
2798 var order = getOrder(lineObj), ch = pos.ch;
2799 if (!order) return get(ch);
2800 var partPos = getBidiPartAt(order, ch);
2801 var val = getBidi(ch, partPos);
2802 if (bidiOther != null) val.other = getBidi(ch, bidiOther);
2803 return val;
2804 }
2805
2806 // Used to cheaply estimate the coordinates for a position. Used for
2807 // intermediate scroll updates.
2808 function estimateCoords(cm, pos) {
2809 var left = 0, pos = clipPos(cm.doc, pos);
2810 if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2811 var lineObj = getLine(cm.doc, pos.line);
2812 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2813 return {left: left, right: left, top: top, bottom: top + lineObj.height};
2814 }
2815
2816 // Positions returned by coordsChar contain some extra information.
2817 // xRel is the relative x position of the input coordinates compared
2818 // to the found position (so xRel > 0 means the coordinates are to
2819 // the right of the character position, for example). When outside
2820 // is true, that means the coordinates lie outside the line's
2821 // vertical range.
2822 function PosWithInfo(line, ch, outside, xRel) {
2823 var pos = Pos(line, ch);
2824 pos.xRel = xRel;
2825 if (outside) pos.outside = true;
2826 return pos;
2827 }
2828
2829 // Compute the character position closest to the given coordinates.
2830 // Input must be lineSpace-local ("div" coordinate system).
2831 function coordsChar(cm, x, y) {
2832 var doc = cm.doc;
2833 y += cm.display.viewOffset;
2834 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2835 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2836 if (lineN > last)
2837 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
2838 if (x < 0) x = 0;
2839
2840 var lineObj = getLine(doc, lineN);
2841 for (;;) {
2842 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2843 var merged = collapsedSpanAtEnd(lineObj);
2844 var mergedPos = merged && merged.find(0, true);
2845 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2846 lineN = lineNo(lineObj = mergedPos.to.line);
2847 else
2848 return found;
2849 }
2850 }
2851
2852 function coordsCharInner(cm, lineObj, lineNo, x, y) {
2853 var innerOff = y - heightAtLine(lineObj);
2854 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2855 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2856
2857 function getX(ch) {
2858 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
2859 wrongLine = true;
2860 if (innerOff > sp.bottom) return sp.left - adjust;
2861 else if (innerOff < sp.top) return sp.left + adjust;
2862 else wrongLine = false;
2863 return sp.left;
2864 }
2865
2866 var bidi = getOrder(lineObj), dist = lineObj.text.length;
2867 var from = lineLeft(lineObj), to = lineRight(lineObj);
2868 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2869
2870 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2871 // Do a binary search between these bounds.
2872 for (;;) {
2873 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2874 var ch = x < fromX || x - fromX <= toX - x ? from : to;
2875 var xDiff = x - (ch == from ? fromX : toX);
2876 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2877 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
2878 xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2879 return pos;
2880 }
2881 var step = Math.ceil(dist / 2), middle = from + step;
2882 if (bidi) {
2883 middle = from;
2884 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
2885 }
2886 var middleX = getX(middle);
2887 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
2888 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
2889 }
2890 }
2891
2892 var measureText;
2893 // Compute the default text height.
2894 function textHeight(display) {
2895 if (display.cachedTextHeight != null) return display.cachedTextHeight;
2896 if (measureText == null) {
2897 measureText = elt("pre");
2898 // Measure a bunch of lines, for browsers that compute
2899 // fractional heights.
2900 for (var i = 0; i < 49; ++i) {
2901 measureText.appendChild(document.createTextNode("x"));
2902 measureText.appendChild(elt("br"));
2903 }
2904 measureText.appendChild(document.createTextNode("x"));
2905 }
2906 removeChildrenAndAdd(display.measure, measureText);
2907 var height = measureText.offsetHeight / 50;
2908 if (height > 3) display.cachedTextHeight = height;
2909 removeChildren(display.measure);
2910 return height || 1;
2911 }
2912
2913 // Compute the default character width.
2914 function charWidth(display) {
2915 if (display.cachedCharWidth != null) return display.cachedCharWidth;
2916 var anchor = elt("span", "xxxxxxxxxx");
2917 var pre = elt("pre", [anchor]);
2918 removeChildrenAndAdd(display.measure, pre);
2919 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2920 if (width > 2) display.cachedCharWidth = width;
2921 return width || 10;
2922 }
2923
2924 // OPERATIONS
2925
2926 // Operations are used to wrap a series of changes to the editor
2927 // state in such a way that each change won't have to update the
2928 // cursor and display (which would be awkward, slow, and
2929 // error-prone). Instead, display updates are batched and then all
2930 // combined and executed at once.
2931
2932 var operationGroup = null;
2933
2934 var nextOpId = 0;
2935 // Start a new operation.
2936 function startOperation(cm) {
2937 cm.curOp = {
2938 cm: cm,
2939 viewChanged: false, // Flag that indicates that lines might need to be redrawn
2940 startHeight: cm.doc.height, // Used to detect need to update scrollbar
2941 forceUpdate: false, // Used to force a redraw
2942 updateInput: null, // Whether to reset the input textarea
2943 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
2944 changeObjs: null, // Accumulated changes, for firing change events
2945 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
2946 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
2947 selectionChanged: false, // Whether the selection needs to be redrawn
2948 updateMaxLine: false, // Set when the widest line needs to be determined anew
2949 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
2950 scrollToPos: null, // Used to scroll to a specific position
2951 focus: false,
2952 id: ++nextOpId // Unique ID
2953 };
2954 if (operationGroup) {
2955 operationGroup.ops.push(cm.curOp);
2956 } else {
2957 cm.curOp.ownsGroup = operationGroup = {
2958 ops: [cm.curOp],
2959 delayedCallbacks: []
2960 };
2961 }
2962 }
2963
2964 function fireCallbacksForOps(group) {
2965 // Calls delayed callbacks and cursorActivity handlers until no
2966 // new ones appear
2967 var callbacks = group.delayedCallbacks, i = 0;
2968 do {
2969 for (; i < callbacks.length; i++)
2970 callbacks[i]();
2971 for (var j = 0; j < group.ops.length; j++) {
2972 var op = group.ops[j];
2973 if (op.cursorActivityHandlers)
2974 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2975 op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
2976 }
2977 } while (i < callbacks.length);
2978 }
2979
2980 // Finish an operation, updating the display and signalling delayed events
2981 function endOperation(cm) {
2982 var op = cm.curOp, group = op.ownsGroup;
2983 if (!group) return;
2984
2985 try { fireCallbacksForOps(group); }
2986 finally {
2987 operationGroup = null;
2988 for (var i = 0; i < group.ops.length; i++)
2989 group.ops[i].cm.curOp = null;
2990 endOperations(group);
2991 }
2992 }
2993
2994 // The DOM updates done when an operation finishes are batched so
2995 // that the minimum number of relayouts are required.
2996 function endOperations(group) {
2997 var ops = group.ops;
2998 for (var i = 0; i < ops.length; i++) // Read DOM
2999 endOperation_R1(ops[i]);
3000 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3001 endOperation_W1(ops[i]);
3002 for (var i = 0; i < ops.length; i++) // Read DOM
3003 endOperation_R2(ops[i]);
3004 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3005 endOperation_W2(ops[i]);
3006 for (var i = 0; i < ops.length; i++) // Read DOM
3007 endOperation_finish(ops[i]);
3008 }
3009
3010 function endOperation_R1(op) {
3011 var cm = op.cm, display = cm.display;
3012 maybeClipScrollbars(cm);
3013 if (op.updateMaxLine) findMaxLine(cm);
3014
3015 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3016 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3017 op.scrollToPos.to.line >= display.viewTo) ||
3018 display.maxLineChanged && cm.options.lineWrapping;
3019 op.update = op.mustUpdate &&
3020 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3021 }
3022
3023 function endOperation_W1(op) {
3024 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3025 }
3026
3027 function endOperation_R2(op) {
3028 var cm = op.cm, display = cm.display;
3029 if (op.updatedDisplay) updateHeightsInViewport(cm);
3030
3031 op.barMeasure = measureForScrollbars(cm);
3032
3033 // If the max line changed since it was last measured, measure it,
3034 // and ensure the document's width matches it.
3035 // updateDisplay_W2 will use these properties to do the actual resizing
3036 if (display.maxLineChanged && !cm.options.lineWrapping) {
3037 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3038 cm.display.sizerWidth = op.adjustWidthTo;
3039 op.barMeasure.scrollWidth =
3040 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3041 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3042 }
3043
3044 if (op.updatedDisplay || op.selectionChanged)
3045 op.preparedSelection = display.input.prepareSelection();
3046 }
3047
3048 function endOperation_W2(op) {
3049 var cm = op.cm;
3050
3051 if (op.adjustWidthTo != null) {
3052 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3053 if (op.maxScrollLeft < cm.doc.scrollLeft)
3054 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
3055 cm.display.maxLineChanged = false;
3056 }
3057
3058 if (op.preparedSelection)
3059 cm.display.input.showSelection(op.preparedSelection);
3060 if (op.updatedDisplay)
3061 setDocumentHeight(cm, op.barMeasure);
3062 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3063 updateScrollbars(cm, op.barMeasure);
3064
3065 if (op.selectionChanged) restartBlink(cm);
3066
3067 if (cm.state.focused && op.updateInput)
3068 cm.display.input.reset(op.typing);
3069 if (op.focus && op.focus == activeElt()) ensureFocus(op.cm);
3070 }
3071
3072 function endOperation_finish(op) {
3073 var cm = op.cm, display = cm.display, doc = cm.doc;
3074
3075 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3076
3077 // Abort mouse wheel delta measurement, when scrolling explicitly
3078 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3079 display.wheelStartX = display.wheelStartY = null;
3080
3081 // Propagate the scroll position to the actual DOM scroller
3082 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3083 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
3084 display.scrollbars.setScrollTop(doc.scrollTop);
3085 display.scroller.scrollTop = doc.scrollTop;
3086 }
3087 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3088 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
3089 display.scrollbars.setScrollLeft(doc.scrollLeft);
3090 display.scroller.scrollLeft = doc.scrollLeft;
3091 alignHorizontally(cm);
3092 }
3093 // If we need to scroll a specific position into view, do so.
3094 if (op.scrollToPos) {
3095 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3096 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3097 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
3098 }
3099
3100 // Fire events for markers that are hidden/unidden by editing or
3101 // undoing
3102 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3103 if (hidden) for (var i = 0; i < hidden.length; ++i)
3104 if (!hidden[i].lines.length) signal(hidden[i], "hide");
3105 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
3106 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3107
3108 if (display.wrapper.offsetHeight)
3109 doc.scrollTop = cm.display.scroller.scrollTop;
3110
3111 // Fire change events, and delayed event handlers
3112 if (op.changeObjs)
3113 signal(cm, "changes", cm, op.changeObjs);
3114 if (op.update)
3115 op.update.finish();
3116 }
3117
3118 // Run the given function in an operation
3119 function runInOp(cm, f) {
3120 if (cm.curOp) return f();
3121 startOperation(cm);
3122 try { return f(); }
3123 finally { endOperation(cm); }
3124 }
3125 // Wraps a function in an operation. Returns the wrapped function.
3126 function operation(cm, f) {
3127 return function() {
3128 if (cm.curOp) return f.apply(cm, arguments);
3129 startOperation(cm);
3130 try { return f.apply(cm, arguments); }
3131 finally { endOperation(cm); }
3132 };
3133 }
3134 // Used to add methods to editor and doc instances, wrapping them in
3135 // operations.
3136 function methodOp(f) {
3137 return function() {
3138 if (this.curOp) return f.apply(this, arguments);
3139 startOperation(this);
3140 try { return f.apply(this, arguments); }
3141 finally { endOperation(this); }
3142 };
3143 }
3144 function docMethodOp(f) {
3145 return function() {
3146 var cm = this.cm;
3147 if (!cm || cm.curOp) return f.apply(this, arguments);
3148 startOperation(cm);
3149 try { return f.apply(this, arguments); }
3150 finally { endOperation(cm); }
3151 };
3152 }
3153
3154 // VIEW TRACKING
3155
3156 // These objects are used to represent the visible (currently drawn)
3157 // part of the document. A LineView may correspond to multiple
3158 // logical lines, if those are connected by collapsed ranges.
3159 function LineView(doc, line, lineN) {
3160 // The starting line
3161 this.line = line;
3162 // Continuing lines, if any
3163 this.rest = visualLineContinued(line);
3164 // Number of logical lines in this visual line
3165 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3166 this.node = this.text = null;
3167 this.hidden = lineIsHidden(doc, line);
3168 }
3169
3170 // Create a range of LineView objects for the given lines.
3171 function buildViewArray(cm, from, to) {
3172 var array = [], nextPos;
3173 for (var pos = from; pos < to; pos = nextPos) {
3174 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3175 nextPos = pos + view.size;
3176 array.push(view);
3177 }
3178 return array;
3179 }
3180
3181 // Updates the display.view data structure for a given change to the
3182 // document. From and to are in pre-change coordinates. Lendiff is
3183 // the amount of lines added or subtracted by the change. This is
3184 // used for changes that span multiple lines, or change the way
3185 // lines are divided into visual lines. regLineChange (below)
3186 // registers single-line changes.
3187 function regChange(cm, from, to, lendiff) {
3188 if (from == null) from = cm.doc.first;
3189 if (to == null) to = cm.doc.first + cm.doc.size;
3190 if (!lendiff) lendiff = 0;
3191
3192 var display = cm.display;
3193 if (lendiff && to < display.viewTo &&
3194 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3195 display.updateLineNumbers = from;
3196
3197 cm.curOp.viewChanged = true;
3198
3199 if (from >= display.viewTo) { // Change after
3200 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3201 resetView(cm);
3202 } else if (to <= display.viewFrom) { // Change before
3203 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3204 resetView(cm);
3205 } else {
3206 display.viewFrom += lendiff;
3207 display.viewTo += lendiff;
3208 }
3209 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3210 resetView(cm);
3211 } else if (from <= display.viewFrom) { // Top overlap
3212 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3213 if (cut) {
3214 display.view = display.view.slice(cut.index);
3215 display.viewFrom = cut.lineN;
3216 display.viewTo += lendiff;
3217 } else {
3218 resetView(cm);
3219 }
3220 } else if (to >= display.viewTo) { // Bottom overlap
3221 var cut = viewCuttingPoint(cm, from, from, -1);
3222 if (cut) {
3223 display.view = display.view.slice(0, cut.index);
3224 display.viewTo = cut.lineN;
3225 } else {
3226 resetView(cm);
3227 }
3228 } else { // Gap in the middle
3229 var cutTop = viewCuttingPoint(cm, from, from, -1);
3230 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3231 if (cutTop && cutBot) {
3232 display.view = display.view.slice(0, cutTop.index)
3233 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3234 .concat(display.view.slice(cutBot.index));
3235 display.viewTo += lendiff;
3236 } else {
3237 resetView(cm);
3238 }
3239 }
3240
3241 var ext = display.externalMeasured;
3242 if (ext) {
3243 if (to < ext.lineN)
3244 ext.lineN += lendiff;
3245 else if (from < ext.lineN + ext.size)
3246 display.externalMeasured = null;
3247 }
3248 }
3249
3250 // Register a change to a single line. Type must be one of "text",
3251 // "gutter", "class", "widget"
3252 function regLineChange(cm, line, type) {
3253 cm.curOp.viewChanged = true;
3254 var display = cm.display, ext = cm.display.externalMeasured;
3255 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3256 display.externalMeasured = null;
3257
3258 if (line < display.viewFrom || line >= display.viewTo) return;
3259 var lineView = display.view[findViewIndex(cm, line)];
3260 if (lineView.node == null) return;
3261 var arr = lineView.changes || (lineView.changes = []);
3262 if (indexOf(arr, type) == -1) arr.push(type);
3263 }
3264
3265 // Clear the view.
3266 function resetView(cm) {
3267 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3268 cm.display.view = [];
3269 cm.display.viewOffset = 0;
3270 }
3271
3272 // Find the view element corresponding to a given line. Return null
3273 // when the line isn't visible.
3274 function findViewIndex(cm, n) {
3275 if (n >= cm.display.viewTo) return null;
3276 n -= cm.display.viewFrom;
3277 if (n < 0) return null;
3278 var view = cm.display.view;
3279 for (var i = 0; i < view.length; i++) {
3280 n -= view[i].size;
3281 if (n < 0) return i;
3282 }
3283 }
3284
3285 function viewCuttingPoint(cm, oldN, newN, dir) {
3286 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3287 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3288 return {index: index, lineN: newN};
3289 for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3290 n += view[i].size;
3291 if (n != oldN) {
3292 if (dir > 0) {
3293 if (index == view.length - 1) return null;
3294 diff = (n + view[index].size) - oldN;
3295 index++;
3296 } else {
3297 diff = n - oldN;
3298 }
3299 oldN += diff; newN += diff;
3300 }
3301 while (visualLineNo(cm.doc, newN) != newN) {
3302 if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3303 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3304 index += dir;
3305 }
3306 return {index: index, lineN: newN};
3307 }
3308
3309 // Force the view to cover a given range, adding empty view element
3310 // or clipping off existing ones as needed.
3311 function adjustView(cm, from, to) {
3312 var display = cm.display, view = display.view;
3313 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3314 display.view = buildViewArray(cm, from, to);
3315 display.viewFrom = from;
3316 } else {
3317 if (display.viewFrom > from)
3318 display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
3319 else if (display.viewFrom < from)
3320 display.view = display.view.slice(findViewIndex(cm, from));
3321 display.viewFrom = from;
3322 if (display.viewTo < to)
3323 display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
3324 else if (display.viewTo > to)
3325 display.view = display.view.slice(0, findViewIndex(cm, to));
3326 }
3327 display.viewTo = to;
3328 }
3329
3330 // Count the number of lines in the view whose DOM representation is
3331 // out of date (or nonexistent).
3332 function countDirtyView(cm) {
3333 var view = cm.display.view, dirty = 0;
3334 for (var i = 0; i < view.length; i++) {
3335 var lineView = view[i];
3336 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3337 }
3338 return dirty;
3339 }
3340
3341 // EVENT HANDLERS
3342
3343 // Attach the necessary event handlers when initializing the editor
3344 function registerEventHandlers(cm) {
3345 var d = cm.display;
3346 on(d.scroller, "mousedown", operation(cm, onMouseDown));
3347 // Older IE's will not fire a second mousedown for a double click
3348 if (ie && ie_version < 11)
3349 on(d.scroller, "dblclick", operation(cm, function(e) {
3350 if (signalDOMEvent(cm, e)) return;
3351 var pos = posFromMouse(cm, e);
3352 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
3353 e_preventDefault(e);
3354 var word = cm.findWordAt(pos);
3355 extendSelection(cm.doc, word.anchor, word.head);
3356 }));
3357 else
3358 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
3359 // Some browsers fire contextmenu *after* opening the menu, at
3360 // which point we can't mess with it anymore. Context menu is
3361 // handled in onMouseDown for these browsers.
3362 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
3363
3364 // Used to suppress mouse event handling when a touch happens
3365 var touchFinished, prevTouch = {end: 0};
3366 function finishTouch() {
3367 if (d.activeTouch) {
3368 touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3369 prevTouch = d.activeTouch;
3370 prevTouch.end = +new Date;
3371 }
3372 };
3373 function isMouseLikeTouchEvent(e) {
3374 if (e.touches.length != 1) return false;
3375 var touch = e.touches[0];
3376 return touch.radiusX <= 1 && touch.radiusY <= 1;
3377 }
3378 function farAway(touch, other) {
3379 if (other.left == null) return true;
3380 var dx = other.left - touch.left, dy = other.top - touch.top;
3381 return dx * dx + dy * dy > 20 * 20;
3382 }
3383 on(d.scroller, "touchstart", function(e) {
3384 if (!isMouseLikeTouchEvent(e)) {
3385 clearTimeout(touchFinished);
3386 var now = +new Date;
3387 d.activeTouch = {start: now, moved: false,
3388 prev: now - prevTouch.end <= 300 ? prevTouch : null};
3389 if (e.touches.length == 1) {
3390 d.activeTouch.left = e.touches[0].pageX;
3391 d.activeTouch.top = e.touches[0].pageY;
3392 }
3393 }
3394 });
3395 on(d.scroller, "touchmove", function() {
3396 if (d.activeTouch) d.activeTouch.moved = true;
3397 });
3398 on(d.scroller, "touchend", function(e) {
3399 var touch = d.activeTouch;
3400 if (touch && !eventInWidget(d, e) && touch.left != null &&
3401 !touch.moved && new Date - touch.start < 300) {
3402 var pos = cm.coordsChar(d.activeTouch, "page"), range;
3403 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3404 range = new Range(pos, pos);
3405 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3406 range = cm.findWordAt(pos);
3407 else // Triple tap
3408 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3409 cm.setSelection(range.anchor, range.head);
3410 cm.focus();
3411 e_preventDefault(e);
3412 }
3413 finishTouch();
3414 });
3415 on(d.scroller, "touchcancel", finishTouch);
3416
3417 // Sync scrolling between fake scrollbars and real scrollable
3418 // area, ensure viewport is updated when scrolling.
3419 on(d.scroller, "scroll", function() {
3420 if (d.scroller.clientHeight) {
3421 setScrollTop(cm, d.scroller.scrollTop);
3422 setScrollLeft(cm, d.scroller.scrollLeft, true);
3423 signal(cm, "scroll", cm);
3424 }
3425 });
3426
3427 // Listen to wheel events in order to try and update the viewport on time.
3428 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3429 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3430
3431 // Prevent wrapper from ever scrolling
3432 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
3433
3434 d.dragFunctions = {
3435 simple: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
3436 start: function(e){onDragStart(cm, e);},
3437 drop: operation(cm, onDrop)
3438 };
3439
3440 var inp = d.input.getField();
3441 on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3442 on(inp, "keydown", operation(cm, onKeyDown));
3443 on(inp, "keypress", operation(cm, onKeyPress));
3444 on(inp, "focus", bind(onFocus, cm));
3445 on(inp, "blur", bind(onBlur, cm));
3446 }
3447
3448 function dragDropChanged(cm, value, old) {
3449 var wasOn = old && old != CodeMirror.Init;
3450 if (!value != !wasOn) {
3451 var funcs = cm.display.dragFunctions;
3452 var toggle = value ? on : off;
3453 toggle(cm.display.scroller, "dragstart", funcs.start);
3454 toggle(cm.display.scroller, "dragenter", funcs.simple);
3455 toggle(cm.display.scroller, "dragover", funcs.simple);
3456 toggle(cm.display.scroller, "drop", funcs.drop);
3457 }
3458 }
3459
3460 // Called when the window resizes
3461 function onResize(cm) {
3462 var d = cm.display;
3463 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
3464 return;
3465 // Might be a text scaling operation, clear size caches.
3466 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3467 d.scrollbarsClipped = false;
3468 cm.setSize();
3469 }
3470
3471 // MOUSE EVENTS
3472
3473 // Return true when the given mouse event happened in a widget
3474 function eventInWidget(display, e) {
3475 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3476 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3477 (n.parentNode == display.sizer && n != display.mover))
3478 return true;
3479 }
3480 }
3481
3482 // Given a mouse event, find the corresponding position. If liberal
3483 // is false, it checks whether a gutter or scrollbar was clicked,
3484 // and returns null if it was. forRect is used by rectangular
3485 // selections, and tries to estimate a character position even for
3486 // coordinates beyond the right of the text.
3487 function posFromMouse(cm, e, liberal, forRect) {
3488 var display = cm.display;
3489 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3490
3491 var x, y, space = display.lineSpace.getBoundingClientRect();
3492 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3493 try { x = e.clientX - space.left; y = e.clientY - space.top; }
3494 catch (e) { return null; }
3495 var coords = coordsChar(cm, x, y), line;
3496 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3497 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3498 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3499 }
3500 return coords;
3501 }
3502
3503 // A mouse down can be a single click, double click, triple click,
3504 // start of selection drag, start of text drag, new cursor
3505 // (ctrl-click), rectangle drag (alt-drag), or xwin
3506 // middle-click-paste. Or it might be a click on something we should
3507 // not interfere with, such as a scrollbar or widget.
3508 function onMouseDown(e) {
3509 var cm = this, display = cm.display;
3510 if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
3511 display.shift = e.shiftKey;
3512
3513 if (eventInWidget(display, e)) {
3514 if (!webkit) {
3515 // Briefly turn off draggability, to allow widgets to do
3516 // normal dragging things.
3517 display.scroller.draggable = false;
3518 setTimeout(function(){display.scroller.draggable = true;}, 100);
3519 }
3520 return;
3521 }
3522 if (clickInGutter(cm, e)) return;
3523 var start = posFromMouse(cm, e);
3524 window.focus();
3525
3526 switch (e_button(e)) {
3527 case 1:
3528 if (start)
3529 leftButtonDown(cm, e, start);
3530 else if (e_target(e) == display.scroller)
3531 e_preventDefault(e);
3532 break;
3533 case 2:
3534 if (webkit) cm.state.lastMiddleDown = +new Date;
3535 if (start) extendSelection(cm.doc, start);
3536 setTimeout(function() {display.input.focus();}, 20);
3537 e_preventDefault(e);
3538 break;
3539 case 3:
3540 if (captureRightClick) onContextMenu(cm, e);
3541 else delayBlurEvent(cm);
3542 break;
3543 }
3544 }
3545
3546 var lastClick, lastDoubleClick;
3547 function leftButtonDown(cm, e, start) {
3548 if (ie) setTimeout(bind(ensureFocus, cm), 0);
3549 else cm.curOp.focus = activeElt();
3550
3551 var now = +new Date, type;
3552 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
3553 type = "triple";
3554 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
3555 type = "double";
3556 lastDoubleClick = {time: now, pos: start};
3557 } else {
3558 type = "single";
3559 lastClick = {time: now, pos: start};
3560 }
3561
3562 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3563 if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
3564 type == "single" && (contained = sel.contains(start)) > -1 &&
3565 !sel.ranges[contained].empty())
3566 leftButtonStartDrag(cm, e, start, modifier);
3567 else
3568 leftButtonSelect(cm, e, start, type, modifier);
3569 }
3570
3571 // Start a text drag. When it ends, see if any dragging actually
3572 // happen, and treat as a click if it didn't.
3573 function leftButtonStartDrag(cm, e, start, modifier) {
3574 var display = cm.display, startTime = +new Date;
3575 var dragEnd = operation(cm, function(e2) {
3576 if (webkit) display.scroller.draggable = false;
3577 cm.state.draggingText = false;
3578 off(document, "mouseup", dragEnd);
3579 off(display.scroller, "drop", dragEnd);
3580 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3581 e_preventDefault(e2);
3582 if (!modifier && +new Date - 200 < startTime)
3583 extendSelection(cm.doc, start);
3584 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
3585 if (webkit || ie && ie_version == 9)
3586 setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3587 else
3588 display.input.focus();
3589 }
3590 });
3591 // Let the drag handler handle this.
3592 if (webkit) display.scroller.draggable = true;
3593 cm.state.draggingText = dragEnd;
3594 // IE's approach to draggable
3595 if (display.scroller.dragDrop) display.scroller.dragDrop();
3596 on(document, "mouseup", dragEnd);
3597 on(display.scroller, "drop", dragEnd);
3598 }
3599
3600 // Normal selection, as opposed to text dragging.
3601 function leftButtonSelect(cm, e, start, type, addNew) {
3602 var display = cm.display, doc = cm.doc;
3603 e_preventDefault(e);
3604
3605 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3606 if (addNew && !e.shiftKey) {
3607 ourIndex = doc.sel.contains(start);
3608 if (ourIndex > -1)
3609 ourRange = ranges[ourIndex];
3610 else
3611 ourRange = new Range(start, start);
3612 } else {
3613 ourRange = doc.sel.primary();
3614 ourIndex = doc.sel.primIndex;
3615 }
3616
3617 if (e.altKey) {
3618 type = "rect";
3619 if (!addNew) ourRange = new Range(start, start);
3620 start = posFromMouse(cm, e, true, true);
3621 ourIndex = -1;
3622 } else if (type == "double") {
3623 var word = cm.findWordAt(start);
3624 if (cm.display.shift || doc.extend)
3625 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3626 else
3627 ourRange = word;
3628 } else if (type == "triple") {
3629 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3630 if (cm.display.shift || doc.extend)
3631 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3632 else
3633 ourRange = line;
3634 } else {
3635 ourRange = extendRange(doc, ourRange, start);
3636 }
3637
3638 if (!addNew) {
3639 ourIndex = 0;
3640 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3641 startSel = doc.sel;
3642 } else if (ourIndex == -1) {
3643 ourIndex = ranges.length;
3644 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3645 {scroll: false, origin: "*mouse"});
3646 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
3647 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));
3648 startSel = doc.sel;
3649 } else {
3650 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3651 }
3652
3653 var lastPos = start;
3654 function extendTo(pos) {
3655 if (cmp(lastPos, pos) == 0) return;
3656 lastPos = pos;
3657
3658 if (type == "rect") {
3659 var ranges = [], tabSize = cm.options.tabSize;
3660 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
3661 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3662 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
3663 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
3664 line <= end; line++) {
3665 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
3666 if (left == right)
3667 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3668 else if (text.length > leftPos)
3669 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3670 }
3671 if (!ranges.length) ranges.push(new Range(start, start));
3672 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
3673 {origin: "*mouse", scroll: false});
3674 cm.scrollIntoView(pos);
3675 } else {
3676 var oldRange = ourRange;
3677 var anchor = oldRange.anchor, head = pos;
3678 if (type != "single") {
3679 if (type == "double")
3680 var range = cm.findWordAt(pos);
3681 else
3682 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
3683 if (cmp(range.anchor, anchor) > 0) {
3684 head = range.head;
3685 anchor = minPos(oldRange.from(), range.anchor);
3686 } else {
3687 head = range.anchor;
3688 anchor = maxPos(oldRange.to(), range.head);
3689 }
3690 }
3691 var ranges = startSel.ranges.slice(0);
3692 ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3693 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3694 }
3695 }
3696
3697 var editorSize = display.wrapper.getBoundingClientRect();
3698 // Used to ensure timeout re-tries don't fire when another extend
3699 // happened in the meantime (clearTimeout isn't reliable -- at
3700 // least on Chrome, the timeouts still happen even when cleared,
3701 // if the clear happens after their scheduled firing time).
3702 var counter = 0;
3703
3704 function extend(e) {
3705 var curCount = ++counter;
3706 var cur = posFromMouse(cm, e, true, type == "rect");
3707 if (!cur) return;
3708 if (cmp(cur, lastPos) != 0) {
3709 cm.curOp.focus = activeElt();
3710 extendTo(cur);
3711 var visible = visibleLines(display, doc);
3712 if (cur.line >= visible.to || cur.line < visible.from)
3713 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
3714 } else {
3715 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
3716 if (outside) setTimeout(operation(cm, function() {
3717 if (counter != curCount) return;
3718 display.scroller.scrollTop += outside;
3719 extend(e);
3720 }), 50);
3721 }
3722 }
3723
3724 function done(e) {
3725 counter = Infinity;
3726 e_preventDefault(e);
3727 display.input.focus();
3728 off(document, "mousemove", move);
3729 off(document, "mouseup", up);
3730 doc.history.lastSelOrigin = null;
3731 }
3732
3733 var move = operation(cm, function(e) {
3734 if (!e_button(e)) done(e);
3735 else extend(e);
3736 });
3737 var up = operation(cm, done);
3738 on(document, "mousemove", move);
3739 on(document, "mouseup", up);
3740 }
3741
3742 // Determines whether an event happened in the gutter, and fires the
3743 // handlers for the corresponding event.
3744 function gutterEvent(cm, e, type, prevent, signalfn) {
3745 try { var mX = e.clientX, mY = e.clientY; }
3746 catch(e) { return false; }
3747 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
3748 if (prevent) e_preventDefault(e);
3749
3750 var display = cm.display;
3751 var lineBox = display.lineDiv.getBoundingClientRect();
3752
3753 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
3754 mY -= lineBox.top - display.viewOffset;
3755
3756 for (var i = 0; i < cm.options.gutters.length; ++i) {
3757 var g = display.gutters.childNodes[i];
3758 if (g && g.getBoundingClientRect().right >= mX) {
3759 var line = lineAtHeight(cm.doc, mY);
3760 var gutter = cm.options.gutters[i];
3761 signalfn(cm, type, cm, line, gutter, e);
3762 return e_defaultPrevented(e);
3763 }
3764 }
3765 }
3766
3767 function clickInGutter(cm, e) {
3768 return gutterEvent(cm, e, "gutterClick", true, signalLater);
3769 }
3770
3771 // Kludge to work around strange IE behavior where it'll sometimes
3772 // re-fire a series of drag-related events right after the drop (#1551)
3773 var lastDrop = 0;
3774
3775 function onDrop(e) {
3776 var cm = this;
3777 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3778 return;
3779 e_preventDefault(e);
3780 if (ie) lastDrop = +new Date;
3781 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3782 if (!pos || isReadOnly(cm)) return;
3783 // Might be a file drop, in which case we simply extract the text
3784 // and insert it.
3785 if (files && files.length && window.FileReader && window.File) {
3786 var n = files.length, text = Array(n), read = 0;
3787 var loadFile = function(file, i) {
3788 var reader = new FileReader;
3789 reader.onload = operation(cm, function() {
3790 text[i] = reader.result;
3791 if (++read == n) {
3792 pos = clipPos(cm.doc, pos);
3793 var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"};
3794 makeChange(cm.doc, change);
3795 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
3796 }
3797 });
3798 reader.readAsText(file);
3799 };
3800 for (var i = 0; i < n; ++i) loadFile(files[i], i);
3801 } else { // Normal drop
3802 // Don't do a replace if the drop happened inside of the selected text.
3803 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3804 cm.state.draggingText(e);
3805 // Ensure the editor is re-focused
3806 setTimeout(function() {cm.display.input.focus();}, 20);
3807 return;
3808 }
3809 try {
3810 var text = e.dataTransfer.getData("Text");
3811 if (text) {
3812 if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
3813 var selected = cm.listSelections();
3814 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3815 if (selected) for (var i = 0; i < selected.length; ++i)
3816 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3817 cm.replaceSelection(text, "around", "paste");
3818 cm.display.input.focus();
3819 }
3820 }
3821 catch(e){}
3822 }
3823 }
3824
3825 function onDragStart(cm, e) {
3826 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3827 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3828
3829 e.dataTransfer.setData("Text", cm.getSelection());
3830
3831 // Use dummy image instead of default browsers image.
3832 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3833 if (e.dataTransfer.setDragImage && !safari) {
3834 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3835 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3836 if (presto) {
3837 img.width = img.height = 1;
3838 cm.display.wrapper.appendChild(img);
3839 // Force a relayout, or Opera won't use our image for some obscure reason
3840 img._top = img.offsetTop;
3841 }
3842 e.dataTransfer.setDragImage(img, 0, 0);
3843 if (presto) img.parentNode.removeChild(img);
3844 }
3845 }
3846
3847 // SCROLL EVENTS
3848
3849 // Sync the scrollable area and scrollbars, ensure the viewport
3850 // covers the visible area.
3851 function setScrollTop(cm, val) {
3852 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3853 cm.doc.scrollTop = val;
3854 if (!gecko) updateDisplaySimple(cm, {top: val});
3855 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3856 cm.display.scrollbars.setScrollTop(val);
3857 if (gecko) updateDisplaySimple(cm);
3858 startWorker(cm, 100);
3859 }
3860 // Sync scroller and scrollbar, ensure the gutter elements are
3861 // aligned.
3862 function setScrollLeft(cm, val, isScroller) {
3863 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3864 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3865 cm.doc.scrollLeft = val;
3866 alignHorizontally(cm);
3867 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3868 cm.display.scrollbars.setScrollLeft(val);
3869 }
3870
3871 // Since the delta values reported on mouse wheel events are
3872 // unstandardized between browsers and even browser versions, and
3873 // generally horribly unpredictable, this code starts by measuring
3874 // the scroll effect that the first few mouse wheel events have,
3875 // and, from that, detects the way it can convert deltas to pixel
3876 // offsets afterwards.
3877 //
3878 // The reason we want to know the amount a wheel event will scroll
3879 // is that it gives us a chance to update the display before the
3880 // actual scrolling happens, reducing flickering.
3881
3882 var wheelSamples = 0, wheelPixelsPerUnit = null;
3883 // Fill in a browser-detected starting value on browsers where we
3884 // know one. These don't have to be accurate -- the result of them
3885 // being wrong would just be a slight flicker on the first wheel
3886 // scroll (if it is large enough).
3887 if (ie) wheelPixelsPerUnit = -.53;
3888 else if (gecko) wheelPixelsPerUnit = 15;
3889 else if (chrome) wheelPixelsPerUnit = -.7;
3890 else if (safari) wheelPixelsPerUnit = -1/3;
3891
3892 var wheelEventDelta = function(e) {
3893 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
3894 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
3895 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
3896 else if (dy == null) dy = e.wheelDelta;
3897 return {x: dx, y: dy};
3898 };
3899 CodeMirror.wheelEventPixels = function(e) {
3900 var delta = wheelEventDelta(e);
3901 delta.x *= wheelPixelsPerUnit;
3902 delta.y *= wheelPixelsPerUnit;
3903 return delta;
3904 };
3905
3906 function onScrollWheel(cm, e) {
3907 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
3908
3909 var display = cm.display, scroll = display.scroller;
3910 // Quit if there's nothing to scroll here
3911 if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
3912 dy && scroll.scrollHeight > scroll.clientHeight)) return;
3913
3914 // Webkit browsers on OS X abort momentum scrolls when the target
3915 // of the scroll event is removed from the scrollable element.
3916 // This hack (see related code in patchDisplay) makes sure the
3917 // element is kept around.
3918 if (dy && mac && webkit) {
3919 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3920 for (var i = 0; i < view.length; i++) {
3921 if (view[i].node == cur) {
3922 cm.display.currentWheelTarget = cur;
3923 break outer;
3924 }
3925 }
3926 }
3927 }
3928
3929 // On some browsers, horizontal scrolling will cause redraws to
3930 // happen before the gutter has been realigned, causing it to
3931 // wriggle around in a most unseemly way. When we have an
3932 // estimated pixels/delta value, we just handle horizontal
3933 // scrolling entirely here. It'll be slightly off from native, but
3934 // better than glitching out.
3935 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3936 if (dy)
3937 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
3938 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
3939 e_preventDefault(e);
3940 display.wheelStartX = null; // Abort measurement, if in progress
3941 return;
3942 }
3943
3944 // 'Project' the visible viewport to cover the area that is being
3945 // scrolled into view (if we know enough to estimate it).
3946 if (dy && wheelPixelsPerUnit != null) {
3947 var pixels = dy * wheelPixelsPerUnit;
3948 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
3949 if (pixels < 0) top = Math.max(0, top + pixels - 50);
3950 else bot = Math.min(cm.doc.height, bot + pixels + 50);
3951 updateDisplaySimple(cm, {top: top, bottom: bot});
3952 }
3953
3954 if (wheelSamples < 20) {
3955 if (display.wheelStartX == null) {
3956 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
3957 display.wheelDX = dx; display.wheelDY = dy;
3958 setTimeout(function() {
3959 if (display.wheelStartX == null) return;
3960 var movedX = scroll.scrollLeft - display.wheelStartX;
3961 var movedY = scroll.scrollTop - display.wheelStartY;
3962 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3963 (movedX && display.wheelDX && movedX / display.wheelDX);
3964 display.wheelStartX = display.wheelStartY = null;
3965 if (!sample) return;
3966 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
3967 ++wheelSamples;
3968 }, 200);
3969 } else {
3970 display.wheelDX += dx; display.wheelDY += dy;
3971 }
3972 }
3973 }
3974
3975 // KEY EVENTS
3976
3977 // Run a handler that was bound to a key.
3978 function doHandleBinding(cm, bound, dropShift) {
3979 if (typeof bound == "string") {
3980 bound = commands[bound];
3981 if (!bound) return false;
3982 }
3983 // Ensure previous input has been read, so that the handler sees a
3984 // consistent view of the document
3985 cm.display.input.ensurePolled();
3986 var prevShift = cm.display.shift, done = false;
3987 try {
3988 if (isReadOnly(cm)) cm.state.suppressEdits = true;
3989 if (dropShift) cm.display.shift = false;
3990 done = bound(cm) != Pass;
3991 } finally {
3992 cm.display.shift = prevShift;
3993 cm.state.suppressEdits = false;
3994 }
3995 return done;
3996 }
3997
3998 function lookupKeyForEditor(cm, name, handle) {
3999 for (var i = 0; i < cm.state.keyMaps.length; i++) {
4000 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
4001 if (result) return result;
4002 }
4003 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
4004 || lookupKey(name, cm.options.keyMap, handle, cm);
4005 }
4006
4007 var stopSeq = new Delayed;
4008 function dispatchKey(cm, name, e, handle) {
4009 var seq = cm.state.keySeq;
4010 if (seq) {
4011 if (isModifierKey(name)) return "handled";
4012 stopSeq.set(50, function() {
4013 if (cm.state.keySeq == seq) {
4014 cm.state.keySeq = null;
4015 cm.display.input.reset();
4016 }
4017 });
4018 name = seq + " " + name;
4019 }
4020 var result = lookupKeyForEditor(cm, name, handle);
4021
4022 if (result == "multi")
4023 cm.state.keySeq = name;
4024 if (result == "handled")
4025 signalLater(cm, "keyHandled", cm, name, e);
4026
4027 if (result == "handled" || result == "multi") {
4028 e_preventDefault(e);
4029 restartBlink(cm);
4030 }
4031
4032 if (seq && !result && /\'$/.test(name)) {
4033 e_preventDefault(e);
4034 return true;
4035 }
4036 return !!result;
4037 }
4038
4039 // Handle a key from the keydown event.
4040 function handleKeyBinding(cm, e) {
4041 var name = keyName(e, true);
4042 if (!name) return false;
4043
4044 if (e.shiftKey && !cm.state.keySeq) {
4045 // First try to resolve full name (including 'Shift-'). Failing
4046 // that, see if there is a cursor-motion command (starting with
4047 // 'go') bound to the keyname without 'Shift-'.
4048 return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
4049 || dispatchKey(cm, name, e, function(b) {
4050 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
4051 return doHandleBinding(cm, b);
4052 });
4053 } else {
4054 return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
4055 }
4056 }
4057
4058 // Handle a key from the keypress event
4059 function handleCharBinding(cm, e, ch) {
4060 return dispatchKey(cm, "'" + ch + "'", e,
4061 function(b) { return doHandleBinding(cm, b, true); });
4062 }
4063
4064 var lastStoppedKey = null;
4065 function onKeyDown(e) {
4066 var cm = this;
4067 cm.curOp.focus = activeElt();
4068 if (signalDOMEvent(cm, e)) return;
4069 // IE does strange things with escape.
4070 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4071 var code = e.keyCode;
4072 cm.display.shift = code == 16 || e.shiftKey;
4073 var handled = handleKeyBinding(cm, e);
4074 if (presto) {
4075 lastStoppedKey = handled ? code : null;
4076 // Opera has no cut event... we try to at least catch the key combo
4077 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
4078 cm.replaceSelection("", null, "cut");
4079 }
4080
4081 // Turn mouse into crosshair when Alt is held on Mac.
4082 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
4083 showCrossHair(cm);
4084 }
4085
4086 function showCrossHair(cm) {
4087 var lineDiv = cm.display.lineDiv;
4088 addClass(lineDiv, "CodeMirror-crosshair");
4089
4090 function up(e) {
4091 if (e.keyCode == 18 || !e.altKey) {
4092 rmClass(lineDiv, "CodeMirror-crosshair");
4093 off(document, "keyup", up);
4094 off(document, "mouseover", up);
4095 }
4096 }
4097 on(document, "keyup", up);
4098 on(document, "mouseover", up);
4099 }
4100
4101 function onKeyUp(e) {
4102 if (e.keyCode == 16) this.doc.sel.shift = false;
4103 signalDOMEvent(this, e);
4104 }
4105
4106 function onKeyPress(e) {
4107 var cm = this;
4108 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
4109 var keyCode = e.keyCode, charCode = e.charCode;
4110 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
4111 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
4112 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
4113 if (handleCharBinding(cm, e, ch)) return;
4114 cm.display.input.onKeyPress(e);
4115 }
4116
4117 // FOCUS/BLUR EVENTS
4118
4119 function delayBlurEvent(cm) {
4120 cm.state.delayingBlurEvent = true;
4121 setTimeout(function() {
4122 if (cm.state.delayingBlurEvent) {
4123 cm.state.delayingBlurEvent = false;
4124 onBlur(cm);
4125 }
4126 }, 100);
4127 }
4128
4129 function onFocus(cm) {
4130 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4131
4132 if (cm.options.readOnly == "nocursor") return;
4133 if (!cm.state.focused) {
4134 signal(cm, "focus", cm);
4135 cm.state.focused = true;
4136 addClass(cm.display.wrapper, "CodeMirror-focused");
4137 // This test prevents this from firing when a context
4138 // menu is closed (since the input reset would kill the
4139 // select-all detection hack)
4140 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4141 cm.display.input.reset();
4142 if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
4143 }
4144 cm.display.input.receivedFocus();
4145 }
4146 restartBlink(cm);
4147 }
4148 function onBlur(cm) {
4149 if (cm.state.delayingBlurEvent) return;
4150
4151 if (cm.state.focused) {
4152 signal(cm, "blur", cm);
4153 cm.state.focused = false;
4154 rmClass(cm.display.wrapper, "CodeMirror-focused");
4155 }
4156 clearInterval(cm.display.blinker);
4157 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
4158 }
4159
4160 // CONTEXT MENU HANDLING
4161
4162 // To make the context menu work, we need to briefly unhide the
4163 // textarea (making it as unobtrusive as possible) to let the
4164 // right-click take effect on it.
4165 function onContextMenu(cm, e) {
4166 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4167 cm.display.input.onContextMenu(e);
4168 }
4169
4170 function contextMenuInGutter(cm, e) {
4171 if (!hasHandler(cm, "gutterContextMenu")) return false;
4172 return gutterEvent(cm, e, "gutterContextMenu", false, signal);
4173 }
4174
4175 // UPDATING
4176
4177 // Compute the position of the end of a change (its 'to' property
4178 // refers to the pre-change end).
4179 var changeEnd = CodeMirror.changeEnd = function(change) {
4180 if (!change.text) return change.to;
4181 return Pos(change.from.line + change.text.length - 1,
4182 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
4183 };
4184
4185 // Adjust a position to refer to the post-change position of the
4186 // same text, or the end of the change if the change covers it.
4187 function adjustForChange(pos, change) {
4188 if (cmp(pos, change.from) < 0) return pos;
4189 if (cmp(pos, change.to) <= 0) return changeEnd(change);
4190
4191 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4192 if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4193 return Pos(line, ch);
4194 }
4195
4196 function computeSelAfterChange(doc, change) {
4197 var out = [];
4198 for (var i = 0; i < doc.sel.ranges.length; i++) {
4199 var range = doc.sel.ranges[i];
4200 out.push(new Range(adjustForChange(range.anchor, change),
4201 adjustForChange(range.head, change)));
4202 }
4203 return normalizeSelection(out, doc.sel.primIndex);
4204 }
4205
4206 function offsetPos(pos, old, nw) {
4207 if (pos.line == old.line)
4208 return Pos(nw.line, pos.ch - old.ch + nw.ch);
4209 else
4210 return Pos(nw.line + (pos.line - old.line), pos.ch);
4211 }
4212
4213 // Used by replaceSelections to allow moving the selection to the
4214 // start or around the replaced test. Hint may be "start" or "around".
4215 function computeReplacedSel(doc, changes, hint) {
4216 var out = [];
4217 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4218 for (var i = 0; i < changes.length; i++) {
4219 var change = changes[i];
4220 var from = offsetPos(change.from, oldPrev, newPrev);
4221 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4222 oldPrev = change.to;
4223 newPrev = to;
4224 if (hint == "around") {
4225 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4226 out[i] = new Range(inv ? to : from, inv ? from : to);
4227 } else {
4228 out[i] = new Range(from, from);
4229 }
4230 }
4231 return new Selection(out, doc.sel.primIndex);
4232 }
4233
4234 // Allow "beforeChange" event handlers to influence a change
4235 function filterChange(doc, change, update) {
4236 var obj = {
4237 canceled: false,
4238 from: change.from,
4239 to: change.to,
4240 text: change.text,
4241 origin: change.origin,
4242 cancel: function() { this.canceled = true; }
4243 };
4244 if (update) obj.update = function(from, to, text, origin) {
4245 if (from) this.from = clipPos(doc, from);
4246 if (to) this.to = clipPos(doc, to);
4247 if (text) this.text = text;
4248 if (origin !== undefined) this.origin = origin;
4249 };
4250 signal(doc, "beforeChange", doc, obj);
4251 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4252
4253 if (obj.canceled) return null;
4254 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4255 }
4256
4257 // Apply a change to a document, and add it to the document's
4258 // history, and propagating it to all linked documents.
4259 function makeChange(doc, change, ignoreReadOnly) {
4260 if (doc.cm) {
4261 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
4262 if (doc.cm.state.suppressEdits) return;
4263 }
4264
4265 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4266 change = filterChange(doc, change, true);
4267 if (!change) return;
4268 }
4269
4270 // Possibly split or suppress the update based on the presence
4271 // of read-only spans in its range.
4272 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4273 if (split) {
4274 for (var i = split.length - 1; i >= 0; --i)
4275 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
4276 } else {
4277 makeChangeInner(doc, change);
4278 }
4279 }
4280
4281 function makeChangeInner(doc, change) {
4282 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
4283 var selAfter = computeSelAfterChange(doc, change);
4284 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4285
4286 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
4287 var rebased = [];
4288
4289 linkedDocs(doc, function(doc, sharedHist) {
4290 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4291 rebaseHist(doc.history, change);
4292 rebased.push(doc.history);
4293 }
4294 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
4295 });
4296 }
4297
4298 // Revert a change stored in a document's history.
4299 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4300 if (doc.cm && doc.cm.state.suppressEdits) return;
4301
4302 var hist = doc.history, event, selAfter = doc.sel;
4303 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4304
4305 // Verify that there is a useable event (so that ctrl-z won't
4306 // needlessly clear selection events)
4307 for (var i = 0; i < source.length; i++) {
4308 event = source[i];
4309 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4310 break;
4311 }
4312 if (i == source.length) return;
4313 hist.lastOrigin = hist.lastSelOrigin = null;
4314
4315 for (;;) {
4316 event = source.pop();
4317 if (event.ranges) {
4318 pushSelectionToHistory(event, dest);
4319 if (allowSelectionOnly && !event.equals(doc.sel)) {
4320 setSelection(doc, event, {clearRedo: false});
4321 return;
4322 }
4323 selAfter = event;
4324 }
4325 else break;
4326 }
4327
4328 // Build up a reverse change object to add to the opposite history
4329 // stack (redo when undoing, and vice versa).
4330 var antiChanges = [];
4331 pushSelectionToHistory(selAfter, dest);
4332 dest.push({changes: antiChanges, generation: hist.generation});
4333 hist.generation = event.generation || ++hist.maxGeneration;
4334
4335 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4336
4337 for (var i = event.changes.length - 1; i >= 0; --i) {
4338 var change = event.changes[i];
4339 change.origin = type;
4340 if (filter && !filterChange(doc, change, false)) {
4341 source.length = 0;
4342 return;
4343 }
4344
4345 antiChanges.push(historyChangeFromChange(doc, change));
4346
4347 var after = i ? computeSelAfterChange(doc, change) : lst(source);
4348 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4349 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
4350 var rebased = [];
4351
4352 // Propagate to the linked documents
4353 linkedDocs(doc, function(doc, sharedHist) {
4354 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4355 rebaseHist(doc.history, change);
4356 rebased.push(doc.history);
4357 }
4358 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4359 });
4360 }
4361 }
4362
4363 // Sub-views need their line numbers shifted when text is added
4364 // above or below them in the parent document.
4365 function shiftDoc(doc, distance) {
4366 if (distance == 0) return;
4367 doc.first += distance;
4368 doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4369 return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4370 Pos(range.head.line + distance, range.head.ch));
4371 }), doc.sel.primIndex);
4372 if (doc.cm) {
4373 regChange(doc.cm, doc.first, doc.first - distance, distance);
4374 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4375 regLineChange(doc.cm, l, "gutter");
4376 }
4377 }
4378
4379 // More lower-level change function, handling only a single document
4380 // (not linked ones).
4381 function makeChangeSingleDoc(doc, change, selAfter, spans) {
4382 if (doc.cm && !doc.cm.curOp)
4383 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
4384
4385 if (change.to.line < doc.first) {
4386 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
4387 return;
4388 }
4389 if (change.from.line > doc.lastLine()) return;
4390
4391 // Clip the change to the size of this doc
4392 if (change.from.line < doc.first) {
4393 var shift = change.text.length - 1 - (doc.first - change.from.line);
4394 shiftDoc(doc, shift);
4395 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
4396 text: [lst(change.text)], origin: change.origin};
4397 }
4398 var last = doc.lastLine();
4399 if (change.to.line > last) {
4400 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
4401 text: [change.text[0]], origin: change.origin};
4402 }
4403
4404 change.removed = getBetween(doc, change.from, change.to);
4405
4406 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4407 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4408 else updateDoc(doc, change, spans);
4409 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
4410 }
4411
4412 // Handle the interaction of a change to a document with the editor
4413 // that this document is part of.
4414 function makeChangeSingleDocInEditor(cm, change, spans) {
4415 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4416
4417 var recomputeMaxLength = false, checkWidthStart = from.line;
4418 if (!cm.options.lineWrapping) {
4419 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4420 doc.iter(checkWidthStart, to.line + 1, function(line) {
4421 if (line == display.maxLine) {
4422 recomputeMaxLength = true;
4423 return true;
4424 }
4425 });
4426 }
4427
4428 if (doc.sel.contains(change.from, change.to) > -1)
4429 signalCursorActivity(cm);
4430
4431 updateDoc(doc, change, spans, estimateHeight(cm));
4432
4433 if (!cm.options.lineWrapping) {
4434 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4435 var len = lineLength(line);
4436 if (len > display.maxLineLength) {
4437 display.maxLine = line;
4438 display.maxLineLength = len;
4439 display.maxLineChanged = true;
4440 recomputeMaxLength = false;
4441 }
4442 });
4443 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4444 }
4445
4446 // Adjust frontier, schedule worker
4447 doc.frontier = Math.min(doc.frontier, from.line);
4448 startWorker(cm, 400);
4449
4450 var lendiff = change.text.length - (to.line - from.line) - 1;
4451 // Remember that these lines changed, for updating the display
4452 if (change.full)
4453 regChange(cm);
4454 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
4455 regLineChange(cm, from.line, "text");
4456 else
4457 regChange(cm, from.line, to.line + 1, lendiff);
4458
4459 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
4460 if (changeHandler || changesHandler) {
4461 var obj = {
4462 from: from, to: to,
4463 text: change.text,
4464 removed: change.removed,
4465 origin: change.origin
4466 };
4467 if (changeHandler) signalLater(cm, "change", cm, obj);
4468 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
4469 }
4470 cm.display.selForContextMenu = null;
4471 }
4472
4473 function replaceRange(doc, code, from, to, origin) {
4474 if (!to) to = from;
4475 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4476 if (typeof code == "string") code = splitLines(code);
4477 makeChange(doc, {from: from, to: to, text: code, origin: origin});
4478 }
4479
4480 // SCROLLING THINGS INTO VIEW
4481
4482 // If an editor sits on the top or bottom of the window, partially
4483 // scrolled out of view, this ensures that the cursor is visible.
4484 function maybeScrollWindow(cm, coords) {
4485 if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4486
4487 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4488 if (coords.top + box.top < 0) doScroll = true;
4489 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
4490 if (doScroll != null && !phantom) {
4491 var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4492 (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
4493 (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
4494 coords.left + "px; width: 2px;");
4495 cm.display.lineSpace.appendChild(scrollNode);
4496 scrollNode.scrollIntoView(doScroll);
4497 cm.display.lineSpace.removeChild(scrollNode);
4498 }
4499 }
4500
4501 // Scroll a given position into view (immediately), verifying that
4502 // it actually became visible (as line heights are accurately
4503 // measured, the position of something may 'drift' during drawing).
4504 function scrollPosIntoView(cm, pos, end, margin) {
4505 if (margin == null) margin = 0;
4506 for (var limit = 0; limit < 5; limit++) {
4507 var changed = false, coords = cursorCoords(cm, pos);
4508 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4509 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
4510 Math.min(coords.top, endCoords.top) - margin,
4511 Math.max(coords.left, endCoords.left),
4512 Math.max(coords.bottom, endCoords.bottom) + margin);
4513 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4514 if (scrollPos.scrollTop != null) {
4515 setScrollTop(cm, scrollPos.scrollTop);
4516 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4517 }
4518 if (scrollPos.scrollLeft != null) {
4519 setScrollLeft(cm, scrollPos.scrollLeft);
4520 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4521 }
4522 if (!changed) break;
4523 }
4524 return coords;
4525 }
4526
4527 // Scroll a given set of coordinates into view (immediately).
4528 function scrollIntoView(cm, x1, y1, x2, y2) {
4529 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4530 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
4531 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
4532 }
4533
4534 // Calculate a new scroll position needed to scroll the given
4535 // rectangle into view. Returns an object with scrollTop and
4536 // scrollLeft properties. When these are undefined, the
4537 // vertical/horizontal position does not need to be adjusted.
4538 function calculateScrollPos(cm, x1, y1, x2, y2) {
4539 var display = cm.display, snapMargin = textHeight(cm.display);
4540 if (y1 < 0) y1 = 0;
4541 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4542 var screen = displayHeight(cm), result = {};
4543 if (y2 - y1 > screen) y2 = y1 + screen;
4544 var docBottom = cm.doc.height + paddingVert(display);
4545 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4546 if (y1 < screentop) {
4547 result.scrollTop = atTop ? 0 : y1;
4548 } else if (y2 > screentop + screen) {
4549 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4550 if (newTop != screentop) result.scrollTop = newTop;
4551 }
4552
4553 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
4554 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4555 var tooWide = x2 - x1 > screenw;
4556 if (tooWide) x2 = x1 + screenw;
4557 if (x1 < 10)
4558 result.scrollLeft = 0;
4559 else if (x1 < screenleft)
4560 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4561 else if (x2 > screenw + screenleft - 3)
4562 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4563 return result;
4564 }
4565
4566 // Store a relative adjustment to the scroll position in the current
4567 // operation (to be applied when the operation finishes).
4568 function addToScrollPos(cm, left, top) {
4569 if (left != null || top != null) resolveScrollToPos(cm);
4570 if (left != null)
4571 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
4572 if (top != null)
4573 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
4574 }
4575
4576 // Make sure that at the end of the operation the current cursor is
4577 // shown.
4578 function ensureCursorVisible(cm) {
4579 resolveScrollToPos(cm);
4580 var cur = cm.getCursor(), from = cur, to = cur;
4581 if (!cm.options.lineWrapping) {
4582 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4583 to = Pos(cur.line, cur.ch + 1);
4584 }
4585 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
4586 }
4587
4588 // When an operation has its scrollToPos property set, and another
4589 // scroll action is applied before the end of the operation, this
4590 // 'simulates' scrolling that position into view in a cheap way, so
4591 // that the effect of intermediate scroll commands is not ignored.
4592 function resolveScrollToPos(cm) {
4593 var range = cm.curOp.scrollToPos;
4594 if (range) {
4595 cm.curOp.scrollToPos = null;
4596 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
4597 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4598 Math.min(from.top, to.top) - range.margin,
4599 Math.max(from.right, to.right),
4600 Math.max(from.bottom, to.bottom) + range.margin);
4601 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4602 }
4603 }
4604
4605 // API UTILITIES
4606
4607 // Indent the given line. The how parameter can be "smart",
4608 // "add"/null, "subtract", or "prev". When aggressive is false
4609 // (typically set to true for forced single-line indents), empty
4610 // lines are not indented, and places where the mode returns Pass
4611 // are left alone.
4612 function indentLine(cm, n, how, aggressive) {
4613 var doc = cm.doc, state;
4614 if (how == null) how = "add";
4615 if (how == "smart") {
4616 // Fall back to "prev" when the mode doesn't have an indentation
4617 // method.
4618 if (!doc.mode.indent) how = "prev";
4619 else state = getStateBefore(cm, n);
4620 }
4621
4622 var tabSize = cm.options.tabSize;
4623 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
4624 if (line.stateAfter) line.stateAfter = null;
4625 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4626 if (!aggressive && !/\S/.test(line.text)) {
4627 indentation = 0;
4628 how = "not";
4629 } else if (how == "smart") {
4630 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
4631 if (indentation == Pass || indentation > 150) {
4632 if (!aggressive) return;
4633 how = "prev";
4634 }
4635 }
4636 if (how == "prev") {
4637 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4638 else indentation = 0;
4639 } else if (how == "add") {
4640 indentation = curSpace + cm.options.indentUnit;
4641 } else if (how == "subtract") {
4642 indentation = curSpace - cm.options.indentUnit;
4643 } else if (typeof how == "number") {
4644 indentation = curSpace + how;
4645 }
4646 indentation = Math.max(0, indentation);
4647
4648 var indentString = "", pos = 0;
4649 if (cm.options.indentWithTabs)
4650 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
4651 if (pos < indentation) indentString += spaceStr(indentation - pos);
4652
4653 if (indentString != curSpaceString) {
4654 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4655 line.stateAfter = null;
4656 return true;
4657 } else {
4658 // Ensure that, if the cursor was in the whitespace at the start
4659 // of the line, it is moved to the end of that space.
4660 for (var i = 0; i < doc.sel.ranges.length; i++) {
4661 var range = doc.sel.ranges[i];
4662 if (range.head.line == n && range.head.ch < curSpaceString.length) {
4663 var pos = Pos(n, curSpaceString.length);
4664 replaceOneSelection(doc, i, new Range(pos, pos));
4665 break;
4666 }
4667 }
4668 }
4669 }
4670
4671 // Utility for applying a change to a line by handle or number,
4672 // returning the number and optionally registering the line as
4673 // changed.
4674 function changeLine(doc, handle, changeType, op) {
4675 var no = handle, line = handle;
4676 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4677 else no = lineNo(handle);
4678 if (no == null) return null;
4679 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4680 return line;
4681 }
4682
4683 // Helper for deleting text near the selection(s), used to implement
4684 // backspace, delete, and similar functionality.
4685 function deleteNearSelection(cm, compute) {
4686 var ranges = cm.doc.sel.ranges, kill = [];
4687 // Build up a set of ranges to kill first, merging overlapping
4688 // ranges.
4689 for (var i = 0; i < ranges.length; i++) {
4690 var toKill = compute(ranges[i]);
4691 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4692 var replaced = kill.pop();
4693 if (cmp(replaced.from, toKill.from) < 0) {
4694 toKill.from = replaced.from;
4695 break;
4696 }
4697 }
4698 kill.push(toKill);
4699 }
4700 // Next, remove those actual ranges.
4701 runInOp(cm, function() {
4702 for (var i = kill.length - 1; i >= 0; i--)
4703 replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4704 ensureCursorVisible(cm);
4705 });
4706 }
4707
4708 // Used for horizontal relative motion. Dir is -1 or 1 (left or
4709 // right), unit can be "char", "column" (like char, but doesn't
4710 // cross line boundaries), "word" (across next word), or "group" (to
4711 // the start of next group of word or non-word-non-whitespace
4712 // chars). The visually param controls whether, in right-to-left
4713 // text, direction 1 means to move towards the next index in the
4714 // string, or towards the character to the right of the current
4715 // position. The resulting position will have a hitSide=true
4716 // property if it reached the end of the document.
4717 function findPosH(doc, pos, dir, unit, visually) {
4718 var line = pos.line, ch = pos.ch, origDir = dir;
4719 var lineObj = getLine(doc, line);
4720 var possible = true;
4721 function findNextLine() {
4722 var l = line + dir;
4723 if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
4724 line = l;
4725 return lineObj = getLine(doc, l);
4726 }
4727 function moveOnce(boundToLine) {
4728 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
4729 if (next == null) {
4730 if (!boundToLine && findNextLine()) {
4731 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4732 else ch = dir < 0 ? lineObj.text.length : 0;
4733 } else return (possible = false);
4734 } else ch = next;
4735 return true;
4736 }
4737
4738 if (unit == "char") moveOnce();
4739 else if (unit == "column") moveOnce(true);
4740 else if (unit == "word" || unit == "group") {
4741 var sawType = null, group = unit == "group";
4742 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4743 for (var first = true;; first = false) {
4744 if (dir < 0 && !moveOnce(!first)) break;
4745 var cur = lineObj.text.charAt(ch) || "\n";
4746 var type = isWordChar(cur, helper) ? "w"
4747 : group && cur == "\n" ? "n"
4748 : !group || /\s/.test(cur) ? null
4749 : "p";
4750 if (group && !first && !type) type = "s";
4751 if (sawType && sawType != type) {
4752 if (dir < 0) {dir = 1; moveOnce();}
4753 break;
4754 }
4755
4756 if (type) sawType = type;
4757 if (dir > 0 && !moveOnce(!first)) break;
4758 }
4759 }
4760 var result = skipAtomic(doc, Pos(line, ch), origDir, true);
4761 if (!possible) result.hitSide = true;
4762 return result;
4763 }
4764
4765 // For relative vertical movement. Dir may be -1 or 1. Unit can be
4766 // "page" or "line". The resulting position will have a hitSide=true
4767 // property if it reached the end of the document.
4768 function findPosV(cm, pos, dir, unit) {
4769 var doc = cm.doc, x = pos.left, y;
4770 if (unit == "page") {
4771 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
4772 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
4773 } else if (unit == "line") {
4774 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4775 }
4776 for (;;) {
4777 var target = coordsChar(cm, x, y);
4778 if (!target.outside) break;
4779 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4780 y += dir * 5;
4781 }
4782 return target;
4783 }
4784
4785 // EDITOR METHODS
4786
4787 // The publicly visible API. Note that methodOp(f) means
4788 // 'wrap f in an operation, performed on its `this` parameter'.
4789
4790 // This is not the complete set of editor methods. Most of the
4791 // methods defined on the Doc type are also injected into
4792 // CodeMirror.prototype, for backwards compatibility and
4793 // convenience.
4794
4795 CodeMirror.prototype = {
4796 constructor: CodeMirror,
4797 focus: function(){window.focus(); this.display.input.focus();},
4798
4799 setOption: function(option, value) {
4800 var options = this.options, old = options[option];
4801 if (options[option] == value && option != "mode") return;
4802 options[option] = value;
4803 if (optionHandlers.hasOwnProperty(option))
4804 operation(this, optionHandlers[option])(this, value, old);
4805 },
4806
4807 getOption: function(option) {return this.options[option];},
4808 getDoc: function() {return this.doc;},
4809
4810 addKeyMap: function(map, bottom) {
4811 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4812 },
4813 removeKeyMap: function(map) {
4814 var maps = this.state.keyMaps;
4815 for (var i = 0; i < maps.length; ++i)
4816 if (maps[i] == map || maps[i].name == map) {
4817 maps.splice(i, 1);
4818 return true;
4819 }
4820 },
4821
4822 addOverlay: methodOp(function(spec, options) {
4823 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4824 if (mode.startState) throw new Error("Overlays may not be stateful.");
4825 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4826 this.state.modeGen++;
4827 regChange(this);
4828 }),
4829 removeOverlay: methodOp(function(spec) {
4830 var overlays = this.state.overlays;
4831 for (var i = 0; i < overlays.length; ++i) {
4832 var cur = overlays[i].modeSpec;
4833 if (cur == spec || typeof spec == "string" && cur.name == spec) {
4834 overlays.splice(i, 1);
4835 this.state.modeGen++;
4836 regChange(this);
4837 return;
4838 }
4839 }
4840 }),
4841
4842 indentLine: methodOp(function(n, dir, aggressive) {
4843 if (typeof dir != "string" && typeof dir != "number") {
4844 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
4845 else dir = dir ? "add" : "subtract";
4846 }
4847 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4848 }),
4849 indentSelection: methodOp(function(how) {
4850 var ranges = this.doc.sel.ranges, end = -1;
4851 for (var i = 0; i < ranges.length; i++) {
4852 var range = ranges[i];
4853 if (!range.empty()) {
4854 var from = range.from(), to = range.to();
4855 var start = Math.max(end, from.line);
4856 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4857 for (var j = start; j < end; ++j)
4858 indentLine(this, j, how);
4859 var newRanges = this.doc.sel.ranges;
4860 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
4861 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4862 } else if (range.head.line > end) {
4863 indentLine(this, range.head.line, how, true);
4864 end = range.head.line;
4865 if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4866 }
4867 }
4868 }),
4869
4870 // Fetch the parser token for a given character. Useful for hacks
4871 // that want to inspect the mode state (say, for completion).
4872 getTokenAt: function(pos, precise) {
4873 return takeToken(this, pos, precise);
4874 },
4875
4876 getLineTokens: function(line, precise) {
4877 return takeToken(this, Pos(line), precise, true);
4878 },
4879
4880 getTokenTypeAt: function(pos) {
4881 pos = clipPos(this.doc, pos);
4882 var styles = getLineStyles(this, getLine(this.doc, pos.line));
4883 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4884 var type;
4885 if (ch == 0) type = styles[2];
4886 else for (;;) {
4887 var mid = (before + after) >> 1;
4888 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4889 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4890 else { type = styles[mid * 2 + 2]; break; }
4891 }
4892 var cut = type ? type.indexOf("cm-overlay ") : -1;
4893 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
4894 },
4895
4896 getModeAt: function(pos) {
4897 var mode = this.doc.mode;
4898 if (!mode.innerMode) return mode;
4899 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
4900 },
4901
4902 getHelper: function(pos, type) {
4903 return this.getHelpers(pos, type)[0];
4904 },
4905
4906 getHelpers: function(pos, type) {
4907 var found = [];
4908 if (!helpers.hasOwnProperty(type)) return found;
4909 var help = helpers[type], mode = this.getModeAt(pos);
4910 if (typeof mode[type] == "string") {
4911 if (help[mode[type]]) found.push(help[mode[type]]);
4912 } else if (mode[type]) {
4913 for (var i = 0; i < mode[type].length; i++) {
4914 var val = help[mode[type][i]];
4915 if (val) found.push(val);
4916 }
4917 } else if (mode.helperType && help[mode.helperType]) {
4918 found.push(help[mode.helperType]);
4919 } else if (help[mode.name]) {
4920 found.push(help[mode.name]);
4921 }
4922 for (var i = 0; i < help._global.length; i++) {
4923 var cur = help._global[i];
4924 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
4925 found.push(cur.val);
4926 }
4927 return found;
4928 },
4929
4930 getStateAfter: function(line, precise) {
4931 var doc = this.doc;
4932 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
4933 return getStateBefore(this, line + 1, precise);
4934 },
4935
4936 cursorCoords: function(start, mode) {
4937 var pos, range = this.doc.sel.primary();
4938 if (start == null) pos = range.head;
4939 else if (typeof start == "object") pos = clipPos(this.doc, start);
4940 else pos = start ? range.from() : range.to();
4941 return cursorCoords(this, pos, mode || "page");
4942 },
4943
4944 charCoords: function(pos, mode) {
4945 return charCoords(this, clipPos(this.doc, pos), mode || "page");
4946 },
4947
4948 coordsChar: function(coords, mode) {
4949 coords = fromCoordSystem(this, coords, mode || "page");
4950 return coordsChar(this, coords.left, coords.top);
4951 },
4952
4953 lineAtHeight: function(height, mode) {
4954 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
4955 return lineAtHeight(this.doc, height + this.display.viewOffset);
4956 },
4957 heightAtLine: function(line, mode) {
4958 var end = false, lineObj;
4959 if (typeof line == "number") {
4960 var last = this.doc.first + this.doc.size - 1;
4961 if (line < this.doc.first) line = this.doc.first;
4962 else if (line > last) { line = last; end = true; }
4963 lineObj = getLine(this.doc, line);
4964 } else {
4965 lineObj = line;
4966 }
4967 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
4968 (end ? this.doc.height - heightAtLine(lineObj) : 0);
4969 },
4970
4971 defaultTextHeight: function() { return textHeight(this.display); },
4972 defaultCharWidth: function() { return charWidth(this.display); },
4973
4974 setGutterMarker: methodOp(function(line, gutterID, value) {
4975 return changeLine(this.doc, line, "gutter", function(line) {
4976 var markers = line.gutterMarkers || (line.gutterMarkers = {});
4977 markers[gutterID] = value;
4978 if (!value && isEmpty(markers)) line.gutterMarkers = null;
4979 return true;
4980 });
4981 }),
4982
4983 clearGutter: methodOp(function(gutterID) {
4984 var cm = this, doc = cm.doc, i = doc.first;
4985 doc.iter(function(line) {
4986 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
4987 line.gutterMarkers[gutterID] = null;
4988 regLineChange(cm, i, "gutter");
4989 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
4990 }
4991 ++i;
4992 });
4993 }),
4994
4995 lineInfo: function(line) {
4996 if (typeof line == "number") {
4997 if (!isLine(this.doc, line)) return null;
4998 var n = line;
4999 line = getLine(this.doc, line);
5000 if (!line) return null;
5001 } else {
5002 var n = lineNo(line);
5003 if (n == null) return null;
5004 }
5005 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5006 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5007 widgets: line.widgets};
5008 },
5009
5010 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
5011
5012 addWidget: function(pos, node, scroll, vert, horiz) {
5013 var display = this.display;
5014 pos = cursorCoords(this, clipPos(this.doc, pos));
5015 var top = pos.bottom, left = pos.left;
5016 node.style.position = "absolute";
5017 node.setAttribute("cm-ignore-events", "true");
5018 this.display.input.setUneditable(node);
5019 display.sizer.appendChild(node);
5020 if (vert == "over") {
5021 top = pos.top;
5022 } else if (vert == "above" || vert == "near") {
5023 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
5024 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
5025 // Default to positioning above (if specified and possible); otherwise default to positioning below
5026 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
5027 top = pos.top - node.offsetHeight;
5028 else if (pos.bottom + node.offsetHeight <= vspace)
5029 top = pos.bottom;
5030 if (left + node.offsetWidth > hspace)
5031 left = hspace - node.offsetWidth;
5032 }
5033 node.style.top = top + "px";
5034 node.style.left = node.style.right = "";
5035 if (horiz == "right") {
5036 left = display.sizer.clientWidth - node.offsetWidth;
5037 node.style.right = "0px";
5038 } else {
5039 if (horiz == "left") left = 0;
5040 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
5041 node.style.left = left + "px";
5042 }
5043 if (scroll)
5044 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
5045 },
5046
5047 triggerOnKeyDown: methodOp(onKeyDown),
5048 triggerOnKeyPress: methodOp(onKeyPress),
5049 triggerOnKeyUp: onKeyUp,
5050
5051 execCommand: function(cmd) {
5052 if (commands.hasOwnProperty(cmd))
5053 return commands[cmd](this);
5054 },
5055
5056 findPosH: function(from, amount, unit, visually) {
5057 var dir = 1;
5058 if (amount < 0) { dir = -1; amount = -amount; }
5059 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5060 cur = findPosH(this.doc, cur, dir, unit, visually);
5061 if (cur.hitSide) break;
5062 }
5063 return cur;
5064 },
5065
5066 moveH: methodOp(function(dir, unit) {
5067 var cm = this;
5068 cm.extendSelectionsBy(function(range) {
5069 if (cm.display.shift || cm.doc.extend || range.empty())
5070 return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
5071 else
5072 return dir < 0 ? range.from() : range.to();
5073 }, sel_move);
5074 }),
5075
5076 deleteH: methodOp(function(dir, unit) {
5077 var sel = this.doc.sel, doc = this.doc;
5078 if (sel.somethingSelected())
5079 doc.replaceSelection("", null, "+delete");
5080 else
5081 deleteNearSelection(this, function(range) {
5082 var other = findPosH(doc, range.head, dir, unit, false);
5083 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
5084 });
5085 }),
5086
5087 findPosV: function(from, amount, unit, goalColumn) {
5088 var dir = 1, x = goalColumn;
5089 if (amount < 0) { dir = -1; amount = -amount; }
5090 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5091 var coords = cursorCoords(this, cur, "div");
5092 if (x == null) x = coords.left;
5093 else coords.left = x;
5094 cur = findPosV(this, coords, dir, unit);
5095 if (cur.hitSide) break;
5096 }
5097 return cur;
5098 },
5099
5100 moveV: methodOp(function(dir, unit) {
5101 var cm = this, doc = this.doc, goals = [];
5102 var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
5103 doc.extendSelectionsBy(function(range) {
5104 if (collapse)
5105 return dir < 0 ? range.from() : range.to();
5106 var headPos = cursorCoords(cm, range.head, "div");
5107 if (range.goalColumn != null) headPos.left = range.goalColumn;
5108 goals.push(headPos.left);
5109 var pos = findPosV(cm, headPos, dir, unit);
5110 if (unit == "page" && range == doc.sel.primary())
5111 addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
5112 return pos;
5113 }, sel_move);
5114 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
5115 doc.sel.ranges[i].goalColumn = goals[i];
5116 }),
5117
5118 // Find the word at the given position (as returned by coordsChar).
5119 findWordAt: function(pos) {
5120 var doc = this.doc, line = getLine(doc, pos.line).text;
5121 var start = pos.ch, end = pos.ch;
5122 if (line) {
5123 var helper = this.getHelper(pos, "wordChars");
5124 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
5125 var startChar = line.charAt(start);
5126 var check = isWordChar(startChar, helper)
5127 ? function(ch) { return isWordChar(ch, helper); }
5128 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
5129 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
5130 while (start > 0 && check(line.charAt(start - 1))) --start;
5131 while (end < line.length && check(line.charAt(end))) ++end;
5132 }
5133 return new Range(Pos(pos.line, start), Pos(pos.line, end));
5134 },
5135
5136 toggleOverwrite: function(value) {
5137 if (value != null && value == this.state.overwrite) return;
5138 if (this.state.overwrite = !this.state.overwrite)
5139 addClass(this.display.cursorDiv, "CodeMirror-overwrite");
5140 else
5141 rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
5142
5143 signal(this, "overwriteToggle", this, this.state.overwrite);
5144 },
5145 hasFocus: function() { return this.display.input.getField() == activeElt(); },
5146
5147 scrollTo: methodOp(function(x, y) {
5148 if (x != null || y != null) resolveScrollToPos(this);
5149 if (x != null) this.curOp.scrollLeft = x;
5150 if (y != null) this.curOp.scrollTop = y;
5151 }),
5152 getScrollInfo: function() {
5153 var scroller = this.display.scroller;
5154 return {left: scroller.scrollLeft, top: scroller.scrollTop,
5155 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
5156 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
5157 clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
5158 },
5159
5160 scrollIntoView: methodOp(function(range, margin) {
5161 if (range == null) {
5162 range = {from: this.doc.sel.primary().head, to: null};
5163 if (margin == null) margin = this.options.cursorScrollMargin;
5164 } else if (typeof range == "number") {
5165 range = {from: Pos(range, 0), to: null};
5166 } else if (range.from == null) {
5167 range = {from: range, to: null};
5168 }
5169 if (!range.to) range.to = range.from;
5170 range.margin = margin || 0;
5171
5172 if (range.from.line != null) {
5173 resolveScrollToPos(this);
5174 this.curOp.scrollToPos = range;
5175 } else {
5176 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
5177 Math.min(range.from.top, range.to.top) - range.margin,
5178 Math.max(range.from.right, range.to.right),
5179 Math.max(range.from.bottom, range.to.bottom) + range.margin);
5180 this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
5181 }
5182 }),
5183
5184 setSize: methodOp(function(width, height) {
5185 var cm = this;
5186 function interpret(val) {
5187 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
5188 }
5189 if (width != null) cm.display.wrapper.style.width = interpret(width);
5190 if (height != null) cm.display.wrapper.style.height = interpret(height);
5191 if (cm.options.lineWrapping) clearLineMeasurementCache(this);
5192 var lineNo = cm.display.viewFrom;
5193 cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
5194 if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
5195 if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
5196 ++lineNo;
5197 });
5198 cm.curOp.forceUpdate = true;
5199 signal(cm, "refresh", this);
5200 }),
5201
5202 operation: function(f){return runInOp(this, f);},
5203
5204 refresh: methodOp(function() {
5205 var oldHeight = this.display.cachedTextHeight;
5206 regChange(this);
5207 this.curOp.forceUpdate = true;
5208 clearCaches(this);
5209 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
5210 updateGutterSpace(this);
5211 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
5212 estimateLineHeights(this);
5213 signal(this, "refresh", this);
5214 }),
5215
5216 swapDoc: methodOp(function(doc) {
5217 var old = this.doc;
5218 old.cm = null;
5219 attachDoc(this, doc);
5220 clearCaches(this);
5221 this.display.input.reset();
5222 this.scrollTo(doc.scrollLeft, doc.scrollTop);
5223 this.curOp.forceScroll = true;
5224 signalLater(this, "swapDoc", this, old);
5225 return old;
5226 }),
5227
5228 getInputField: function(){return this.display.input.getField();},
5229 getWrapperElement: function(){return this.display.wrapper;},
5230 getScrollerElement: function(){return this.display.scroller;},
5231 getGutterElement: function(){return this.display.gutters;}
5232 };
5233 eventMixin(CodeMirror);
5234
5235 // OPTION DEFAULTS
5236
5237 // The default configuration options.
5238 var defaults = CodeMirror.defaults = {};
5239 // Functions to run when options are changed.
5240 var optionHandlers = CodeMirror.optionHandlers = {};
5241
5242 function option(name, deflt, handle, notOnInit) {
5243 CodeMirror.defaults[name] = deflt;
5244 if (handle) optionHandlers[name] =
5245 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
5246 }
5247
5248 // Passed to option handlers when there is no old value.
5249 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
5250
5251 // These two are, on init, called from the constructor because they
5252 // have to be initialized before the editor can start at all.
5253 option("value", "", function(cm, val) {
5254 cm.setValue(val);
5255 }, true);
5256 option("mode", null, function(cm, val) {
5257 cm.doc.modeOption = val;
5258 loadMode(cm);
5259 }, true);
5260
5261 option("indentUnit", 2, loadMode, true);
5262 option("indentWithTabs", false);
5263 option("smartIndent", true);
5264 option("tabSize", 4, function(cm) {
5265 resetModeState(cm);
5266 clearCaches(cm);
5267 regChange(cm);
5268 }, true);
5269 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
5270 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
5271 if (old != CodeMirror.Init) cm.refresh();
5272 });
5273 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
5274 option("electricChars", true);
5275 option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5276 throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5277 }, true);
5278 option("rtlMoveVisually", !windows);
5279 option("wholeLineUpdateBefore", true);
5280
5281 option("theme", "default", function(cm) {
5282 themeChanged(cm);
5283 guttersChanged(cm);
5284 }, true);
5285 option("keyMap", "default", function(cm, val, old) {
5286 var next = getKeyMap(val);
5287 var prev = old != CodeMirror.Init && getKeyMap(old);
5288 if (prev && prev.detach) prev.detach(cm, next);
5289 if (next.attach) next.attach(cm, prev || null);
5290 });
5291 option("extraKeys", null);
5292
5293 option("lineWrapping", false, wrappingChanged, true);
5294 option("gutters", [], function(cm) {
5295 setGuttersForLineNumbers(cm.options);
5296 guttersChanged(cm);
5297 }, true);
5298 option("fixedGutter", true, function(cm, val) {
5299 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
5300 cm.refresh();
5301 }, true);
5302 option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
5303 option("scrollbarStyle", "native", function(cm) {
5304 initScrollbars(cm);
5305 updateScrollbars(cm);
5306 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
5307 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
5308 }, true);
5309 option("lineNumbers", false, function(cm) {
5310 setGuttersForLineNumbers(cm.options);
5311 guttersChanged(cm);
5312 }, true);
5313 option("firstLineNumber", 1, guttersChanged, true);
5314 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
5315 option("showCursorWhenSelecting", false, updateSelection, true);
5316
5317 option("resetSelectionOnContextMenu", true);
5318 option("lineWiseCopyCut", true);
5319
5320 option("readOnly", false, function(cm, val) {
5321 if (val == "nocursor") {
5322 onBlur(cm);
5323 cm.display.input.blur();
5324 cm.display.disabled = true;
5325 } else {
5326 cm.display.disabled = false;
5327 if (!val) cm.display.input.reset();
5328 }
5329 });
5330 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
5331 option("dragDrop", true, dragDropChanged);
5332
5333 option("cursorBlinkRate", 530);
5334 option("cursorScrollMargin", 0);
5335 option("cursorHeight", 1, updateSelection, true);
5336 option("singleCursorHeightPerLine", true, updateSelection, true);
5337 option("workTime", 100);
5338 option("workDelay", 100);
5339 option("flattenSpans", true, resetModeState, true);
5340 option("addModeClass", false, resetModeState, true);
5341 option("pollInterval", 100);
5342 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
5343 option("historyEventDelay", 1250);
5344 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
5345 option("maxHighlightLength", 10000, resetModeState, true);
5346 option("moveInputWithCursor", true, function(cm, val) {
5347 if (!val) cm.display.input.resetPosition();
5348 });
5349
5350 option("tabindex", null, function(cm, val) {
5351 cm.display.input.getField().tabIndex = val || "";
5352 });
5353 option("autofocus", null);
5354
5355 // MODE DEFINITION AND QUERYING
5356
5357 // Known modes, by name and by MIME
5358 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
5359
5360 // Extra arguments are stored as the mode's dependencies, which is
5361 // used by (legacy) mechanisms like loadmode.js to automatically
5362 // load a mode. (Preferred mechanism is the require/define calls.)
5363 CodeMirror.defineMode = function(name, mode) {
5364 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
5365 if (arguments.length > 2)
5366 mode.dependencies = Array.prototype.slice.call(arguments, 2);
5367 modes[name] = mode;
5368 };
5369
5370 CodeMirror.defineMIME = function(mime, spec) {
5371 mimeModes[mime] = spec;
5372 };
5373
5374 // Given a MIME type, a {name, ...options} config object, or a name
5375 // string, return a mode config object.
5376 CodeMirror.resolveMode = function(spec) {
5377 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
5378 spec = mimeModes[spec];
5379 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
5380 var found = mimeModes[spec.name];
5381 if (typeof found == "string") found = {name: found};
5382 spec = createObj(found, spec);
5383 spec.name = found.name;
5384 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
5385 return CodeMirror.resolveMode("application/xml");
5386 }
5387 if (typeof spec == "string") return {name: spec};
5388 else return spec || {name: "null"};
5389 };
5390
5391 // Given a mode spec (anything that resolveMode accepts), find and
5392 // initialize an actual mode object.
5393 CodeMirror.getMode = function(options, spec) {
5394 var spec = CodeMirror.resolveMode(spec);
5395 var mfactory = modes[spec.name];
5396 if (!mfactory) return CodeMirror.getMode(options, "text/plain");
5397 var modeObj = mfactory(options, spec);
5398 if (modeExtensions.hasOwnProperty(spec.name)) {
5399 var exts = modeExtensions[spec.name];
5400 for (var prop in exts) {
5401 if (!exts.hasOwnProperty(prop)) continue;
5402 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
5403 modeObj[prop] = exts[prop];
5404 }
5405 }
5406 modeObj.name = spec.name;
5407 if (spec.helperType) modeObj.helperType = spec.helperType;
5408 if (spec.modeProps) for (var prop in spec.modeProps)
5409 modeObj[prop] = spec.modeProps[prop];
5410
5411 return modeObj;
5412 };
5413
5414 // Minimal default mode.
5415 CodeMirror.defineMode("null", function() {
5416 return {token: function(stream) {stream.skipToEnd();}};
5417 });
5418 CodeMirror.defineMIME("text/plain", "null");
5419
5420 // This can be used to attach properties to mode objects from
5421 // outside the actual mode definition.
5422 var modeExtensions = CodeMirror.modeExtensions = {};
5423 CodeMirror.extendMode = function(mode, properties) {
5424 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
5425 copyObj(properties, exts);
5426 };
5427
5428 // EXTENSIONS
5429
5430 CodeMirror.defineExtension = function(name, func) {
5431 CodeMirror.prototype[name] = func;
5432 };
5433 CodeMirror.defineDocExtension = function(name, func) {
5434 Doc.prototype[name] = func;
5435 };
5436 CodeMirror.defineOption = option;
5437
5438 var initHooks = [];
5439 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5440
5441 var helpers = CodeMirror.helpers = {};
5442 CodeMirror.registerHelper = function(type, name, value) {
5443 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
5444 helpers[type][name] = value;
5445 };
5446 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5447 CodeMirror.registerHelper(type, name, value);
5448 helpers[type]._global.push({pred: predicate, val: value});
5449 };
5450
5451 // MODE STATE HANDLING
5452
5453 // Utility functions for working with state. Exported because nested
5454 // modes need to do this for their inner modes.
5455
5456 var copyState = CodeMirror.copyState = function(mode, state) {
5457 if (state === true) return state;
5458 if (mode.copyState) return mode.copyState(state);
5459 var nstate = {};
5460 for (var n in state) {
5461 var val = state[n];
5462 if (val instanceof Array) val = val.concat([]);
5463 nstate[n] = val;
5464 }
5465 return nstate;
5466 };
5467
5468 var startState = CodeMirror.startState = function(mode, a1, a2) {
5469 return mode.startState ? mode.startState(a1, a2) : true;
5470 };
5471
5472 // Given a mode and a state (for that mode), find the inner mode and
5473 // state at the position that the state refers to.
5474 CodeMirror.innerMode = function(mode, state) {
5475 while (mode.innerMode) {
5476 var info = mode.innerMode(state);
5477 if (!info || info.mode == mode) break;
5478 state = info.state;
5479 mode = info.mode;
5480 }
5481 return info || {mode: mode, state: state};
5482 };
5483
5484 // STANDARD COMMANDS
5485
5486 // Commands are parameter-less actions that can be performed on an
5487 // editor, mostly used for keybindings.
5488 var commands = CodeMirror.commands = {
5489 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
5490 singleSelection: function(cm) {
5491 cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
5492 },
5493 killLine: function(cm) {
5494 deleteNearSelection(cm, function(range) {
5495 if (range.empty()) {
5496 var len = getLine(cm.doc, range.head.line).text.length;
5497 if (range.head.ch == len && range.head.line < cm.lastLine())
5498 return {from: range.head, to: Pos(range.head.line + 1, 0)};
5499 else
5500 return {from: range.head, to: Pos(range.head.line, len)};
5501 } else {
5502 return {from: range.from(), to: range.to()};
5503 }
5504 });
5505 },
5506 deleteLine: function(cm) {
5507 deleteNearSelection(cm, function(range) {
5508 return {from: Pos(range.from().line, 0),
5509 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5510 });
5511 },
5512 delLineLeft: function(cm) {
5513 deleteNearSelection(cm, function(range) {
5514 return {from: Pos(range.from().line, 0), to: range.from()};
5515 });
5516 },
5517 delWrappedLineLeft: function(cm) {
5518 deleteNearSelection(cm, function(range) {
5519 var top = cm.charCoords(range.head, "div").top + 5;
5520 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5521 return {from: leftPos, to: range.from()};
5522 });
5523 },
5524 delWrappedLineRight: function(cm) {
5525 deleteNearSelection(cm, function(range) {
5526 var top = cm.charCoords(range.head, "div").top + 5;
5527 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5528 return {from: range.from(), to: rightPos };
5529 });
5530 },
5531 undo: function(cm) {cm.undo();},
5532 redo: function(cm) {cm.redo();},
5533 undoSelection: function(cm) {cm.undoSelection();},
5534 redoSelection: function(cm) {cm.redoSelection();},
5535 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5536 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5537 goLineStart: function(cm) {
5538 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
5539 {origin: "+move", bias: 1});
5540 },
5541 goLineStartSmart: function(cm) {
5542 cm.extendSelectionsBy(function(range) {
5543 return lineStartSmart(cm, range.head);
5544 }, {origin: "+move", bias: 1});
5545 },
5546 goLineEnd: function(cm) {
5547 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
5548 {origin: "+move", bias: -1});
5549 },
5550 goLineRight: function(cm) {
5551 cm.extendSelectionsBy(function(range) {
5552 var top = cm.charCoords(range.head, "div").top + 5;
5553 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5554 }, sel_move);
5555 },
5556 goLineLeft: function(cm) {
5557 cm.extendSelectionsBy(function(range) {
5558 var top = cm.charCoords(range.head, "div").top + 5;
5559 return cm.coordsChar({left: 0, top: top}, "div");
5560 }, sel_move);
5561 },
5562 goLineLeftSmart: function(cm) {
5563 cm.extendSelectionsBy(function(range) {
5564 var top = cm.charCoords(range.head, "div").top + 5;
5565 var pos = cm.coordsChar({left: 0, top: top}, "div");
5566 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
5567 return pos;
5568 }, sel_move);
5569 },
5570 goLineUp: function(cm) {cm.moveV(-1, "line");},
5571 goLineDown: function(cm) {cm.moveV(1, "line");},
5572 goPageUp: function(cm) {cm.moveV(-1, "page");},
5573 goPageDown: function(cm) {cm.moveV(1, "page");},
5574 goCharLeft: function(cm) {cm.moveH(-1, "char");},
5575 goCharRight: function(cm) {cm.moveH(1, "char");},
5576 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5577 goColumnRight: function(cm) {cm.moveH(1, "column");},
5578 goWordLeft: function(cm) {cm.moveH(-1, "word");},
5579 goGroupRight: function(cm) {cm.moveH(1, "group");},
5580 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5581 goWordRight: function(cm) {cm.moveH(1, "word");},
5582 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5583 delCharAfter: function(cm) {cm.deleteH(1, "char");},
5584 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5585 delWordAfter: function(cm) {cm.deleteH(1, "word");},
5586 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5587 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5588 indentAuto: function(cm) {cm.indentSelection("smart");},
5589 indentMore: function(cm) {cm.indentSelection("add");},
5590 indentLess: function(cm) {cm.indentSelection("subtract");},
5591 insertTab: function(cm) {cm.replaceSelection("\t");},
5592 insertSoftTab: function(cm) {
5593 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
5594 for (var i = 0; i < ranges.length; i++) {
5595 var pos = ranges[i].from();
5596 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5597 spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
5598 }
5599 cm.replaceSelections(spaces);
5600 },
5601 defaultTab: function(cm) {
5602 if (cm.somethingSelected()) cm.indentSelection("add");
5603 else cm.execCommand("insertTab");
5604 },
5605 transposeChars: function(cm) {
5606 runInOp(cm, function() {
5607 var ranges = cm.listSelections(), newSel = [];
5608 for (var i = 0; i < ranges.length; i++) {
5609 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
5610 if (line) {
5611 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
5612 if (cur.ch > 0) {
5613 cur = new Pos(cur.line, cur.ch + 1);
5614 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
5615 Pos(cur.line, cur.ch - 2), cur, "+transpose");
5616 } else if (cur.line > cm.doc.first) {
5617 var prev = getLine(cm.doc, cur.line - 1).text;
5618 if (prev)
5619 cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1),
5620 Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
5621 }
5622 }
5623 newSel.push(new Range(cur, cur));
5624 }
5625 cm.setSelections(newSel);
5626 });
5627 },
5628 newlineAndIndent: function(cm) {
5629 runInOp(cm, function() {
5630 var len = cm.listSelections().length;
5631 for (var i = 0; i < len; i++) {
5632 var range = cm.listSelections()[i];
5633 cm.replaceRange("\n", range.anchor, range.head, "+input");
5634 cm.indentLine(range.from().line + 1, null, true);
5635 ensureCursorVisible(cm);
5636 }
5637 });
5638 },
5639 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
5640 };
5641
5642
5643 // STANDARD KEYMAPS
5644
5645 var keyMap = CodeMirror.keyMap = {};
5646
5647 keyMap.basic = {
5648 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
5649 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
5650 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
5651 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
5652 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
5653 "Esc": "singleSelection"
5654 };
5655 // Note that the save and find-related commands aren't defined by
5656 // default. User code or addons can define them. Unknown commands
5657 // are simply ignored.
5658 keyMap.pcDefault = {
5659 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
5660 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
5661 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
5662 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
5663 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5664 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5665 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
5666 fallthrough: "basic"
5667 };
5668 // Very basic readline/emacs-style bindings, which are standard on Mac.
5669 keyMap.emacsy = {
5670 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
5671 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
5672 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
5673 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
5674 };
5675 keyMap.macDefault = {
5676 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5677 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5678 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
5679 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
5680 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
5681 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
5682 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
5683 fallthrough: ["basic", "emacsy"]
5684 };
5685 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5686
5687 // KEYMAP DISPATCH
5688
5689 function normalizeKeyName(name) {
5690 var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5691 var alt, ctrl, shift, cmd;
5692 for (var i = 0; i < parts.length - 1; i++) {
5693 var mod = parts[i];
5694 if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5695 else if (/^a(lt)?$/i.test(mod)) alt = true;
5696 else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5697 else if (/^s(hift)$/i.test(mod)) shift = true;
5698 else throw new Error("Unrecognized modifier name: " + mod);
5699 }
5700 if (alt) name = "Alt-" + name;
5701 if (ctrl) name = "Ctrl-" + name;
5702 if (cmd) name = "Cmd-" + name;
5703 if (shift) name = "Shift-" + name;
5704 return name;
5705 }
5706
5707 // This is a kludge to keep keymaps mostly working as raw objects
5708 // (backwards compatibility) while at the same time support features
5709 // like normalization and multi-stroke key bindings. It compiles a
5710 // new normalized keymap, and then updates the old object to reflect
5711 // this.
5712 CodeMirror.normalizeKeyMap = function(keymap) {
5713 var copy = {};
5714 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5715 var value = keymap[keyname];
5716 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5717 if (value == "...") { delete keymap[keyname]; continue; }
5718
5719 var keys = map(keyname.split(" "), normalizeKeyName);
5720 for (var i = 0; i < keys.length; i++) {
5721 var val, name;
5722 if (i == keys.length - 1) {
5723 name = keyname;
5724 val = value;
5725 } else {
5726 name = keys.slice(0, i + 1).join(" ");
5727 val = "...";
5728 }
5729 var prev = copy[name];
5730 if (!prev) copy[name] = val;
5731 else if (prev != val) throw new Error("Inconsistent bindings for " + name);
5732 }
5733 delete keymap[keyname];
5734 }
5735 for (var prop in copy) keymap[prop] = copy[prop];
5736 return keymap;
5737 };
5738
5739 var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5740 map = getKeyMap(map);
5741 var found = map.call ? map.call(key, context) : map[key];
5742 if (found === false) return "nothing";
5743 if (found === "...") return "multi";
5744 if (found != null && handle(found)) return "handled";
5745
5746 if (map.fallthrough) {
5747 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5748 return lookupKey(key, map.fallthrough, handle, context);
5749 for (var i = 0; i < map.fallthrough.length; i++) {
5750 var result = lookupKey(key, map.fallthrough[i], handle, context);
5751 if (result) return result;
5752 }
5753 }
5754 };
5755
5756 // Modifier key presses don't count as 'real' key presses for the
5757 // purpose of keymap fallthrough.
5758 var isModifierKey = CodeMirror.isModifierKey = function(value) {
5759 var name = typeof value == "string" ? value : keyNames[value.keyCode];
5760 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5761 };
5762
5763 // Look up the name of a key as indicated by an event object.
5764 var keyName = CodeMirror.keyName = function(event, noShift) {
5765 if (presto && event.keyCode == 34 && event["char"]) return false;
5766 var base = keyNames[event.keyCode], name = base;
5767 if (name == null || event.altGraphKey) return false;
5768 if (event.altKey && base != "Alt") name = "Alt-" + name;
5769 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5770 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
5771 if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5772 return name;
5773 };
5774
5775 function getKeyMap(val) {
5776 return typeof val == "string" ? keyMap[val] : val;
5777 }
5778
5779 // FROMTEXTAREA
5780
5781 CodeMirror.fromTextArea = function(textarea, options) {
5782 options = options ? copyObj(options) : {};
5783 options.value = textarea.value;
5784 if (!options.tabindex && textarea.tabIndex)
5785 options.tabindex = textarea.tabIndex;
5786 if (!options.placeholder && textarea.placeholder)
5787 options.placeholder = textarea.placeholder;
5788 // Set autofocus to true if this textarea is focused, or if it has
5789 // autofocus and no other element is focused.
5790 if (options.autofocus == null) {
5791 var hasFocus = activeElt();
5792 options.autofocus = hasFocus == textarea ||
5793 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
5794 }
5795
5796 function save() {textarea.value = cm.getValue();}
5797 if (textarea.form) {
5798 on(textarea.form, "submit", save);
5799 // Deplorable hack to make the submit method do the right thing.
5800 if (!options.leaveSubmitMethodAlone) {
5801 var form = textarea.form, realSubmit = form.submit;
5802 try {
5803 var wrappedSubmit = form.submit = function() {
5804 save();
5805 form.submit = realSubmit;
5806 form.submit();
5807 form.submit = wrappedSubmit;
5808 };
5809 } catch(e) {}
5810 }
5811 }
5812
5813 options.finishInit = function(cm) {
5814 cm.save = save;
5815 cm.getTextArea = function() { return textarea; };
5816 cm.toTextArea = function() {
5817 cm.toTextArea = isNaN; // Prevent this from being ran twice
5818 save();
5819 textarea.parentNode.removeChild(cm.getWrapperElement());
5820 textarea.style.display = "";
5821 if (textarea.form) {
5822 off(textarea.form, "submit", save);
5823 if (typeof textarea.form.submit == "function")
5824 textarea.form.submit = realSubmit;
5825 }
5826 };
5827 };
5828
5829 textarea.style.display = "none";
5830 var cm = CodeMirror(function(node) {
5831 textarea.parentNode.insertBefore(node, textarea.nextSibling);
5832 }, options);
5833 return cm;
5834 };
5835
5836 // STRING STREAM
5837
5838 // Fed to the mode parsers, provides helper functions to make
5839 // parsers more succinct.
5840
5841 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5842 this.pos = this.start = 0;
5843 this.string = string;
5844 this.tabSize = tabSize || 8;
5845 this.lastColumnPos = this.lastColumnValue = 0;
5846 this.lineStart = 0;
5847 };
5848
5849 StringStream.prototype = {
5850 eol: function() {return this.pos >= this.string.length;},
5851 sol: function() {return this.pos == this.lineStart;},
5852 peek: function() {return this.string.charAt(this.pos) || undefined;},
5853 next: function() {
5854 if (this.pos < this.string.length)
5855 return this.string.charAt(this.pos++);
5856 },
5857 eat: function(match) {
5858 var ch = this.string.charAt(this.pos);
5859 if (typeof match == "string") var ok = ch == match;
5860 else var ok = ch && (match.test ? match.test(ch) : match(ch));
5861 if (ok) {++this.pos; return ch;}
5862 },
5863 eatWhile: function(match) {
5864 var start = this.pos;
5865 while (this.eat(match)){}
5866 return this.pos > start;
5867 },
5868 eatSpace: function() {
5869 var start = this.pos;
5870 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
5871 return this.pos > start;
5872 },
5873 skipToEnd: function() {this.pos = this.string.length;},
5874 skipTo: function(ch) {
5875 var found = this.string.indexOf(ch, this.pos);
5876 if (found > -1) {this.pos = found; return true;}
5877 },
5878 backUp: function(n) {this.pos -= n;},
5879 column: function() {
5880 if (this.lastColumnPos < this.start) {
5881 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
5882 this.lastColumnPos = this.start;
5883 }
5884 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5885 },
5886 indentation: function() {
5887 return countColumn(this.string, null, this.tabSize) -
5888 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5889 },
5890 match: function(pattern, consume, caseInsensitive) {
5891 if (typeof pattern == "string") {
5892 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
5893 var substr = this.string.substr(this.pos, pattern.length);
5894 if (cased(substr) == cased(pattern)) {
5895 if (consume !== false) this.pos += pattern.length;
5896 return true;
5897 }
5898 } else {
5899 var match = this.string.slice(this.pos).match(pattern);
5900 if (match && match.index > 0) return null;
5901 if (match && consume !== false) this.pos += match[0].length;
5902 return match;
5903 }
5904 },
5905 current: function(){return this.string.slice(this.start, this.pos);},
5906 hideFirstChars: function(n, inner) {
5907 this.lineStart += n;
5908 try { return inner(); }
5909 finally { this.lineStart -= n; }
5910 }
5911 };
5912
5913 // TEXTMARKERS
5914
5915 // Created with markText and setBookmark methods. A TextMarker is a
5916 // handle that can be used to clear or find a marked position in the
5917 // document. Line objects hold arrays (markedSpans) containing
5918 // {from, to, marker} object pointing to such marker objects, and
5919 // indicating that such a marker is present on that line. Multiple
5920 // lines may point to the same marker when it spans across lines.
5921 // The spans will have null for their from/to properties when the
5922 // marker continues beyond the start/end of the line. Markers have
5923 // links back to the lines they currently touch.
5924
5925 var nextMarkerId = 0;
5926
5927 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
5928 this.lines = [];
5929 this.type = type;
5930 this.doc = doc;
5931 this.id = ++nextMarkerId;
5932 };
5933 eventMixin(TextMarker);
5934
5935 // Clear the marker.
5936 TextMarker.prototype.clear = function() {
5937 if (this.explicitlyCleared) return;
5938 var cm = this.doc.cm, withOp = cm && !cm.curOp;
5939 if (withOp) startOperation(cm);
5940 if (hasHandler(this, "clear")) {
5941 var found = this.find();
5942 if (found) signalLater(this, "clear", found.from, found.to);
5943 }
5944 var min = null, max = null;
5945 for (var i = 0; i < this.lines.length; ++i) {
5946 var line = this.lines[i];
5947 var span = getMarkedSpanFor(line.markedSpans, this);
5948 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
5949 else if (cm) {
5950 if (span.to != null) max = lineNo(line);
5951 if (span.from != null) min = lineNo(line);
5952 }
5953 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5954 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
5955 updateLineHeight(line, textHeight(cm.display));
5956 }
5957 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
5958 var visual = visualLine(this.lines[i]), len = lineLength(visual);
5959 if (len > cm.display.maxLineLength) {
5960 cm.display.maxLine = visual;
5961 cm.display.maxLineLength = len;
5962 cm.display.maxLineChanged = true;
5963 }
5964 }
5965
5966 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
5967 this.lines.length = 0;
5968 this.explicitlyCleared = true;
5969 if (this.atomic && this.doc.cantEdit) {
5970 this.doc.cantEdit = false;
5971 if (cm) reCheckSelection(cm.doc);
5972 }
5973 if (cm) signalLater(cm, "markerCleared", cm, this);
5974 if (withOp) endOperation(cm);
5975 if (this.parent) this.parent.clear();
5976 };
5977
5978 // Find the position of the marker in the document. Returns a {from,
5979 // to} object by default. Side can be passed to get a specific side
5980 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5981 // Pos objects returned contain a line object, rather than a line
5982 // number (used to prevent looking up the same line twice).
5983 TextMarker.prototype.find = function(side, lineObj) {
5984 if (side == null && this.type == "bookmark") side = 1;
5985 var from, to;
5986 for (var i = 0; i < this.lines.length; ++i) {
5987 var line = this.lines[i];
5988 var span = getMarkedSpanFor(line.markedSpans, this);
5989 if (span.from != null) {
5990 from = Pos(lineObj ? line : lineNo(line), span.from);
5991 if (side == -1) return from;
5992 }
5993 if (span.to != null) {
5994 to = Pos(lineObj ? line : lineNo(line), span.to);
5995 if (side == 1) return to;
5996 }
5997 }
5998 return from && {from: from, to: to};
5999 };
6000
6001 // Signals that the marker's widget changed, and surrounding layout
6002 // should be recomputed.
6003 TextMarker.prototype.changed = function() {
6004 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
6005 if (!pos || !cm) return;
6006 runInOp(cm, function() {
6007 var line = pos.line, lineN = lineNo(pos.line);
6008 var view = findViewForLine(cm, lineN);
6009 if (view) {
6010 clearLineMeasurementCacheFor(view);
6011 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
6012 }
6013 cm.curOp.updateMaxLine = true;
6014 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
6015 var oldHeight = widget.height;
6016 widget.height = null;
6017 var dHeight = widgetHeight(widget) - oldHeight;
6018 if (dHeight)
6019 updateLineHeight(line, line.height + dHeight);
6020 }
6021 });
6022 };
6023
6024 TextMarker.prototype.attachLine = function(line) {
6025 if (!this.lines.length && this.doc.cm) {
6026 var op = this.doc.cm.curOp;
6027 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
6028 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
6029 }
6030 this.lines.push(line);
6031 };
6032 TextMarker.prototype.detachLine = function(line) {
6033 this.lines.splice(indexOf(this.lines, line), 1);
6034 if (!this.lines.length && this.doc.cm) {
6035 var op = this.doc.cm.curOp;
6036 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
6037 }
6038 };
6039
6040 // Collapsed markers have unique ids, in order to be able to order
6041 // them, which is needed for uniquely determining an outer marker
6042 // when they overlap (they may nest, but not partially overlap).
6043 var nextMarkerId = 0;
6044
6045 // Create a marker, wire it up to the right lines, and
6046 function markText(doc, from, to, options, type) {
6047 // Shared markers (across linked documents) are handled separately
6048 // (markTextShared will call out to this again, once per
6049 // document).
6050 if (options && options.shared) return markTextShared(doc, from, to, options, type);
6051 // Ensure we are in an operation.
6052 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
6053
6054 var marker = new TextMarker(doc, type), diff = cmp(from, to);
6055 if (options) copyObj(options, marker, false);
6056 // Don't connect empty markers unless clearWhenEmpty is false
6057 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
6058 return marker;
6059 if (marker.replacedWith) {
6060 // Showing up as a widget implies collapsed (widget replaces text)
6061 marker.collapsed = true;
6062 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
6063 if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
6064 if (options.insertLeft) marker.widgetNode.insertLeft = true;
6065 }
6066 if (marker.collapsed) {
6067 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6068 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6069 throw new Error("Inserting collapsed marker partially overlapping an existing one");
6070 sawCollapsedSpans = true;
6071 }
6072
6073 if (marker.addToHistory)
6074 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
6075
6076 var curLine = from.line, cm = doc.cm, updateMaxLine;
6077 doc.iter(curLine, to.line + 1, function(line) {
6078 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6079 updateMaxLine = true;
6080 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6081 addMarkedSpan(line, new MarkedSpan(marker,
6082 curLine == from.line ? from.ch : null,
6083 curLine == to.line ? to.ch : null));
6084 ++curLine;
6085 });
6086 // lineIsHidden depends on the presence of the spans, so needs a second pass
6087 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6088 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6089 });
6090
6091 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
6092
6093 if (marker.readOnly) {
6094 sawReadOnlySpans = true;
6095 if (doc.history.done.length || doc.history.undone.length)
6096 doc.clearHistory();
6097 }
6098 if (marker.collapsed) {
6099 marker.id = ++nextMarkerId;
6100 marker.atomic = true;
6101 }
6102 if (cm) {
6103 // Sync editor state
6104 if (updateMaxLine) cm.curOp.updateMaxLine = true;
6105 if (marker.collapsed)
6106 regChange(cm, from.line, to.line + 1);
6107 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
6108 for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6109 if (marker.atomic) reCheckSelection(cm.doc);
6110 signalLater(cm, "markerAdded", cm, marker);
6111 }
6112 return marker;
6113 }
6114
6115 // SHARED TEXTMARKERS
6116
6117 // A shared marker spans multiple linked documents. It is
6118 // implemented as a meta-marker-object controlling multiple normal
6119 // markers.
6120 var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
6121 this.markers = markers;
6122 this.primary = primary;
6123 for (var i = 0; i < markers.length; ++i)
6124 markers[i].parent = this;
6125 };
6126 eventMixin(SharedTextMarker);
6127
6128 SharedTextMarker.prototype.clear = function() {
6129 if (this.explicitlyCleared) return;
6130 this.explicitlyCleared = true;
6131 for (var i = 0; i < this.markers.length; ++i)
6132 this.markers[i].clear();
6133 signalLater(this, "clear");
6134 };
6135 SharedTextMarker.prototype.find = function(side, lineObj) {
6136 return this.primary.find(side, lineObj);
6137 };
6138
6139 function markTextShared(doc, from, to, options, type) {
6140 options = copyObj(options);
6141 options.shared = false;
6142 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6143 var widget = options.widgetNode;
6144 linkedDocs(doc, function(doc) {
6145 if (widget) options.widgetNode = widget.cloneNode(true);
6146 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6147 for (var i = 0; i < doc.linked.length; ++i)
6148 if (doc.linked[i].isParent) return;
6149 primary = lst(markers);
6150 });
6151 return new SharedTextMarker(markers, primary);
6152 }
6153
6154 function findSharedMarkers(doc) {
6155 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6156 function(m) { return m.parent; });
6157 }
6158
6159 function copySharedMarkers(doc, markers) {
6160 for (var i = 0; i < markers.length; i++) {
6161 var marker = markers[i], pos = marker.find();
6162 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6163 if (cmp(mFrom, mTo)) {
6164 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6165 marker.markers.push(subMark);
6166 subMark.parent = marker;
6167 }
6168 }
6169 }
6170
6171 function detachSharedMarkers(markers) {
6172 for (var i = 0; i < markers.length; i++) {
6173 var marker = markers[i], linked = [marker.primary.doc];;
6174 linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
6175 for (var j = 0; j < marker.markers.length; j++) {
6176 var subMarker = marker.markers[j];
6177 if (indexOf(linked, subMarker.doc) == -1) {
6178 subMarker.parent = null;
6179 marker.markers.splice(j--, 1);
6180 }
6181 }
6182 }
6183 }
6184
6185 // TEXTMARKER SPANS
6186
6187 function MarkedSpan(marker, from, to) {
6188 this.marker = marker;
6189 this.from = from; this.to = to;
6190 }
6191
6192 // Search an array of spans for a span matching the given marker.
6193 function getMarkedSpanFor(spans, marker) {
6194 if (spans) for (var i = 0; i < spans.length; ++i) {
6195 var span = spans[i];
6196 if (span.marker == marker) return span;
6197 }
6198 }
6199 // Remove a span from an array, returning undefined if no spans are
6200 // left (we don't store arrays for lines without spans).
6201 function removeMarkedSpan(spans, span) {
6202 for (var r, i = 0; i < spans.length; ++i)
6203 if (spans[i] != span) (r || (r = [])).push(spans[i]);
6204 return r;
6205 }
6206 // Add a span to a line.
6207 function addMarkedSpan(line, span) {
6208 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
6209 span.marker.attachLine(line);
6210 }
6211
6212 // Used for the algorithm that adjusts markers for a change in the
6213 // document. These functions cut an array of spans at a given
6214 // character position, returning an array of remaining chunks (or
6215 // undefined if nothing remains).
6216 function markedSpansBefore(old, startCh, isInsert) {
6217 if (old) for (var i = 0, nw; i < old.length; ++i) {
6218 var span = old[i], marker = span.marker;
6219 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
6220 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
6221 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
6222 (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
6223 }
6224 }
6225 return nw;
6226 }
6227 function markedSpansAfter(old, endCh, isInsert) {
6228 if (old) for (var i = 0, nw; i < old.length; ++i) {
6229 var span = old[i], marker = span.marker;
6230 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
6231 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
6232 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
6233 (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
6234 span.to == null ? null : span.to - endCh));
6235 }
6236 }
6237 return nw;
6238 }
6239
6240 // Given a change object, compute the new set of marker spans that
6241 // cover the line in which the change took place. Removes spans
6242 // entirely within the change, reconnects spans belonging to the
6243 // same marker that appear on both sides of the change, and cuts off
6244 // spans partially within the change. Returns an array of span
6245 // arrays with one element for each line in (after) the change.
6246 function stretchSpansOverChange(doc, change) {
6247 if (change.full) return null;
6248 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
6249 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
6250 if (!oldFirst && !oldLast) return null;
6251
6252 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
6253 // Get the spans that 'stick out' on both sides
6254 var first = markedSpansBefore(oldFirst, startCh, isInsert);
6255 var last = markedSpansAfter(oldLast, endCh, isInsert);
6256
6257 // Next, merge those two ends
6258 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
6259 if (first) {
6260 // Fix up .to properties of first
6261 for (var i = 0; i < first.length; ++i) {
6262 var span = first[i];
6263 if (span.to == null) {
6264 var found = getMarkedSpanFor(last, span.marker);
6265 if (!found) span.to = startCh;
6266 else if (sameLine) span.to = found.to == null ? null : found.to + offset;
6267 }
6268 }
6269 }
6270 if (last) {
6271 // Fix up .from in last (or move them into first in case of sameLine)
6272 for (var i = 0; i < last.length; ++i) {
6273 var span = last[i];
6274 if (span.to != null) span.to += offset;
6275 if (span.from == null) {
6276 var found = getMarkedSpanFor(first, span.marker);
6277 if (!found) {
6278 span.from = offset;
6279 if (sameLine) (first || (first = [])).push(span);
6280 }
6281 } else {
6282 span.from += offset;
6283 if (sameLine) (first || (first = [])).push(span);
6284 }
6285 }
6286 }
6287 // Make sure we didn't create any zero-length spans
6288 if (first) first = clearEmptySpans(first);
6289 if (last && last != first) last = clearEmptySpans(last);
6290
6291 var newMarkers = [first];
6292 if (!sameLine) {
6293 // Fill gap with whole-line-spans
6294 var gap = change.text.length - 2, gapMarkers;
6295 if (gap > 0 && first)
6296 for (var i = 0; i < first.length; ++i)
6297 if (first[i].to == null)
6298 (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
6299 for (var i = 0; i < gap; ++i)
6300 newMarkers.push(gapMarkers);
6301 newMarkers.push(last);
6302 }
6303 return newMarkers;
6304 }
6305
6306 // Remove spans that are empty and don't have a clearWhenEmpty
6307 // option of false.
6308 function clearEmptySpans(spans) {
6309 for (var i = 0; i < spans.length; ++i) {
6310 var span = spans[i];
6311 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
6312 spans.splice(i--, 1);
6313 }
6314 if (!spans.length) return null;
6315 return spans;
6316 }
6317
6318 // Used for un/re-doing changes from the history. Combines the
6319 // result of computing the existing spans with the set of spans that
6320 // existed in the history (so that deleting around a span and then
6321 // undoing brings back the span).
6322 function mergeOldSpans(doc, change) {
6323 var old = getOldSpans(doc, change);
6324 var stretched = stretchSpansOverChange(doc, change);
6325 if (!old) return stretched;
6326 if (!stretched) return old;
6327
6328 for (var i = 0; i < old.length; ++i) {
6329 var oldCur = old[i], stretchCur = stretched[i];
6330 if (oldCur && stretchCur) {
6331 spans: for (var j = 0; j < stretchCur.length; ++j) {
6332 var span = stretchCur[j];
6333 for (var k = 0; k < oldCur.length; ++k)
6334 if (oldCur[k].marker == span.marker) continue spans;
6335 oldCur.push(span);
6336 }
6337 } else if (stretchCur) {
6338 old[i] = stretchCur;
6339 }
6340 }
6341 return old;
6342 }
6343
6344 // Used to 'clip' out readOnly ranges when making a change.
6345 function removeReadOnlyRanges(doc, from, to) {
6346 var markers = null;
6347 doc.iter(from.line, to.line + 1, function(line) {
6348 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
6349 var mark = line.markedSpans[i].marker;
6350 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
6351 (markers || (markers = [])).push(mark);
6352 }
6353 });
6354 if (!markers) return null;
6355 var parts = [{from: from, to: to}];
6356 for (var i = 0; i < markers.length; ++i) {
6357 var mk = markers[i], m = mk.find(0);
6358 for (var j = 0; j < parts.length; ++j) {
6359 var p = parts[j];
6360 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
6361 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
6362 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
6363 newParts.push({from: p.from, to: m.from});
6364 if (dto > 0 || !mk.inclusiveRight && !dto)
6365 newParts.push({from: m.to, to: p.to});
6366 parts.splice.apply(parts, newParts);
6367 j += newParts.length - 1;
6368 }
6369 }
6370 return parts;
6371 }
6372
6373 // Connect or disconnect spans from a line.
6374 function detachMarkedSpans(line) {
6375 var spans = line.markedSpans;
6376 if (!spans) return;
6377 for (var i = 0; i < spans.length; ++i)
6378 spans[i].marker.detachLine(line);
6379 line.markedSpans = null;
6380 }
6381 function attachMarkedSpans(line, spans) {
6382 if (!spans) return;
6383 for (var i = 0; i < spans.length; ++i)
6384 spans[i].marker.attachLine(line);
6385 line.markedSpans = spans;
6386 }
6387
6388 // Helpers used when computing which overlapping collapsed span
6389 // counts as the larger one.
6390 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
6391 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
6392
6393 // Returns a number indicating which of two overlapping collapsed
6394 // spans is larger (and thus includes the other). Falls back to
6395 // comparing ids when the spans cover exactly the same range.
6396 function compareCollapsedMarkers(a, b) {
6397 var lenDiff = a.lines.length - b.lines.length;
6398 if (lenDiff != 0) return lenDiff;
6399 var aPos = a.find(), bPos = b.find();
6400 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6401 if (fromCmp) return -fromCmp;
6402 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6403 if (toCmp) return toCmp;
6404 return b.id - a.id;
6405 }
6406
6407 // Find out whether a line ends or starts in a collapsed span. If
6408 // so, return the marker for that span.
6409 function collapsedSpanAtSide(line, start) {
6410 var sps = sawCollapsedSpans && line.markedSpans, found;
6411 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6412 sp = sps[i];
6413 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
6414 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6415 found = sp.marker;
6416 }
6417 return found;
6418 }
6419 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6420 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6421
6422 // Test whether there exists a collapsed span that partially
6423 // overlaps (covers the start or end, but not both) of a new span.
6424 // Such overlap is not allowed.
6425 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6426 var line = getLine(doc, lineNo);
6427 var sps = sawCollapsedSpans && line.markedSpans;
6428 if (sps) for (var i = 0; i < sps.length; ++i) {
6429 var sp = sps[i];
6430 if (!sp.marker.collapsed) continue;
6431 var found = sp.marker.find(0);
6432 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
6433 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
6434 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6435 if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
6436 fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
6437 return true;
6438 }
6439 }
6440
6441 // A visual line is a line as drawn on the screen. Folding, for
6442 // example, can cause multiple logical lines to appear on the same
6443 // visual line. This finds the start of the visual line that the
6444 // given line is part of (usually that is the line itself).
6445 function visualLine(line) {
6446 var merged;
6447 while (merged = collapsedSpanAtStart(line))
6448 line = merged.find(-1, true).line;
6449 return line;
6450 }
6451
6452 // Returns an array of logical lines that continue the visual line
6453 // started by the argument, or undefined if there are no such lines.
6454 function visualLineContinued(line) {
6455 var merged, lines;
6456 while (merged = collapsedSpanAtEnd(line)) {
6457 line = merged.find(1, true).line;
6458 (lines || (lines = [])).push(line);
6459 }
6460 return lines;
6461 }
6462
6463 // Get the line number of the start of the visual line that the
6464 // given line number is part of.
6465 function visualLineNo(doc, lineN) {
6466 var line = getLine(doc, lineN), vis = visualLine(line);
6467 if (line == vis) return lineN;
6468 return lineNo(vis);
6469 }
6470 // Get the line number of the start of the next visual line after
6471 // the given line.
6472 function visualLineEndNo(doc, lineN) {
6473 if (lineN > doc.lastLine()) return lineN;
6474 var line = getLine(doc, lineN), merged;
6475 if (!lineIsHidden(doc, line)) return lineN;
6476 while (merged = collapsedSpanAtEnd(line))
6477 line = merged.find(1, true).line;
6478 return lineNo(line) + 1;
6479 }
6480
6481 // Compute whether a line is hidden. Lines count as hidden when they
6482 // are part of a visual line that starts with another line, or when
6483 // they are entirely covered by collapsed, non-widget span.
6484 function lineIsHidden(doc, line) {
6485 var sps = sawCollapsedSpans && line.markedSpans;
6486 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6487 sp = sps[i];
6488 if (!sp.marker.collapsed) continue;
6489 if (sp.from == null) return true;
6490 if (sp.marker.widgetNode) continue;
6491 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
6492 return true;
6493 }
6494 }
6495 function lineIsHiddenInner(doc, line, span) {
6496 if (span.to == null) {
6497 var end = span.marker.find(1, true);
6498 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
6499 }
6500 if (span.marker.inclusiveRight && span.to == line.text.length)
6501 return true;
6502 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6503 sp = line.markedSpans[i];
6504 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6505 (sp.to == null || sp.to != span.from) &&
6506 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6507 lineIsHiddenInner(doc, line, sp)) return true;
6508 }
6509 }
6510
6511 // LINE WIDGETS
6512
6513 // Line widgets are block elements displayed above or below a line.
6514
6515 var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
6516 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6517 this[opt] = options[opt];
6518 this.doc = doc;
6519 this.node = node;
6520 };
6521 eventMixin(LineWidget);
6522
6523 function adjustScrollWhenAboveVisible(cm, line, diff) {
6524 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
6525 addToScrollPos(cm, null, diff);
6526 }
6527
6528 LineWidget.prototype.clear = function() {
6529 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
6530 if (no == null || !ws) return;
6531 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
6532 if (!ws.length) line.widgets = null;
6533 var height = widgetHeight(this);
6534 updateLineHeight(line, Math.max(0, line.height - height));
6535 if (cm) runInOp(cm, function() {
6536 adjustScrollWhenAboveVisible(cm, line, -height);
6537 regLineChange(cm, no, "widget");
6538 });
6539 };
6540 LineWidget.prototype.changed = function() {
6541 var oldH = this.height, cm = this.doc.cm, line = this.line;
6542 this.height = null;
6543 var diff = widgetHeight(this) - oldH;
6544 if (!diff) return;
6545 updateLineHeight(line, line.height + diff);
6546 if (cm) runInOp(cm, function() {
6547 cm.curOp.forceUpdate = true;
6548 adjustScrollWhenAboveVisible(cm, line, diff);
6549 });
6550 };
6551
6552 function widgetHeight(widget) {
6553 if (widget.height != null) return widget.height;
6554 var cm = widget.doc.cm;
6555 if (!cm) return 0;
6556 if (!contains(document.body, widget.node)) {
6557 var parentStyle = "position: relative;";
6558 if (widget.coverGutter)
6559 parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
6560 if (widget.noHScroll)
6561 parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
6562 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
6563 }
6564 return widget.height = widget.node.offsetHeight;
6565 }
6566
6567 function addLineWidget(doc, handle, node, options) {
6568 var widget = new LineWidget(doc, node, options);
6569 var cm = doc.cm;
6570 if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6571 changeLine(doc, handle, "widget", function(line) {
6572 var widgets = line.widgets || (line.widgets = []);
6573 if (widget.insertAt == null) widgets.push(widget);
6574 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
6575 widget.line = line;
6576 if (cm && !lineIsHidden(doc, line)) {
6577 var aboveVisible = heightAtLine(line) < doc.scrollTop;
6578 updateLineHeight(line, line.height + widgetHeight(widget));
6579 if (aboveVisible) addToScrollPos(cm, null, widget.height);
6580 cm.curOp.forceUpdate = true;
6581 }
6582 return true;
6583 });
6584 return widget;
6585 }
6586
6587 // LINE DATA STRUCTURE
6588
6589 // Line objects. These hold state related to a line, including
6590 // highlighting info (the styles array).
6591 var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6592 this.text = text;
6593 attachMarkedSpans(this, markedSpans);
6594 this.height = estimateHeight ? estimateHeight(this) : 1;
6595 };
6596 eventMixin(Line);
6597 Line.prototype.lineNo = function() { return lineNo(this); };
6598
6599 // Change the content (text, markers) of a line. Automatically
6600 // invalidates cached information and tries to re-estimate the
6601 // line's height.
6602 function updateLine(line, text, markedSpans, estimateHeight) {
6603 line.text = text;
6604 if (line.stateAfter) line.stateAfter = null;
6605 if (line.styles) line.styles = null;
6606 if (line.order != null) line.order = null;
6607 detachMarkedSpans(line);
6608 attachMarkedSpans(line, markedSpans);
6609 var estHeight = estimateHeight ? estimateHeight(line) : 1;
6610 if (estHeight != line.height) updateLineHeight(line, estHeight);
6611 }
6612
6613 // Detach a line from the document tree and its markers.
6614 function cleanUpLine(line) {
6615 line.parent = null;
6616 detachMarkedSpans(line);
6617 }
6618
6619 function extractLineClasses(type, output) {
6620 if (type) for (;;) {
6621 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6622 if (!lineClass) break;
6623 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
6624 var prop = lineClass[1] ? "bgClass" : "textClass";
6625 if (output[prop] == null)
6626 output[prop] = lineClass[2];
6627 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
6628 output[prop] += " " + lineClass[2];
6629 }
6630 return type;
6631 }
6632
6633 function callBlankLine(mode, state) {
6634 if (mode.blankLine) return mode.blankLine(state);
6635 if (!mode.innerMode) return;
6636 var inner = CodeMirror.innerMode(mode, state);
6637 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
6638 }
6639
6640 function readToken(mode, stream, state, inner) {
6641 for (var i = 0; i < 10; i++) {
6642 if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6643 var style = mode.token(stream, state);
6644 if (stream.pos > stream.start) return style;
6645 }
6646 throw new Error("Mode " + mode.name + " failed to advance stream.");
6647 }
6648
6649 // Utility for getTokenAt and getLineTokens
6650 function takeToken(cm, pos, precise, asArray) {
6651 function getObj(copy) {
6652 return {start: stream.start, end: stream.pos,
6653 string: stream.current(),
6654 type: style || null,
6655 state: copy ? copyState(doc.mode, state) : state};
6656 }
6657
6658 var doc = cm.doc, mode = doc.mode, style;
6659 pos = clipPos(doc, pos);
6660 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
6661 var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6662 if (asArray) tokens = [];
6663 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6664 stream.start = stream.pos;
6665 style = readToken(mode, stream, state);
6666 if (asArray) tokens.push(getObj(true));
6667 }
6668 return asArray ? tokens : getObj();
6669 }
6670
6671 // Run the given mode's parser over a line, calling f for each token.
6672 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6673 var flattenSpans = mode.flattenSpans;
6674 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
6675 var curStart = 0, curStyle = null;
6676 var stream = new StringStream(text, cm.options.tabSize), style;
6677 var inner = cm.options.addModeClass && [null];
6678 if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6679 while (!stream.eol()) {
6680 if (stream.pos > cm.options.maxHighlightLength) {
6681 flattenSpans = false;
6682 if (forceToEnd) processLine(cm, text, state, stream.pos);
6683 stream.pos = text.length;
6684 style = null;
6685 } else {
6686 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
6687 }
6688 if (inner) {
6689 var mName = inner[0].name;
6690 if (mName) style = "m-" + (style ? mName + " " + style : mName);
6691 }
6692 if (!flattenSpans || curStyle != style) {
6693 while (curStart < stream.start) {
6694 curStart = Math.min(stream.start, curStart + 50000);
6695 f(curStart, curStyle);
6696 }
6697 curStyle = style;
6698 }
6699 stream.start = stream.pos;
6700 }
6701 while (curStart < stream.pos) {
6702 // Webkit seems to refuse to render text nodes longer than 57444 characters
6703 var pos = Math.min(stream.pos, curStart + 50000);
6704 f(pos, curStyle);
6705 curStart = pos;
6706 }
6707 }
6708
6709 // Compute a style array (an array starting with a mode generation
6710 // -- for invalidation -- followed by pairs of end positions and
6711 // style strings), which is used to highlight the tokens on the
6712 // line.
6713 function highlightLine(cm, line, state, forceToEnd) {
6714 // A styles array always starts with a number identifying the
6715 // mode/overlays that it is based on (for easy invalidation).
6716 var st = [cm.state.modeGen], lineClasses = {};
6717 // Compute the base array of styles
6718 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6719 st.push(end, style);
6720 }, lineClasses, forceToEnd);
6721
6722 // Run overlays, adjust style array.
6723 for (var o = 0; o < cm.state.overlays.length; ++o) {
6724 var overlay = cm.state.overlays[o], i = 1, at = 0;
6725 runMode(cm, line.text, overlay.mode, true, function(end, style) {
6726 var start = i;
6727 // Ensure there's a token end at the current position, and that i points at it
6728 while (at < end) {
6729 var i_end = st[i];
6730 if (i_end > end)
6731 st.splice(i, 1, end, st[i+1], i_end);
6732 i += 2;
6733 at = Math.min(end, i_end);
6734 }
6735 if (!style) return;
6736 if (overlay.opaque) {
6737 st.splice(start, i - start, end, "cm-overlay " + style);
6738 i = start + 2;
6739 } else {
6740 for (; start < i; start += 2) {
6741 var cur = st[start+1];
6742 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6743 }
6744 }
6745 }, lineClasses);
6746 }
6747
6748 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6749 }
6750
6751 function getLineStyles(cm, line, updateFrontier) {
6752 if (!line.styles || line.styles[0] != cm.state.modeGen) {
6753 var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
6754 line.styles = result.styles;
6755 if (result.classes) line.styleClasses = result.classes;
6756 else if (line.styleClasses) line.styleClasses = null;
6757 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6758 }
6759 return line.styles;
6760 }
6761
6762 // Lightweight form of highlight -- proceed over this line and
6763 // update state, but don't save a style array. Used for lines that
6764 // aren't currently visible.
6765 function processLine(cm, text, state, startAt) {
6766 var mode = cm.doc.mode;
6767 var stream = new StringStream(text, cm.options.tabSize);
6768 stream.start = stream.pos = startAt || 0;
6769 if (text == "") callBlankLine(mode, state);
6770 while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
6771 readToken(mode, stream, state);
6772 stream.start = stream.pos;
6773 }
6774 }
6775
6776 // Convert a style as returned by a mode (either null, or a string
6777 // containing one or more styles) to a CSS style. This is cached,
6778 // and also looks for line-wide styles.
6779 var styleToClassCache = {}, styleToClassCacheWithMode = {};
6780 function interpretTokenStyle(style, options) {
6781 if (!style || /^\s*$/.test(style)) return null;
6782 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
6783 return cache[style] ||
6784 (cache[style] = style.replace(/\S+/g, "cm-$&"));
6785 }
6786
6787 // Render the DOM representation of the text of a line. Also builds
6788 // up a 'line map', which points at the DOM nodes that represent
6789 // specific stretches of text, and is used by the measuring code.
6790 // The returned object contains the DOM node, this map, and
6791 // information about line-wide styles that were set by the mode.
6792 function buildLineContent(cm, lineView) {
6793 // The padding-right forces the element to have a 'border', which
6794 // is needed on Webkit to be able to get line-level bounding
6795 // rectangles for it (in measureChar).
6796 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
6797 var builder = {pre: elt("pre", [content]), content: content,
6798 col: 0, pos: 0, cm: cm,
6799 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6800 lineView.measure = {};
6801
6802 // Iterate over the logical lines that make up this visual line.
6803 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6804 var line = i ? lineView.rest[i - 1] : lineView.line, order;
6805 builder.pos = 0;
6806 builder.addToken = buildToken;
6807 // Optionally wire in some hacks into the token-rendering
6808 // algorithm, to deal with browser quirks.
6809 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6810 builder.addToken = buildTokenBadBidi(builder.addToken, order);
6811 builder.map = [];
6812 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6813 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
6814 if (line.styleClasses) {
6815 if (line.styleClasses.bgClass)
6816 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
6817 if (line.styleClasses.textClass)
6818 builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
6819 }
6820
6821 // Ensure at least a single node is present, for measuring.
6822 if (builder.map.length == 0)
6823 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
6824
6825 // Store the map and a cache object for the current logical line
6826 if (i == 0) {
6827 lineView.measure.map = builder.map;
6828 lineView.measure.cache = {};
6829 } else {
6830 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
6831 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6832 }
6833 }
6834
6835 // See issue #2901
6836 if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6837 builder.content.className = "cm-tab-wrap-hack";
6838
6839 signal(cm, "renderLine", cm, lineView.line, builder.pre);
6840 if (builder.pre.className)
6841 builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6842
6843 return builder;
6844 }
6845
6846 function defaultSpecialCharPlaceholder(ch) {
6847 var token = elt("span", "\u2022", "cm-invalidchar");
6848 token.title = "\\u" + ch.charCodeAt(0).toString(16);
6849 token.setAttribute("aria-label", token.title);
6850 return token;
6851 }
6852
6853 // Build up the DOM representation for a single token, and add it to
6854 // the line map. Takes care to render special characters separately.
6855 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6856 if (!text) return;
6857 var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
6858 var special = builder.cm.state.specialChars, mustWrap = false;
6859 if (!special.test(text)) {
6860 builder.col += text.length;
6861 var content = document.createTextNode(displayText);
6862 builder.map.push(builder.pos, builder.pos + text.length, content);
6863 if (ie && ie_version < 9) mustWrap = true;
6864 builder.pos += text.length;
6865 } else {
6866 var content = document.createDocumentFragment(), pos = 0;
6867 while (true) {
6868 special.lastIndex = pos;
6869 var m = special.exec(text);
6870 var skipped = m ? m.index - pos : text.length - pos;
6871 if (skipped) {
6872 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
6873 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6874 else content.appendChild(txt);
6875 builder.map.push(builder.pos, builder.pos + skipped, txt);
6876 builder.col += skipped;
6877 builder.pos += skipped;
6878 }
6879 if (!m) break;
6880 pos += skipped + 1;
6881 if (m[0] == "\t") {
6882 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
6883 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
6884 txt.setAttribute("role", "presentation");
6885 txt.setAttribute("cm-text", "\t");
6886 builder.col += tabWidth;
6887 } else {
6888 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
6889 txt.setAttribute("cm-text", m[0]);
6890 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6891 else content.appendChild(txt);
6892 builder.col += 1;
6893 }
6894 builder.map.push(builder.pos, builder.pos + 1, txt);
6895 builder.pos++;
6896 }
6897 }
6898 if (style || startStyle || endStyle || mustWrap || css) {
6899 var fullStyle = style || "";
6900 if (startStyle) fullStyle += startStyle;
6901 if (endStyle) fullStyle += endStyle;
6902 var token = elt("span", [content], fullStyle, css);
6903 if (title) token.title = title;
6904 return builder.content.appendChild(token);
6905 }
6906 builder.content.appendChild(content);
6907 }
6908
6909 function splitSpaces(old) {
6910 var out = " ";
6911 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
6912 out += " ";
6913 return out;
6914 }
6915
6916 // Work around nonsense dimensions being reported for stretches of
6917 // right-to-left text.
6918 function buildTokenBadBidi(inner, order) {
6919 return function(builder, text, style, startStyle, endStyle, title, css) {
6920 style = style ? style + " cm-force-border" : "cm-force-border";
6921 var start = builder.pos, end = start + text.length;
6922 for (;;) {
6923 // Find the part that overlaps with the start of this text
6924 for (var i = 0; i < order.length; i++) {
6925 var part = order[i];
6926 if (part.to > start && part.from <= start) break;
6927 }
6928 if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
6929 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
6930 startStyle = null;
6931 text = text.slice(part.to - start);
6932 start = part.to;
6933 }
6934 };
6935 }
6936
6937 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
6938 var widget = !ignoreWidget && marker.widgetNode;
6939 if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
6940 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
6941 if (!widget)
6942 widget = builder.content.appendChild(document.createElement("span"));
6943 widget.setAttribute("cm-marker", marker.id);
6944 }
6945 if (widget) {
6946 builder.cm.display.input.setUneditable(widget);
6947 builder.content.appendChild(widget);
6948 }
6949 builder.pos += size;
6950 }
6951
6952 // Outputs a number of spans to make up a line, taking highlighting
6953 // and marked text into account.
6954 function insertLineContent(line, builder, styles) {
6955 var spans = line.markedSpans, allText = line.text, at = 0;
6956 if (!spans) {
6957 for (var i = 1; i < styles.length; i+=2)
6958 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
6959 return;
6960 }
6961
6962 var len = allText.length, pos = 0, i = 1, text = "", style, css;
6963 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
6964 for (;;) {
6965 if (nextChange == pos) { // Update current marker set
6966 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
6967 collapsed = null; nextChange = Infinity;
6968 var foundBookmarks = [];
6969 for (var j = 0; j < spans.length; ++j) {
6970 var sp = spans[j], m = sp.marker;
6971 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
6972 foundBookmarks.push(m);
6973 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
6974 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
6975 nextChange = sp.to;
6976 spanEndStyle = "";
6977 }
6978 if (m.className) spanStyle += " " + m.className;
6979 if (m.css) css = m.css;
6980 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
6981 if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
6982 if (m.title && !title) title = m.title;
6983 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
6984 collapsed = sp;
6985 } else if (sp.from > pos && nextChange > sp.from) {
6986 nextChange = sp.from;
6987 }
6988 }
6989 if (collapsed && (collapsed.from || 0) == pos) {
6990 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
6991 collapsed.marker, collapsed.from == null);
6992 if (collapsed.to == null) return;
6993 if (collapsed.to == pos) collapsed = false;
6994 }
6995 if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
6996 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
6997 }
6998 if (pos >= len) break;
6999
7000 var upto = Math.min(len, nextChange);
7001 while (true) {
7002 if (text) {
7003 var end = pos + text.length;
7004 if (!collapsed) {
7005 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
7006 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
7007 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
7008 }
7009 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
7010 pos = end;
7011 spanStartStyle = "";
7012 }
7013 text = allText.slice(at, at = styles[i++]);
7014 style = interpretTokenStyle(styles[i++], builder.cm.options);
7015 }
7016 }
7017 }
7018
7019 // DOCUMENT DATA STRUCTURE
7020
7021 // By default, updates that start and end at the beginning of a line
7022 // are treated specially, in order to make the association of line
7023 // widgets and marker elements with the text behave more intuitive.
7024 function isWholeLineUpdate(doc, change) {
7025 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
7026 (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
7027 }
7028
7029 // Perform a change on the document data structure.
7030 function updateDoc(doc, change, markedSpans, estimateHeight) {
7031 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
7032 function update(line, text, spans) {
7033 updateLine(line, text, spans, estimateHeight);
7034 signalLater(line, "change", line, change);
7035 }
7036 function linesFor(start, end) {
7037 for (var i = start, result = []; i < end; ++i)
7038 result.push(new Line(text[i], spansFor(i), estimateHeight));
7039 return result;
7040 }
7041
7042 var from = change.from, to = change.to, text = change.text;
7043 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
7044 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
7045
7046 // Adjust the line structure
7047 if (change.full) {
7048 doc.insert(0, linesFor(0, text.length));
7049 doc.remove(text.length, doc.size - text.length);
7050 } else if (isWholeLineUpdate(doc, change)) {
7051 // This is a whole-line replace. Treated specially to make
7052 // sure line objects move the way they are supposed to.
7053 var added = linesFor(0, text.length - 1);
7054 update(lastLine, lastLine.text, lastSpans);
7055 if (nlines) doc.remove(from.line, nlines);
7056 if (added.length) doc.insert(from.line, added);
7057 } else if (firstLine == lastLine) {
7058 if (text.length == 1) {
7059 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
7060 } else {
7061 var added = linesFor(1, text.length - 1);
7062 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
7063 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7064 doc.insert(from.line + 1, added);
7065 }
7066 } else if (text.length == 1) {
7067 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
7068 doc.remove(from.line + 1, nlines);
7069 } else {
7070 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7071 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
7072 var added = linesFor(1, text.length - 1);
7073 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
7074 doc.insert(from.line + 1, added);
7075 }
7076
7077 signalLater(doc, "change", doc, change);
7078 }
7079
7080 // The document is represented as a BTree consisting of leaves, with
7081 // chunk of lines in them, and branches, with up to ten leaves or
7082 // other branch nodes below them. The top node is always a branch
7083 // node, and is the document object itself (meaning it has
7084 // additional methods and properties).
7085 //
7086 // All nodes have parent links. The tree is used both to go from
7087 // line numbers to line objects, and to go from objects to numbers.
7088 // It also indexes by height, and is used to convert between height
7089 // and line object, and to find the total height of the document.
7090 //
7091 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7092
7093 function LeafChunk(lines) {
7094 this.lines = lines;
7095 this.parent = null;
7096 for (var i = 0, height = 0; i < lines.length; ++i) {
7097 lines[i].parent = this;
7098 height += lines[i].height;
7099 }
7100 this.height = height;
7101 }
7102
7103 LeafChunk.prototype = {
7104 chunkSize: function() { return this.lines.length; },
7105 // Remove the n lines at offset 'at'.
7106 removeInner: function(at, n) {
7107 for (var i = at, e = at + n; i < e; ++i) {
7108 var line = this.lines[i];
7109 this.height -= line.height;
7110 cleanUpLine(line);
7111 signalLater(line, "delete");
7112 }
7113 this.lines.splice(at, n);
7114 },
7115 // Helper used to collapse a small branch into a single leaf.
7116 collapse: function(lines) {
7117 lines.push.apply(lines, this.lines);
7118 },
7119 // Insert the given array of lines at offset 'at', count them as
7120 // having the given height.
7121 insertInner: function(at, lines, height) {
7122 this.height += height;
7123 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7124 for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7125 },
7126 // Used to iterate over a part of the tree.
7127 iterN: function(at, n, op) {
7128 for (var e = at + n; at < e; ++at)
7129 if (op(this.lines[at])) return true;
7130 }
7131 };
7132
7133 function BranchChunk(children) {
7134 this.children = children;
7135 var size = 0, height = 0;
7136 for (var i = 0; i < children.length; ++i) {
7137 var ch = children[i];
7138 size += ch.chunkSize(); height += ch.height;
7139 ch.parent = this;
7140 }
7141 this.size = size;
7142 this.height = height;
7143 this.parent = null;
7144 }
7145
7146 BranchChunk.prototype = {
7147 chunkSize: function() { return this.size; },
7148 removeInner: function(at, n) {
7149 this.size -= n;
7150 for (var i = 0; i < this.children.length; ++i) {
7151 var child = this.children[i], sz = child.chunkSize();
7152 if (at < sz) {
7153 var rm = Math.min(n, sz - at), oldHeight = child.height;
7154 child.removeInner(at, rm);
7155 this.height -= oldHeight - child.height;
7156 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
7157 if ((n -= rm) == 0) break;
7158 at = 0;
7159 } else at -= sz;
7160 }
7161 // If the result is smaller than 25 lines, ensure that it is a
7162 // single leaf node.
7163 if (this.size - n < 25 &&
7164 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7165 var lines = [];
7166 this.collapse(lines);
7167 this.children = [new LeafChunk(lines)];
7168 this.children[0].parent = this;
7169 }
7170 },
7171 collapse: function(lines) {
7172 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
7173 },
7174 insertInner: function(at, lines, height) {
7175 this.size += lines.length;
7176 this.height += height;
7177 for (var i = 0; i < this.children.length; ++i) {
7178 var child = this.children[i], sz = child.chunkSize();
7179 if (at <= sz) {
7180 child.insertInner(at, lines, height);
7181 if (child.lines && child.lines.length > 50) {
7182 while (child.lines.length > 50) {
7183 var spilled = child.lines.splice(child.lines.length - 25, 25);
7184 var newleaf = new LeafChunk(spilled);
7185 child.height -= newleaf.height;
7186 this.children.splice(i + 1, 0, newleaf);
7187 newleaf.parent = this;
7188 }
7189 this.maybeSpill();
7190 }
7191 break;
7192 }
7193 at -= sz;
7194 }
7195 },
7196 // When a node has grown, check whether it should be split.
7197 maybeSpill: function() {
7198 if (this.children.length <= 10) return;
7199 var me = this;
7200 do {
7201 var spilled = me.children.splice(me.children.length - 5, 5);
7202 var sibling = new BranchChunk(spilled);
7203 if (!me.parent) { // Become the parent node
7204 var copy = new BranchChunk(me.children);
7205 copy.parent = me;
7206 me.children = [copy, sibling];
7207 me = copy;
7208 } else {
7209 me.size -= sibling.size;
7210 me.height -= sibling.height;
7211 var myIndex = indexOf(me.parent.children, me);
7212 me.parent.children.splice(myIndex + 1, 0, sibling);
7213 }
7214 sibling.parent = me.parent;
7215 } while (me.children.length > 10);
7216 me.parent.maybeSpill();
7217 },
7218 iterN: function(at, n, op) {
7219 for (var i = 0; i < this.children.length; ++i) {
7220 var child = this.children[i], sz = child.chunkSize();
7221 if (at < sz) {
7222 var used = Math.min(n, sz - at);
7223 if (child.iterN(at, used, op)) return true;
7224 if ((n -= used) == 0) break;
7225 at = 0;
7226 } else at -= sz;
7227 }
7228 }
7229 };
7230
7231 var nextDocId = 0;
7232 var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
7233 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
7234 if (firstLine == null) firstLine = 0;
7235
7236 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7237 this.first = firstLine;
7238 this.scrollTop = this.scrollLeft = 0;
7239 this.cantEdit = false;
7240 this.cleanGeneration = 1;
7241 this.frontier = firstLine;
7242 var start = Pos(firstLine, 0);
7243 this.sel = simpleSelection(start);
7244 this.history = new History(null);
7245 this.id = ++nextDocId;
7246 this.modeOption = mode;
7247
7248 if (typeof text == "string") text = splitLines(text);
7249 updateDoc(this, {from: start, to: start, text: text});
7250 setSelection(this, simpleSelection(start), sel_dontScroll);
7251 };
7252
7253 Doc.prototype = createObj(BranchChunk.prototype, {
7254 constructor: Doc,
7255 // Iterate over the document. Supports two forms -- with only one
7256 // argument, it calls that for each line in the document. With
7257 // three, it iterates over the range given by the first two (with
7258 // the second being non-inclusive).
7259 iter: function(from, to, op) {
7260 if (op) this.iterN(from - this.first, to - from, op);
7261 else this.iterN(this.first, this.first + this.size, from);
7262 },
7263
7264 // Non-public interface for adding and removing lines.
7265 insert: function(at, lines) {
7266 var height = 0;
7267 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7268 this.insertInner(at - this.first, lines, height);
7269 },
7270 remove: function(at, n) { this.removeInner(at - this.first, n); },
7271
7272 // From here, the methods are part of the public interface. Most
7273 // are also available from CodeMirror (editor) instances.
7274
7275 getValue: function(lineSep) {
7276 var lines = getLines(this, this.first, this.first + this.size);
7277 if (lineSep === false) return lines;
7278 return lines.join(lineSep || "\n");
7279 },
7280 setValue: docMethodOp(function(code) {
7281 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7282 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7283 text: splitLines(code), origin: "setValue", full: true}, true);
7284 setSelection(this, simpleSelection(top));
7285 }),
7286 replaceRange: function(code, from, to, origin) {
7287 from = clipPos(this, from);
7288 to = to ? clipPos(this, to) : from;
7289 replaceRange(this, code, from, to, origin);
7290 },
7291 getRange: function(from, to, lineSep) {
7292 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7293 if (lineSep === false) return lines;
7294 return lines.join(lineSep || "\n");
7295 },
7296
7297 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
7298
7299 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
7300 getLineNumber: function(line) {return lineNo(line);},
7301
7302 getLineHandleVisualStart: function(line) {
7303 if (typeof line == "number") line = getLine(this, line);
7304 return visualLine(line);
7305 },
7306
7307 lineCount: function() {return this.size;},
7308 firstLine: function() {return this.first;},
7309 lastLine: function() {return this.first + this.size - 1;},
7310
7311 clipPos: function(pos) {return clipPos(this, pos);},
7312
7313 getCursor: function(start) {
7314 var range = this.sel.primary(), pos;
7315 if (start == null || start == "head") pos = range.head;
7316 else if (start == "anchor") pos = range.anchor;
7317 else if (start == "end" || start == "to" || start === false) pos = range.to();
7318 else pos = range.from();
7319 return pos;
7320 },
7321 listSelections: function() { return this.sel.ranges; },
7322 somethingSelected: function() {return this.sel.somethingSelected();},
7323
7324 setCursor: docMethodOp(function(line, ch, options) {
7325 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7326 }),
7327 setSelection: docMethodOp(function(anchor, head, options) {
7328 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7329 }),
7330 extendSelection: docMethodOp(function(head, other, options) {
7331 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7332 }),
7333 extendSelections: docMethodOp(function(heads, options) {
7334 extendSelections(this, clipPosArray(this, heads, options));
7335 }),
7336 extendSelectionsBy: docMethodOp(function(f, options) {
7337 extendSelections(this, map(this.sel.ranges, f), options);
7338 }),
7339 setSelections: docMethodOp(function(ranges, primary, options) {
7340 if (!ranges.length) return;
7341 for (var i = 0, out = []; i < ranges.length; i++)
7342 out[i] = new Range(clipPos(this, ranges[i].anchor),
7343 clipPos(this, ranges[i].head));
7344 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
7345 setSelection(this, normalizeSelection(out, primary), options);
7346 }),
7347 addSelection: docMethodOp(function(anchor, head, options) {
7348 var ranges = this.sel.ranges.slice(0);
7349 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7350 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7351 }),
7352
7353 getSelection: function(lineSep) {
7354 var ranges = this.sel.ranges, lines;
7355 for (var i = 0; i < ranges.length; i++) {
7356 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7357 lines = lines ? lines.concat(sel) : sel;
7358 }
7359 if (lineSep === false) return lines;
7360 else return lines.join(lineSep || "\n");
7361 },
7362 getSelections: function(lineSep) {
7363 var parts = [], ranges = this.sel.ranges;
7364 for (var i = 0; i < ranges.length; i++) {
7365 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7366 if (lineSep !== false) sel = sel.join(lineSep || "\n");
7367 parts[i] = sel;
7368 }
7369 return parts;
7370 },
7371 replaceSelection: function(code, collapse, origin) {
7372 var dup = [];
7373 for (var i = 0; i < this.sel.ranges.length; i++)
7374 dup[i] = code;
7375 this.replaceSelections(dup, collapse, origin || "+input");
7376 },
7377 replaceSelections: docMethodOp(function(code, collapse, origin) {
7378 var changes = [], sel = this.sel;
7379 for (var i = 0; i < sel.ranges.length; i++) {
7380 var range = sel.ranges[i];
7381 changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};
7382 }
7383 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7384 for (var i = changes.length - 1; i >= 0; i--)
7385 makeChange(this, changes[i]);
7386 if (newSel) setSelectionReplaceHistory(this, newSel);
7387 else if (this.cm) ensureCursorVisible(this.cm);
7388 }),
7389 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7390 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7391 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7392 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7393
7394 setExtending: function(val) {this.extend = val;},
7395 getExtending: function() {return this.extend;},
7396
7397 historySize: function() {
7398 var hist = this.history, done = 0, undone = 0;
7399 for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
7400 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
7401 return {undo: done, redo: undone};
7402 },
7403 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7404
7405 markClean: function() {
7406 this.cleanGeneration = this.changeGeneration(true);
7407 },
7408 changeGeneration: function(forceSplit) {
7409 if (forceSplit)
7410 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7411 return this.history.generation;
7412 },
7413 isClean: function (gen) {
7414 return this.history.generation == (gen || this.cleanGeneration);
7415 },
7416
7417 getHistory: function() {
7418 return {done: copyHistoryArray(this.history.done),
7419 undone: copyHistoryArray(this.history.undone)};
7420 },
7421 setHistory: function(histData) {
7422 var hist = this.history = new History(this.history.maxGeneration);
7423 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7424 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7425 },
7426
7427 addLineClass: docMethodOp(function(handle, where, cls) {
7428 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7429 var prop = where == "text" ? "textClass"
7430 : where == "background" ? "bgClass"
7431 : where == "gutter" ? "gutterClass" : "wrapClass";
7432 if (!line[prop]) line[prop] = cls;
7433 else if (classTest(cls).test(line[prop])) return false;
7434 else line[prop] += " " + cls;
7435 return true;
7436 });
7437 }),
7438 removeLineClass: docMethodOp(function(handle, where, cls) {
7439 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7440 var prop = where == "text" ? "textClass"
7441 : where == "background" ? "bgClass"
7442 : where == "gutter" ? "gutterClass" : "wrapClass";
7443 var cur = line[prop];
7444 if (!cur) return false;
7445 else if (cls == null) line[prop] = null;
7446 else {
7447 var found = cur.match(classTest(cls));
7448 if (!found) return false;
7449 var end = found.index + found[0].length;
7450 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7451 }
7452 return true;
7453 });
7454 }),
7455
7456 addLineWidget: docMethodOp(function(handle, node, options) {
7457 return addLineWidget(this, handle, node, options);
7458 }),
7459 removeLineWidget: function(widget) { widget.clear(); },
7460
7461 markText: function(from, to, options) {
7462 return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
7463 },
7464 setBookmark: function(pos, options) {
7465 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7466 insertLeft: options && options.insertLeft,
7467 clearWhenEmpty: false, shared: options && options.shared,
7468 handleMouseEvents: options && options.handleMouseEvents};
7469 pos = clipPos(this, pos);
7470 return markText(this, pos, pos, realOpts, "bookmark");
7471 },
7472 findMarksAt: function(pos) {
7473 pos = clipPos(this, pos);
7474 var markers = [], spans = getLine(this, pos.line).markedSpans;
7475 if (spans) for (var i = 0; i < spans.length; ++i) {
7476 var span = spans[i];
7477 if ((span.from == null || span.from <= pos.ch) &&
7478 (span.to == null || span.to >= pos.ch))
7479 markers.push(span.marker.parent || span.marker);
7480 }
7481 return markers;
7482 },
7483 findMarks: function(from, to, filter) {
7484 from = clipPos(this, from); to = clipPos(this, to);
7485 var found = [], lineNo = from.line;
7486 this.iter(from.line, to.line + 1, function(line) {
7487 var spans = line.markedSpans;
7488 if (spans) for (var i = 0; i < spans.length; i++) {
7489 var span = spans[i];
7490 if (!(lineNo == from.line && from.ch > span.to ||
7491 span.from == null && lineNo != from.line||
7492 lineNo == to.line && span.from > to.ch) &&
7493 (!filter || filter(span.marker)))
7494 found.push(span.marker.parent || span.marker);
7495 }
7496 ++lineNo;
7497 });
7498 return found;
7499 },
7500 getAllMarks: function() {
7501 var markers = [];
7502 this.iter(function(line) {
7503 var sps = line.markedSpans;
7504 if (sps) for (var i = 0; i < sps.length; ++i)
7505 if (sps[i].from != null) markers.push(sps[i].marker);
7506 });
7507 return markers;
7508 },
7509
7510 posFromIndex: function(off) {
7511 var ch, lineNo = this.first;
7512 this.iter(function(line) {
7513 var sz = line.text.length + 1;
7514 if (sz > off) { ch = off; return true; }
7515 off -= sz;
7516 ++lineNo;
7517 });
7518 return clipPos(this, Pos(lineNo, ch));
7519 },
7520 indexFromPos: function (coords) {
7521 coords = clipPos(this, coords);
7522 var index = coords.ch;
7523 if (coords.line < this.first || coords.ch < 0) return 0;
7524 this.iter(this.first, coords.line, function (line) {
7525 index += line.text.length + 1;
7526 });
7527 return index;
7528 },
7529
7530 copy: function(copyHistory) {
7531 var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
7532 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7533 doc.sel = this.sel;
7534 doc.extend = false;
7535 if (copyHistory) {
7536 doc.history.undoDepth = this.history.undoDepth;
7537 doc.setHistory(this.getHistory());
7538 }
7539 return doc;
7540 },
7541
7542 linkedDoc: function(options) {
7543 if (!options) options = {};
7544 var from = this.first, to = this.first + this.size;
7545 if (options.from != null && options.from > from) from = options.from;
7546 if (options.to != null && options.to < to) to = options.to;
7547 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
7548 if (options.sharedHist) copy.history = this.history;
7549 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7550 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7551 copySharedMarkers(copy, findSharedMarkers(this));
7552 return copy;
7553 },
7554 unlinkDoc: function(other) {
7555 if (other instanceof CodeMirror) other = other.doc;
7556 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7557 var link = this.linked[i];
7558 if (link.doc != other) continue;
7559 this.linked.splice(i, 1);
7560 other.unlinkDoc(this);
7561 detachSharedMarkers(findSharedMarkers(this));
7562 break;
7563 }
7564 // If the histories were shared, split them again
7565 if (other.history == this.history) {
7566 var splitIds = [other.id];
7567 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7568 other.history = new History(null);
7569 other.history.done = copyHistoryArray(this.history.done, splitIds);
7570 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7571 }
7572 },
7573 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7574
7575 getMode: function() {return this.mode;},
7576 getEditor: function() {return this.cm;}
7577 });
7578
7579 // Public alias.
7580 Doc.prototype.eachLine = Doc.prototype.iter;
7581
7582 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
7583 var dontDelegate = "iter insert remove copy getEditor".split(" ");
7584 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
7585 CodeMirror.prototype[prop] = (function(method) {
7586 return function() {return method.apply(this.doc, arguments);};
7587 })(Doc.prototype[prop]);
7588
7589 eventMixin(Doc);
7590
7591 // Call f for all linked documents.
7592 function linkedDocs(doc, f, sharedHistOnly) {
7593 function propagate(doc, skip, sharedHist) {
7594 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
7595 var rel = doc.linked[i];
7596 if (rel.doc == skip) continue;
7597 var shared = sharedHist && rel.sharedHist;
7598 if (sharedHistOnly && !shared) continue;
7599 f(rel.doc, shared);
7600 propagate(rel.doc, doc, shared);
7601 }
7602 }
7603 propagate(doc, null, true);
7604 }
7605
7606 // Attach a document to an editor.
7607 function attachDoc(cm, doc) {
7608 if (doc.cm) throw new Error("This document is already in use.");
7609 cm.doc = doc;
7610 doc.cm = cm;
7611 estimateLineHeights(cm);
7612 loadMode(cm);
7613 if (!cm.options.lineWrapping) findMaxLine(cm);
7614 cm.options.mode = doc.modeOption;
7615 regChange(cm);
7616 }
7617
7618 // LINE UTILITIES
7619
7620 // Find the line object corresponding to the given line number.
7621 function getLine(doc, n) {
7622 n -= doc.first;
7623 if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
7624 for (var chunk = doc; !chunk.lines;) {
7625 for (var i = 0;; ++i) {
7626 var child = chunk.children[i], sz = child.chunkSize();
7627 if (n < sz) { chunk = child; break; }
7628 n -= sz;
7629 }
7630 }
7631 return chunk.lines[n];
7632 }
7633
7634 // Get the part of a document between two positions, as an array of
7635 // strings.
7636 function getBetween(doc, start, end) {
7637 var out = [], n = start.line;
7638 doc.iter(start.line, end.line + 1, function(line) {
7639 var text = line.text;
7640 if (n == end.line) text = text.slice(0, end.ch);
7641 if (n == start.line) text = text.slice(start.ch);
7642 out.push(text);
7643 ++n;
7644 });
7645 return out;
7646 }
7647 // Get the lines between from and to, as array of strings.
7648 function getLines(doc, from, to) {
7649 var out = [];
7650 doc.iter(from, to, function(line) { out.push(line.text); });
7651 return out;
7652 }
7653
7654 // Update the height of a line, propagating the height change
7655 // upwards to parent nodes.
7656 function updateLineHeight(line, height) {
7657 var diff = height - line.height;
7658 if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7659 }
7660
7661 // Given a line object, find its line number by walking up through
7662 // its parent links.
7663 function lineNo(line) {
7664 if (line.parent == null) return null;
7665 var cur = line.parent, no = indexOf(cur.lines, line);
7666 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7667 for (var i = 0;; ++i) {
7668 if (chunk.children[i] == cur) break;
7669 no += chunk.children[i].chunkSize();
7670 }
7671 }
7672 return no + cur.first;
7673 }
7674
7675 // Find the line at the given vertical position, using the height
7676 // information in the document tree.
7677 function lineAtHeight(chunk, h) {
7678 var n = chunk.first;
7679 outer: do {
7680 for (var i = 0; i < chunk.children.length; ++i) {
7681 var child = chunk.children[i], ch = child.height;
7682 if (h < ch) { chunk = child; continue outer; }
7683 h -= ch;
7684 n += child.chunkSize();
7685 }
7686 return n;
7687 } while (!chunk.lines);
7688 for (var i = 0; i < chunk.lines.length; ++i) {
7689 var line = chunk.lines[i], lh = line.height;
7690 if (h < lh) break;
7691 h -= lh;
7692 }
7693 return n + i;
7694 }
7695
7696
7697 // Find the height above the given line.
7698 function heightAtLine(lineObj) {
7699 lineObj = visualLine(lineObj);
7700
7701 var h = 0, chunk = lineObj.parent;
7702 for (var i = 0; i < chunk.lines.length; ++i) {
7703 var line = chunk.lines[i];
7704 if (line == lineObj) break;
7705 else h += line.height;
7706 }
7707 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7708 for (var i = 0; i < p.children.length; ++i) {
7709 var cur = p.children[i];
7710 if (cur == chunk) break;
7711 else h += cur.height;
7712 }
7713 }
7714 return h;
7715 }
7716
7717 // Get the bidi ordering for the given line (and cache it). Returns
7718 // false for lines that are fully left-to-right, and an array of
7719 // BidiSpan objects otherwise.
7720 function getOrder(line) {
7721 var order = line.order;
7722 if (order == null) order = line.order = bidiOrdering(line.text);
7723 return order;
7724 }
7725
7726 // HISTORY
7727
7728 function History(startGen) {
7729 // Arrays of change events and selections. Doing something adds an
7730 // event to done and clears undo. Undoing moves events from done
7731 // to undone, redoing moves them in the other direction.
7732 this.done = []; this.undone = [];
7733 this.undoDepth = Infinity;
7734 // Used to track when changes can be merged into a single undo
7735 // event
7736 this.lastModTime = this.lastSelTime = 0;
7737 this.lastOp = this.lastSelOp = null;
7738 this.lastOrigin = this.lastSelOrigin = null;
7739 // Used by the isClean() method
7740 this.generation = this.maxGeneration = startGen || 1;
7741 }
7742
7743 // Create a history change event from an updateDoc-style change
7744 // object.
7745 function historyChangeFromChange(doc, change) {
7746 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
7747 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7748 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
7749 return histChange;
7750 }
7751
7752 // Pop all selection events off the end of a history array. Stop at
7753 // a change event.
7754 function clearSelectionEvents(array) {
7755 while (array.length) {
7756 var last = lst(array);
7757 if (last.ranges) array.pop();
7758 else break;
7759 }
7760 }
7761
7762 // Find the top change event in the history. Pop off selection
7763 // events that are in the way.
7764 function lastChangeEvent(hist, force) {
7765 if (force) {
7766 clearSelectionEvents(hist.done);
7767 return lst(hist.done);
7768 } else if (hist.done.length && !lst(hist.done).ranges) {
7769 return lst(hist.done);
7770 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
7771 hist.done.pop();
7772 return lst(hist.done);
7773 }
7774 }
7775
7776 // Register a change in the history. Merges changes that are within
7777 // a single operation, ore are close together with an origin that
7778 // allows merging (starting with "+") into a single event.
7779 function addChangeToHistory(doc, change, selAfter, opId) {
7780 var hist = doc.history;
7781 hist.undone.length = 0;
7782 var time = +new Date, cur;
7783
7784 if ((hist.lastOp == opId ||
7785 hist.lastOrigin == change.origin && change.origin &&
7786 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
7787 change.origin.charAt(0) == "*")) &&
7788 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7789 // Merge this change into the last event
7790 var last = lst(cur.changes);
7791 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
7792 // Optimized case for simple insertion -- don't want to add
7793 // new changesets for every character typed
7794 last.to = changeEnd(change);
7795 } else {
7796 // Add new sub-event
7797 cur.changes.push(historyChangeFromChange(doc, change));
7798 }
7799 } else {
7800 // Can not be merged, start a new event.
7801 var before = lst(hist.done);
7802 if (!before || !before.ranges)
7803 pushSelectionToHistory(doc.sel, hist.done);
7804 cur = {changes: [historyChangeFromChange(doc, change)],
7805 generation: hist.generation};
7806 hist.done.push(cur);
7807 while (hist.done.length > hist.undoDepth) {
7808 hist.done.shift();
7809 if (!hist.done[0].ranges) hist.done.shift();
7810 }
7811 }
7812 hist.done.push(selAfter);
7813 hist.generation = ++hist.maxGeneration;
7814 hist.lastModTime = hist.lastSelTime = time;
7815 hist.lastOp = hist.lastSelOp = opId;
7816 hist.lastOrigin = hist.lastSelOrigin = change.origin;
7817
7818 if (!last) signal(doc, "historyAdded");
7819 }
7820
7821 function selectionEventCanBeMerged(doc, origin, prev, sel) {
7822 var ch = origin.charAt(0);
7823 return ch == "*" ||
7824 ch == "+" &&
7825 prev.ranges.length == sel.ranges.length &&
7826 prev.somethingSelected() == sel.somethingSelected() &&
7827 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
7828 }
7829
7830 // Called whenever the selection changes, sets the new selection as
7831 // the pending selection in the history, and pushes the old pending
7832 // selection into the 'done' array when it was significantly
7833 // different (in number of selected ranges, emptiness, or time).
7834 function addSelectionToHistory(doc, sel, opId, options) {
7835 var hist = doc.history, origin = options && options.origin;
7836
7837 // A new event is started when the previous origin does not match
7838 // the current, or the origins don't allow matching. Origins
7839 // starting with * are always merged, those starting with + are
7840 // merged when similar and close together in time.
7841 if (opId == hist.lastSelOp ||
7842 (origin && hist.lastSelOrigin == origin &&
7843 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
7844 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
7845 hist.done[hist.done.length - 1] = sel;
7846 else
7847 pushSelectionToHistory(sel, hist.done);
7848
7849 hist.lastSelTime = +new Date;
7850 hist.lastSelOrigin = origin;
7851 hist.lastSelOp = opId;
7852 if (options && options.clearRedo !== false)
7853 clearSelectionEvents(hist.undone);
7854 }
7855
7856 function pushSelectionToHistory(sel, dest) {
7857 var top = lst(dest);
7858 if (!(top && top.ranges && top.equals(sel)))
7859 dest.push(sel);
7860 }
7861
7862 // Used to store marked span information in the history.
7863 function attachLocalSpans(doc, change, from, to) {
7864 var existing = change["spans_" + doc.id], n = 0;
7865 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
7866 if (line.markedSpans)
7867 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
7868 ++n;
7869 });
7870 }
7871
7872 // When un/re-doing restores text containing marked spans, those
7873 // that have been explicitly cleared should not be restored.
7874 function removeClearedSpans(spans) {
7875 if (!spans) return null;
7876 for (var i = 0, out; i < spans.length; ++i) {
7877 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
7878 else if (out) out.push(spans[i]);
7879 }
7880 return !out ? spans : out.length ? out : null;
7881 }
7882
7883 // Retrieve and filter the old marked spans stored in a change event.
7884 function getOldSpans(doc, change) {
7885 var found = change["spans_" + doc.id];
7886 if (!found) return null;
7887 for (var i = 0, nw = []; i < change.text.length; ++i)
7888 nw.push(removeClearedSpans(found[i]));
7889 return nw;
7890 }
7891
7892 // Used both to provide a JSON-safe object in .getHistory, and, when
7893 // detaching a document, to split the history in two
7894 function copyHistoryArray(events, newGroup, instantiateSel) {
7895 for (var i = 0, copy = []; i < events.length; ++i) {
7896 var event = events[i];
7897 if (event.ranges) {
7898 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
7899 continue;
7900 }
7901 var changes = event.changes, newChanges = [];
7902 copy.push({changes: newChanges});
7903 for (var j = 0; j < changes.length; ++j) {
7904 var change = changes[j], m;
7905 newChanges.push({from: change.from, to: change.to, text: change.text});
7906 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
7907 if (indexOf(newGroup, Number(m[1])) > -1) {
7908 lst(newChanges)[prop] = change[prop];
7909 delete change[prop];
7910 }
7911 }
7912 }
7913 }
7914 return copy;
7915 }
7916
7917 // Rebasing/resetting history to deal with externally-sourced changes
7918
7919 function rebaseHistSelSingle(pos, from, to, diff) {
7920 if (to < pos.line) {
7921 pos.line += diff;
7922 } else if (from < pos.line) {
7923 pos.line = from;
7924 pos.ch = 0;
7925 }
7926 }
7927
7928 // Tries to rebase an array of history events given a change in the
7929 // document. If the change touches the same lines as the event, the
7930 // event, and everything 'behind' it, is discarded. If the change is
7931 // before the event, the event's positions are updated. Uses a
7932 // copy-on-write scheme for the positions, to avoid having to
7933 // reallocate them all on every rebase, but also avoid problems with
7934 // shared position objects being unsafely updated.
7935 function rebaseHistArray(array, from, to, diff) {
7936 for (var i = 0; i < array.length; ++i) {
7937 var sub = array[i], ok = true;
7938 if (sub.ranges) {
7939 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7940 for (var j = 0; j < sub.ranges.length; j++) {
7941 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7942 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7943 }
7944 continue;
7945 }
7946 for (var j = 0; j < sub.changes.length; ++j) {
7947 var cur = sub.changes[j];
7948 if (to < cur.from.line) {
7949 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7950 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7951 } else if (from <= cur.to.line) {
7952 ok = false;
7953 break;
7954 }
7955 }
7956 if (!ok) {
7957 array.splice(0, i + 1);
7958 i = 0;
7959 }
7960 }
7961 }
7962
7963 function rebaseHist(hist, change) {
7964 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7965 rebaseHistArray(hist.done, from, to, diff);
7966 rebaseHistArray(hist.undone, from, to, diff);
7967 }
7968
7969 // EVENT UTILITIES
7970
7971 // Due to the fact that we still support jurassic IE versions, some
7972 // compatibility wrappers are needed.
7973
7974 var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
7975 if (e.preventDefault) e.preventDefault();
7976 else e.returnValue = false;
7977 };
7978 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
7979 if (e.stopPropagation) e.stopPropagation();
7980 else e.cancelBubble = true;
7981 };
7982 function e_defaultPrevented(e) {
7983 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
7984 }
7985 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
7986
7987 function e_target(e) {return e.target || e.srcElement;}
7988 function e_button(e) {
7989 var b = e.which;
7990 if (b == null) {
7991 if (e.button & 1) b = 1;
7992 else if (e.button & 2) b = 3;
7993 else if (e.button & 4) b = 2;
7994 }
7995 if (mac && e.ctrlKey && b == 1) b = 3;
7996 return b;
7997 }
7998
7999 // EVENT HANDLING
8000
8001 // Lightweight event framework. on/off also work on DOM nodes,
8002 // registering native DOM handlers.
8003
8004 var on = CodeMirror.on = function(emitter, type, f) {
8005 if (emitter.addEventListener)
8006 emitter.addEventListener(type, f, false);
8007 else if (emitter.attachEvent)
8008 emitter.attachEvent("on" + type, f);
8009 else {
8010 var map = emitter._handlers || (emitter._handlers = {});
8011 var arr = map[type] || (map[type] = []);
8012 arr.push(f);
8013 }
8014 };
8015
8016 var off = CodeMirror.off = function(emitter, type, f) {
8017 if (emitter.removeEventListener)
8018 emitter.removeEventListener(type, f, false);
8019 else if (emitter.detachEvent)
8020 emitter.detachEvent("on" + type, f);
8021 else {
8022 var arr = emitter._handlers && emitter._handlers[type];
8023 if (!arr) return;
8024 for (var i = 0; i < arr.length; ++i)
8025 if (arr[i] == f) { arr.splice(i, 1); break; }
8026 }
8027 };
8028
8029 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
8030 var arr = emitter._handlers && emitter._handlers[type];
8031 if (!arr) return;
8032 var args = Array.prototype.slice.call(arguments, 2);
8033 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
8034 };
8035
8036 var orphanDelayedCallbacks = null;
8037
8038 // Often, we want to signal events at a point where we are in the
8039 // middle of some work, but don't want the handler to start calling
8040 // other methods on the editor, which might be in an inconsistent
8041 // state or simply not expect any other events to happen.
8042 // signalLater looks whether there are any handlers, and schedules
8043 // them to be executed when the last operation ends, or, if no
8044 // operation is active, when a timeout fires.
8045 function signalLater(emitter, type /*, values...*/) {
8046 var arr = emitter._handlers && emitter._handlers[type];
8047 if (!arr) return;
8048 var args = Array.prototype.slice.call(arguments, 2), list;
8049 if (operationGroup) {
8050 list = operationGroup.delayedCallbacks;
8051 } else if (orphanDelayedCallbacks) {
8052 list = orphanDelayedCallbacks;
8053 } else {
8054 list = orphanDelayedCallbacks = [];
8055 setTimeout(fireOrphanDelayed, 0);
8056 }
8057 function bnd(f) {return function(){f.apply(null, args);};};
8058 for (var i = 0; i < arr.length; ++i)
8059 list.push(bnd(arr[i]));
8060 }
8061
8062 function fireOrphanDelayed() {
8063 var delayed = orphanDelayedCallbacks;
8064 orphanDelayedCallbacks = null;
8065 for (var i = 0; i < delayed.length; ++i) delayed[i]();
8066 }
8067
8068 // The DOM events that CodeMirror handles can be overridden by
8069 // registering a (non-DOM) handler on the editor for the event name,
8070 // and preventDefault-ing the event in that handler.
8071 function signalDOMEvent(cm, e, override) {
8072 if (typeof e == "string")
8073 e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
8074 signal(cm, override || e.type, cm, e);
8075 return e_defaultPrevented(e) || e.codemirrorIgnore;
8076 }
8077
8078 function signalCursorActivity(cm) {
8079 var arr = cm._handlers && cm._handlers.cursorActivity;
8080 if (!arr) return;
8081 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
8082 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
8083 set.push(arr[i]);
8084 }
8085
8086 function hasHandler(emitter, type) {
8087 var arr = emitter._handlers && emitter._handlers[type];
8088 return arr && arr.length > 0;
8089 }
8090
8091 // Add on and off methods to a constructor's prototype, to make
8092 // registering events on such objects more convenient.
8093 function eventMixin(ctor) {
8094 ctor.prototype.on = function(type, f) {on(this, type, f);};
8095 ctor.prototype.off = function(type, f) {off(this, type, f);};
8096 }
8097
8098 // MISC UTILITIES
8099
8100 // Number of pixels added to scroller and sizer to hide scrollbar
8101 var scrollerGap = 30;
8102
8103 // Returned or thrown by various protocols to signal 'I'm not
8104 // handling this'.
8105 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
8106
8107 // Reused option objects for setSelection & friends
8108 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8109
8110 function Delayed() {this.id = null;}
8111 Delayed.prototype.set = function(ms, f) {
8112 clearTimeout(this.id);
8113 this.id = setTimeout(f, ms);
8114 };
8115
8116 // Counts the column offset in a string, taking tabs into account.
8117 // Used mostly to find indentation.
8118 var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
8119 if (end == null) {
8120 end = string.search(/[^\s\u00a0]/);
8121 if (end == -1) end = string.length;
8122 }
8123 for (var i = startIndex || 0, n = startValue || 0;;) {
8124 var nextTab = string.indexOf("\t", i);
8125 if (nextTab < 0 || nextTab >= end)
8126 return n + (end - i);
8127 n += nextTab - i;
8128 n += tabSize - (n % tabSize);
8129 i = nextTab + 1;
8130 }
8131 };
8132
8133 // The inverse of countColumn -- find the offset that corresponds to
8134 // a particular column.
8135 function findColumn(string, goal, tabSize) {
8136 for (var pos = 0, col = 0;;) {
8137 var nextTab = string.indexOf("\t", pos);
8138 if (nextTab == -1) nextTab = string.length;
8139 var skipped = nextTab - pos;
8140 if (nextTab == string.length || col + skipped >= goal)
8141 return pos + Math.min(skipped, goal - col);
8142 col += nextTab - pos;
8143 col += tabSize - (col % tabSize);
8144 pos = nextTab + 1;
8145 if (col >= goal) return pos;
8146 }
8147 }
8148
8149 var spaceStrs = [""];
8150 function spaceStr(n) {
8151 while (spaceStrs.length <= n)
8152 spaceStrs.push(lst(spaceStrs) + " ");
8153 return spaceStrs[n];
8154 }
8155
8156 function lst(arr) { return arr[arr.length-1]; }
8157
8158 var selectInput = function(node) { node.select(); };
8159 if (ios) // Mobile Safari apparently has a bug where select() is broken.
8160 selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8161 else if (ie) // Suppress mysterious IE10 errors
8162 selectInput = function(node) { try { node.select(); } catch(_e) {} };
8163
8164 function indexOf(array, elt) {
8165 for (var i = 0; i < array.length; ++i)
8166 if (array[i] == elt) return i;
8167 return -1;
8168 }
8169 function map(array, f) {
8170 var out = [];
8171 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8172 return out;
8173 }
8174
8175 function nothing() {}
8176
8177 function createObj(base, props) {
8178 var inst;
8179 if (Object.create) {
8180 inst = Object.create(base);
8181 } else {
8182 nothing.prototype = base;
8183 inst = new nothing();
8184 }
8185 if (props) copyObj(props, inst);
8186 return inst;
8187 };
8188
8189 function copyObj(obj, target, overwrite) {
8190 if (!target) target = {};
8191 for (var prop in obj)
8192 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8193 target[prop] = obj[prop];
8194 return target;
8195 }
8196
8197 function bind(f) {
8198 var args = Array.prototype.slice.call(arguments, 1);
8199 return function(){return f.apply(null, args);};
8200 }
8201
8202 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8203 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8204 return /\w/.test(ch) || ch > "\x80" &&
8205 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
8206 };
8207 function isWordChar(ch, helper) {
8208 if (!helper) return isWordCharBasic(ch);
8209 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8210 return helper.test(ch);
8211 }
8212
8213 function isEmpty(obj) {
8214 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8215 return true;
8216 }
8217
8218 // Extending unicode characters. A series of a non-extending char +
8219 // any number of extending chars is treated as a single unit as far
8220 // as editing and measuring is concerned. This is not fully correct,
8221 // since some scripts/fonts/browsers also treat other configurations
8222 // of code points as a group.
8223 var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
8224 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
8225
8226 // DOM UTILITIES
8227
8228 function elt(tag, content, className, style) {
8229 var e = document.createElement(tag);
8230 if (className) e.className = className;
8231 if (style) e.style.cssText = style;
8232 if (typeof content == "string") e.appendChild(document.createTextNode(content));
8233 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
8234 return e;
8235 }
8236
8237 var range;
8238 if (document.createRange) range = function(node, start, end, endNode) {
8239 var r = document.createRange();
8240 r.setEnd(endNode || node, end);
8241 r.setStart(node, start);
8242 return r;
8243 };
8244 else range = function(node, start, end) {
8245 var r = document.body.createTextRange();
8246 try { r.moveToElementText(node.parentNode); }
8247 catch(e) { return r; }
8248 r.collapse(true);
8249 r.moveEnd("character", end);
8250 r.moveStart("character", start);
8251 return r;
8252 };
8253
8254 function removeChildren(e) {
8255 for (var count = e.childNodes.length; count > 0; --count)
8256 e.removeChild(e.firstChild);
8257 return e;
8258 }
8259
8260 function removeChildrenAndAdd(parent, e) {
8261 return removeChildren(parent).appendChild(e);
8262 }
8263
8264 var contains = CodeMirror.contains = function(parent, child) {
8265 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8266 child = child.parentNode;
8267 if (parent.contains)
8268 return parent.contains(child);
8269 do {
8270 if (child.nodeType == 11) child = child.host;
8271 if (child == parent) return true;
8272 } while (child = child.parentNode);
8273 };
8274
8275 function activeElt() { return document.activeElement; }
8276 // Older versions of IE throws unspecified error when touching
8277 // document.activeElement in some cases (during loading, in iframe)
8278 if (ie && ie_version < 11) activeElt = function() {
8279 try { return document.activeElement; }
8280 catch(e) { return document.body; }
8281 };
8282
8283 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
8284 var rmClass = CodeMirror.rmClass = function(node, cls) {
8285 var current = node.className;
8286 var match = classTest(cls).exec(current);
8287 if (match) {
8288 var after = current.slice(match.index + match[0].length);
8289 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8290 }
8291 };
8292 var addClass = CodeMirror.addClass = function(node, cls) {
8293 var current = node.className;
8294 if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8295 };
8296 function joinClasses(a, b) {
8297 var as = a.split(" ");
8298 for (var i = 0; i < as.length; i++)
8299 if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8300 return b;
8301 }
8302
8303 // WINDOW-WIDE EVENTS
8304
8305 // These must be handled carefully, because naively registering a
8306 // handler for each editor will cause the editors to never be
8307 // garbage collected.
8308
8309 function forEachCodeMirror(f) {
8310 if (!document.body.getElementsByClassName) return;
8311 var byClass = document.body.getElementsByClassName("CodeMirror");
8312 for (var i = 0; i < byClass.length; i++) {
8313 var cm = byClass[i].CodeMirror;
8314 if (cm) f(cm);
8315 }
8316 }
8317
8318 var globalsRegistered = false;
8319 function ensureGlobalHandlers() {
8320 if (globalsRegistered) return;
8321 registerGlobalHandlers();
8322 globalsRegistered = true;
8323 }
8324 function registerGlobalHandlers() {
8325 // When the window resizes, we need to refresh active editors.
8326 var resizeTimer;
8327 on(window, "resize", function() {
8328 if (resizeTimer == null) resizeTimer = setTimeout(function() {
8329 resizeTimer = null;
8330 forEachCodeMirror(onResize);
8331 }, 100);
8332 });
8333 // When the window loses focus, we want to show the editor as blurred
8334 on(window, "blur", function() {
8335 forEachCodeMirror(onBlur);
8336 });
8337 }
8338
8339 // FEATURE DETECTION
8340
8341 // Detect drag-and-drop
8342 var dragAndDrop = function() {
8343 // There is *some* kind of drag-and-drop support in IE6-8, but I
8344 // couldn't get it to work yet.
8345 if (ie && ie_version < 9) return false;
8346 var div = elt('div');
8347 return "draggable" in div || "dragDrop" in div;
8348 }();
8349
8350 var zwspSupported;
8351 function zeroWidthElement(measure) {
8352 if (zwspSupported == null) {
8353 var test = elt("span", "\u200b");
8354 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
8355 if (measure.firstChild.offsetHeight != 0)
8356 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
8357 }
8358 var node = zwspSupported ? elt("span", "\u200b") :
8359 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
8360 node.setAttribute("cm-text", "");
8361 return node;
8362 }
8363
8364 // Feature-detect IE's crummy client rect reporting for bidi text
8365 var badBidiRects;
8366 function hasBadBidiRects(measure) {
8367 if (badBidiRects != null) return badBidiRects;
8368 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
8369 var r0 = range(txt, 0, 1).getBoundingClientRect();
8370 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8371 var r1 = range(txt, 1, 2).getBoundingClientRect();
8372 return badBidiRects = (r1.right - r0.right < 3);
8373 }
8374
8375 // See if "".split is the broken IE version, if so, provide an
8376 // alternative way to split lines.
8377 var splitLines = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8378 var pos = 0, result = [], l = string.length;
8379 while (pos <= l) {
8380 var nl = string.indexOf("\n", pos);
8381 if (nl == -1) nl = string.length;
8382 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8383 var rt = line.indexOf("\r");
8384 if (rt != -1) {
8385 result.push(line.slice(0, rt));
8386 pos += rt + 1;
8387 } else {
8388 result.push(line);
8389 pos = nl + 1;
8390 }
8391 }
8392 return result;
8393 } : function(string){return string.split(/\r\n?|\n/);};
8394
8395 var hasSelection = window.getSelection ? function(te) {
8396 try { return te.selectionStart != te.selectionEnd; }
8397 catch(e) { return false; }
8398 } : function(te) {
8399 try {var range = te.ownerDocument.selection.createRange();}
8400 catch(e) {}
8401 if (!range || range.parentElement() != te) return false;
8402 return range.compareEndPoints("StartToEnd", range) != 0;
8403 };
8404
8405 var hasCopyEvent = (function() {
8406 var e = elt("div");
8407 if ("oncopy" in e) return true;
8408 e.setAttribute("oncopy", "return;");
8409 return typeof e.oncopy == "function";
8410 })();
8411
8412 var badZoomedRects = null;
8413 function hasBadZoomedRects(measure) {
8414 if (badZoomedRects != null) return badZoomedRects;
8415 var node = removeChildrenAndAdd(measure, elt("span", "x"));
8416 var normal = node.getBoundingClientRect();
8417 var fromRange = range(node, 0, 1).getBoundingClientRect();
8418 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8419 }
8420
8421 // KEY NAMES
8422
8423 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8424 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8425 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8426 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete",
8427 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8428 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8429 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
8430 CodeMirror.keyNames = keyNames;
8431 (function() {
8432 // Number keys
8433 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
8434 // Alphabetic keys
8435 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
8436 // Function keys
8437 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
8438 })();
8439
8440 // BIDI HELPERS
8441
8442 function iterateBidiSections(order, from, to, f) {
8443 if (!order) return f(from, to, "ltr");
8444 var found = false;
8445 for (var i = 0; i < order.length; ++i) {
8446 var part = order[i];
8447 if (part.from < to && part.to > from || from == to && part.to == from) {
8448 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
8449 found = true;
8450 }
8451 }
8452 if (!found) f(from, to, "ltr");
8453 }
8454
8455 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8456 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8457
8458 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
8459 function lineRight(line) {
8460 var order = getOrder(line);
8461 if (!order) return line.text.length;
8462 return bidiRight(lst(order));
8463 }
8464
8465 function lineStart(cm, lineN) {
8466 var line = getLine(cm.doc, lineN);
8467 var visual = visualLine(line);
8468 if (visual != line) lineN = lineNo(visual);
8469 var order = getOrder(visual);
8470 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
8471 return Pos(lineN, ch);
8472 }
8473 function lineEnd(cm, lineN) {
8474 var merged, line = getLine(cm.doc, lineN);
8475 while (merged = collapsedSpanAtEnd(line)) {
8476 line = merged.find(1, true).line;
8477 lineN = null;
8478 }
8479 var order = getOrder(line);
8480 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
8481 return Pos(lineN == null ? lineNo(line) : lineN, ch);
8482 }
8483 function lineStartSmart(cm, pos) {
8484 var start = lineStart(cm, pos.line);
8485 var line = getLine(cm.doc, start.line);
8486 var order = getOrder(line);
8487 if (!order || order[0].level == 0) {
8488 var firstNonWS = Math.max(0, line.text.search(/\S/));
8489 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8490 return Pos(start.line, inWS ? 0 : firstNonWS);
8491 }
8492 return start;
8493 }
8494
8495 function compareBidiLevel(order, a, b) {
8496 var linedir = order[0].level;
8497 if (a == linedir) return true;
8498 if (b == linedir) return false;
8499 return a < b;
8500 }
8501 var bidiOther;
8502 function getBidiPartAt(order, pos) {
8503 bidiOther = null;
8504 for (var i = 0, found; i < order.length; ++i) {
8505 var cur = order[i];
8506 if (cur.from < pos && cur.to > pos) return i;
8507 if ((cur.from == pos || cur.to == pos)) {
8508 if (found == null) {
8509 found = i;
8510 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8511 if (cur.from != cur.to) bidiOther = found;
8512 return i;
8513 } else {
8514 if (cur.from != cur.to) bidiOther = i;
8515 return found;
8516 }
8517 }
8518 }
8519 return found;
8520 }
8521
8522 function moveInLine(line, pos, dir, byUnit) {
8523 if (!byUnit) return pos + dir;
8524 do pos += dir;
8525 while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8526 return pos;
8527 }
8528
8529 // This is needed in order to move 'visually' through bi-directional
8530 // text -- i.e., pressing left should make the cursor go left, even
8531 // when in RTL text. The tricky part is the 'jumps', where RTL and
8532 // LTR text touch each other. This often requires the cursor offset
8533 // to move more than one unit, in order to visually move one unit.
8534 function moveVisually(line, start, dir, byUnit) {
8535 var bidi = getOrder(line);
8536 if (!bidi) return moveLogically(line, start, dir, byUnit);
8537 var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8538 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8539
8540 for (;;) {
8541 if (target > part.from && target < part.to) return target;
8542 if (target == part.from || target == part.to) {
8543 if (getBidiPartAt(bidi, target) == pos) return target;
8544 part = bidi[pos += dir];
8545 return (dir > 0) == part.level % 2 ? part.to : part.from;
8546 } else {
8547 part = bidi[pos += dir];
8548 if (!part) return null;
8549 if ((dir > 0) == part.level % 2)
8550 target = moveInLine(line, part.to, -1, byUnit);
8551 else
8552 target = moveInLine(line, part.from, 1, byUnit);
8553 }
8554 }
8555 }
8556
8557 function moveLogically(line, start, dir, byUnit) {
8558 var target = start + dir;
8559 if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8560 return target < 0 || target > line.text.length ? null : target;
8561 }
8562
8563 // Bidirectional ordering algorithm
8564 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8565 // that this (partially) implements.
8566
8567 // One-char codes used for character types:
8568 // L (L): Left-to-Right
8569 // R (R): Right-to-Left
8570 // r (AL): Right-to-Left Arabic
8571 // 1 (EN): European Number
8572 // + (ES): European Number Separator
8573 // % (ET): European Number Terminator
8574 // n (AN): Arabic Number
8575 // , (CS): Common Number Separator
8576 // m (NSM): Non-Spacing Mark
8577 // b (BN): Boundary Neutral
8578 // s (B): Paragraph Separator
8579 // t (S): Segment Separator
8580 // w (WS): Whitespace
8581 // N (ON): Other Neutrals
8582
8583 // Returns null if characters are ordered as they appear
8584 // (left-to-right), or an array of sections ({from, to, level}
8585 // objects) in the order in which they occur visually.
8586 var bidiOrdering = (function() {
8587 // Character types for codepoints 0 to 0xff
8588 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8589 // Character types for codepoints 0x600 to 0x6ff
8590 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
8591 function charType(code) {
8592 if (code <= 0xf7) return lowTypes.charAt(code);
8593 else if (0x590 <= code && code <= 0x5f4) return "R";
8594 else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8595 else if (0x6ee <= code && code <= 0x8ac) return "r";
8596 else if (0x2000 <= code && code <= 0x200b) return "w";
8597 else if (code == 0x200c) return "b";
8598 else return "L";
8599 }
8600
8601 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8602 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
8603 // Browsers seem to always treat the boundaries of block elements as being L.
8604 var outerType = "L";
8605
8606 function BidiSpan(level, from, to) {
8607 this.level = level;
8608 this.from = from; this.to = to;
8609 }
8610
8611 return function(str) {
8612 if (!bidiRE.test(str)) return false;
8613 var len = str.length, types = [];
8614 for (var i = 0, type; i < len; ++i)
8615 types.push(type = charType(str.charCodeAt(i)));
8616
8617 // W1. Examine each non-spacing mark (NSM) in the level run, and
8618 // change the type of the NSM to the type of the previous
8619 // character. If the NSM is at the start of the level run, it will
8620 // get the type of sor.
8621 for (var i = 0, prev = outerType; i < len; ++i) {
8622 var type = types[i];
8623 if (type == "m") types[i] = prev;
8624 else prev = type;
8625 }
8626
8627 // W2. Search backwards from each instance of a European number
8628 // until the first strong type (R, L, AL, or sor) is found. If an
8629 // AL is found, change the type of the European number to Arabic
8630 // number.
8631 // W3. Change all ALs to R.
8632 for (var i = 0, cur = outerType; i < len; ++i) {
8633 var type = types[i];
8634 if (type == "1" && cur == "r") types[i] = "n";
8635 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8636 }
8637
8638 // W4. A single European separator between two European numbers
8639 // changes to a European number. A single common separator between
8640 // two numbers of the same type changes to that type.
8641 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
8642 var type = types[i];
8643 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8644 else if (type == "," && prev == types[i+1] &&
8645 (prev == "1" || prev == "n")) types[i] = prev;
8646 prev = type;
8647 }
8648
8649 // W5. A sequence of European terminators adjacent to European
8650 // numbers changes to all European numbers.
8651 // W6. Otherwise, separators and terminators change to Other
8652 // Neutral.
8653 for (var i = 0; i < len; ++i) {
8654 var type = types[i];
8655 if (type == ",") types[i] = "N";
8656 else if (type == "%") {
8657 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
8658 var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8659 for (var j = i; j < end; ++j) types[j] = replace;
8660 i = end - 1;
8661 }
8662 }
8663
8664 // W7. Search backwards from each instance of a European number
8665 // until the first strong type (R, L, or sor) is found. If an L is
8666 // found, then change the type of the European number to L.
8667 for (var i = 0, cur = outerType; i < len; ++i) {
8668 var type = types[i];
8669 if (cur == "L" && type == "1") types[i] = "L";
8670 else if (isStrong.test(type)) cur = type;
8671 }
8672
8673 // N1. A sequence of neutrals takes the direction of the
8674 // surrounding strong text if the text on both sides has the same
8675 // direction. European and Arabic numbers act as if they were R in
8676 // terms of their influence on neutrals. Start-of-level-run (sor)
8677 // and end-of-level-run (eor) are used at level run boundaries.
8678 // N2. Any remaining neutrals take the embedding direction.
8679 for (var i = 0; i < len; ++i) {
8680 if (isNeutral.test(types[i])) {
8681 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
8682 var before = (i ? types[i-1] : outerType) == "L";
8683 var after = (end < len ? types[end] : outerType) == "L";
8684 var replace = before || after ? "L" : "R";
8685 for (var j = i; j < end; ++j) types[j] = replace;
8686 i = end - 1;
8687 }
8688 }
8689
8690 // Here we depart from the documented algorithm, in order to avoid
8691 // building up an actual levels array. Since there are only three
8692 // levels (0, 1, 2) in an implementation that doesn't take
8693 // explicit embedding into account, we can build up the order on
8694 // the fly, without following the level-based algorithm.
8695 var order = [], m;
8696 for (var i = 0; i < len;) {
8697 if (countsAsLeft.test(types[i])) {
8698 var start = i;
8699 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
8700 order.push(new BidiSpan(0, start, i));
8701 } else {
8702 var pos = i, at = order.length;
8703 for (++i; i < len && types[i] != "L"; ++i) {}
8704 for (var j = pos; j < i;) {
8705 if (countsAsNum.test(types[j])) {
8706 if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8707 var nstart = j;
8708 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
8709 order.splice(at, 0, new BidiSpan(2, nstart, j));
8710 pos = j;
8711 } else ++j;
8712 }
8713 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
8714 }
8715 }
8716 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
8717 order[0].from = m[0].length;
8718 order.unshift(new BidiSpan(0, 0, m[0].length));
8719 }
8720 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
8721 lst(order).to -= m[0].length;
8722 order.push(new BidiSpan(0, len - m[0].length, len));
8723 }
8724 if (order[0].level == 2)
8725 order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8726 if (order[0].level != lst(order).level)
8727 order.push(new BidiSpan(order[0].level, len, len));
8728
8729 return order;
8730 };
8731 })();
8732
8733 // THE END
8734
8735 CodeMirror.version = "5.2.0";
8736
8737 return CodeMirror;
8738});
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/javascript.js b/sources/samples/toolbarconfigurator/lib/codemirror/javascript.js
new file mode 100644
index 0000000..ef01847
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/javascript.js
@@ -0,0 +1,701 @@
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: http://codemirror.net/LICENSE
3
4// TODO actually recognize syntax of TypeScript constructs
5
6(function(mod) {
7 if (typeof exports == "object" && typeof module == "object") // CommonJS
8 mod(require("../../lib/codemirror"));
9 else if (typeof define == "function" && define.amd) // AMD
10 define(["../../lib/codemirror"], mod);
11 else // Plain browser env
12 mod(CodeMirror);
13})(function(CodeMirror) {
14"use strict";
15
16CodeMirror.defineMode("javascript", function(config, parserConfig) {
17 var indentUnit = config.indentUnit;
18 var statementIndent = parserConfig.statementIndent;
19 var jsonldMode = parserConfig.jsonld;
20 var jsonMode = parserConfig.json || jsonldMode;
21 var isTS = parserConfig.typescript;
22 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
23
24 // Tokenizer
25
26 var keywords = function(){
27 function kw(type) {return {type: type, style: "keyword"};}
28 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
29 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
30
31 var jsKeywords = {
32 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
33 "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C,
34 "var": kw("var"), "const": kw("var"), "let": kw("var"),
35 "function": kw("function"), "catch": kw("catch"),
36 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
37 "in": operator, "typeof": operator, "instanceof": operator,
38 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
39 "this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"),
40 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
41 };
42
43 // Extend the 'normal' keywords with the TypeScript language extensions
44 if (isTS) {
45 var type = {type: "variable", style: "variable-3"};
46 var tsKeywords = {
47 // object-like things
48 "interface": kw("interface"),
49 "extends": kw("extends"),
50 "constructor": kw("constructor"),
51
52 // scope modifiers
53 "public": kw("public"),
54 "private": kw("private"),
55 "protected": kw("protected"),
56 "static": kw("static"),
57
58 // types
59 "string": type, "number": type, "bool": type, "any": type
60 };
61
62 for (var attr in tsKeywords) {
63 jsKeywords[attr] = tsKeywords[attr];
64 }
65 }
66
67 return jsKeywords;
68 }();
69
70 var isOperatorChar = /[+\-*&%=<>!?|~^]/;
71 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
72
73 function readRegexp(stream) {
74 var escaped = false, next, inSet = false;
75 while ((next = stream.next()) != null) {
76 if (!escaped) {
77 if (next == "/" && !inSet) return;
78 if (next == "[") inSet = true;
79 else if (inSet && next == "]") inSet = false;
80 }
81 escaped = !escaped && next == "\\";
82 }
83 }
84
85 // Used as scratch variables to communicate multiple values without
86 // consing up tons of objects.
87 var type, content;
88 function ret(tp, style, cont) {
89 type = tp; content = cont;
90 return style;
91 }
92 function tokenBase(stream, state) {
93 var ch = stream.next();
94 if (ch == '"' || ch == "'") {
95 state.tokenize = tokenString(ch);
96 return state.tokenize(stream, state);
97 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
98 return ret("number", "number");
99 } else if (ch == "." && stream.match("..")) {
100 return ret("spread", "meta");
101 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
102 return ret(ch);
103 } else if (ch == "=" && stream.eat(">")) {
104 return ret("=>", "operator");
105 } else if (ch == "0" && stream.eat(/x/i)) {
106 stream.eatWhile(/[\da-f]/i);
107 return ret("number", "number");
108 } else if (/\d/.test(ch)) {
109 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
110 return ret("number", "number");
111 } else if (ch == "/") {
112 if (stream.eat("*")) {
113 state.tokenize = tokenComment;
114 return tokenComment(stream, state);
115 } else if (stream.eat("/")) {
116 stream.skipToEnd();
117 return ret("comment", "comment");
118 } else if (state.lastType == "operator" || state.lastType == "keyword c" ||
119 state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
120 readRegexp(stream);
121 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
122 return ret("regexp", "string-2");
123 } else {
124 stream.eatWhile(isOperatorChar);
125 return ret("operator", "operator", stream.current());
126 }
127 } else if (ch == "`") {
128 state.tokenize = tokenQuasi;
129 return tokenQuasi(stream, state);
130 } else if (ch == "#") {
131 stream.skipToEnd();
132 return ret("error", "error");
133 } else if (isOperatorChar.test(ch)) {
134 stream.eatWhile(isOperatorChar);
135 return ret("operator", "operator", stream.current());
136 } else if (wordRE.test(ch)) {
137 stream.eatWhile(wordRE);
138 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
139 return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
140 ret("variable", "variable", word);
141 }
142 }
143
144 function tokenString(quote) {
145 return function(stream, state) {
146 var escaped = false, next;
147 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
148 state.tokenize = tokenBase;
149 return ret("jsonld-keyword", "meta");
150 }
151 while ((next = stream.next()) != null) {
152 if (next == quote && !escaped) break;
153 escaped = !escaped && next == "\\";
154 }
155 if (!escaped) state.tokenize = tokenBase;
156 return ret("string", "string");
157 };
158 }
159
160 function tokenComment(stream, state) {
161 var maybeEnd = false, ch;
162 while (ch = stream.next()) {
163 if (ch == "/" && maybeEnd) {
164 state.tokenize = tokenBase;
165 break;
166 }
167 maybeEnd = (ch == "*");
168 }
169 return ret("comment", "comment");
170 }
171
172 function tokenQuasi(stream, state) {
173 var escaped = false, next;
174 while ((next = stream.next()) != null) {
175 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
176 state.tokenize = tokenBase;
177 break;
178 }
179 escaped = !escaped && next == "\\";
180 }
181 return ret("quasi", "string-2", stream.current());
182 }
183
184 var brackets = "([{}])";
185 // This is a crude lookahead trick to try and notice that we're
186 // parsing the argument patterns for a fat-arrow function before we
187 // actually hit the arrow token. It only works if the arrow is on
188 // the same line as the arguments and there's no strange noise
189 // (comments) in between. Fallback is to only notice when we hit the
190 // arrow, and not declare the arguments as locals for the arrow
191 // body.
192 function findFatArrow(stream, state) {
193 if (state.fatArrowAt) state.fatArrowAt = null;
194 var arrow = stream.string.indexOf("=>", stream.start);
195 if (arrow < 0) return;
196
197 var depth = 0, sawSomething = false;
198 for (var pos = arrow - 1; pos >= 0; --pos) {
199 var ch = stream.string.charAt(pos);
200 var bracket = brackets.indexOf(ch);
201 if (bracket >= 0 && bracket < 3) {
202 if (!depth) { ++pos; break; }
203 if (--depth == 0) break;
204 } else if (bracket >= 3 && bracket < 6) {
205 ++depth;
206 } else if (wordRE.test(ch)) {
207 sawSomething = true;
208 } else if (/["'\/]/.test(ch)) {
209 return;
210 } else if (sawSomething && !depth) {
211 ++pos;
212 break;
213 }
214 }
215 if (sawSomething && !depth) state.fatArrowAt = pos;
216 }
217
218 // Parser
219
220 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
221
222 function JSLexical(indented, column, type, align, prev, info) {
223 this.indented = indented;
224 this.column = column;
225 this.type = type;
226 this.prev = prev;
227 this.info = info;
228 if (align != null) this.align = align;
229 }
230
231 function inScope(state, varname) {
232 for (var v = state.localVars; v; v = v.next)
233 if (v.name == varname) return true;
234 for (var cx = state.context; cx; cx = cx.prev) {
235 for (var v = cx.vars; v; v = v.next)
236 if (v.name == varname) return true;
237 }
238 }
239
240 function parseJS(state, style, type, content, stream) {
241 var cc = state.cc;
242 // Communicate our context to the combinators.
243 // (Less wasteful than consing up a hundred closures on every call.)
244 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
245
246 if (!state.lexical.hasOwnProperty("align"))
247 state.lexical.align = true;
248
249 while(true) {
250 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
251 if (combinator(type, content)) {
252 while(cc.length && cc[cc.length - 1].lex)
253 cc.pop()();
254 if (cx.marked) return cx.marked;
255 if (type == "variable" && inScope(state, content)) return "variable-2";
256 return style;
257 }
258 }
259 }
260
261 // Combinator utils
262
263 var cx = {state: null, column: null, marked: null, cc: null};
264 function pass() {
265 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
266 }
267 function cont() {
268 pass.apply(null, arguments);
269 return true;
270 }
271 function register(varname) {
272 function inList(list) {
273 for (var v = list; v; v = v.next)
274 if (v.name == varname) return true;
275 return false;
276 }
277 var state = cx.state;
278 if (state.context) {
279 cx.marked = "def";
280 if (inList(state.localVars)) return;
281 state.localVars = {name: varname, next: state.localVars};
282 } else {
283 if (inList(state.globalVars)) return;
284 if (parserConfig.globalVars)
285 state.globalVars = {name: varname, next: state.globalVars};
286 }
287 }
288
289 // Combinators
290
291 var defaultVars = {name: "this", next: {name: "arguments"}};
292 function pushcontext() {
293 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
294 cx.state.localVars = defaultVars;
295 }
296 function popcontext() {
297 cx.state.localVars = cx.state.context.vars;
298 cx.state.context = cx.state.context.prev;
299 }
300 function pushlex(type, info) {
301 var result = function() {
302 var state = cx.state, indent = state.indented;
303 if (state.lexical.type == "stat") indent = state.lexical.indented;
304 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
305 indent = outer.indented;
306 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
307 };
308 result.lex = true;
309 return result;
310 }
311 function poplex() {
312 var state = cx.state;
313 if (state.lexical.prev) {
314 if (state.lexical.type == ")")
315 state.indented = state.lexical.indented;
316 state.lexical = state.lexical.prev;
317 }
318 }
319 poplex.lex = true;
320
321 function expect(wanted) {
322 function exp(type) {
323 if (type == wanted) return cont();
324 else if (wanted == ";") return pass();
325 else return cont(exp);
326 };
327 return exp;
328 }
329
330 function statement(type, value) {
331 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
332 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
333 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
334 if (type == "{") return cont(pushlex("}"), block, poplex);
335 if (type == ";") return cont();
336 if (type == "if") {
337 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
338 cx.state.cc.pop()();
339 return cont(pushlex("form"), expression, statement, poplex, maybeelse);
340 }
341 if (type == "function") return cont(functiondef);
342 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
343 if (type == "variable") return cont(pushlex("stat"), maybelabel);
344 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
345 block, poplex, poplex);
346 if (type == "case") return cont(expression, expect(":"));
347 if (type == "default") return cont(expect(":"));
348 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
349 statement, poplex, popcontext);
350 if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex);
351 if (type == "class") return cont(pushlex("form"), className, poplex);
352 if (type == "export") return cont(pushlex("form"), afterExport, poplex);
353 if (type == "import") return cont(pushlex("form"), afterImport, poplex);
354 return pass(pushlex("stat"), expression, expect(";"), poplex);
355 }
356 function expression(type) {
357 return expressionInner(type, false);
358 }
359 function expressionNoComma(type) {
360 return expressionInner(type, true);
361 }
362 function expressionInner(type, noComma) {
363 if (cx.state.fatArrowAt == cx.stream.start) {
364 var body = noComma ? arrowBodyNoComma : arrowBody;
365 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
366 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
367 }
368
369 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
370 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
371 if (type == "function") return cont(functiondef, maybeop);
372 if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
373 if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
374 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
375 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
376 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
377 if (type == "quasi") { return pass(quasi, maybeop); }
378 return cont();
379 }
380 function maybeexpression(type) {
381 if (type.match(/[;\}\)\],]/)) return pass();
382 return pass(expression);
383 }
384 function maybeexpressionNoComma(type) {
385 if (type.match(/[;\}\)\],]/)) return pass();
386 return pass(expressionNoComma);
387 }
388
389 function maybeoperatorComma(type, value) {
390 if (type == ",") return cont(expression);
391 return maybeoperatorNoComma(type, value, false);
392 }
393 function maybeoperatorNoComma(type, value, noComma) {
394 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
395 var expr = noComma == false ? expression : expressionNoComma;
396 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
397 if (type == "operator") {
398 if (/\+\+|--/.test(value)) return cont(me);
399 if (value == "?") return cont(expression, expect(":"), expr);
400 return cont(expr);
401 }
402 if (type == "quasi") { return pass(quasi, me); }
403 if (type == ";") return;
404 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
405 if (type == ".") return cont(property, me);
406 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
407 }
408 function quasi(type, value) {
409 if (type != "quasi") return pass();
410 if (value.slice(value.length - 2) != "${") return cont(quasi);
411 return cont(expression, continueQuasi);
412 }
413 function continueQuasi(type) {
414 if (type == "}") {
415 cx.marked = "string-2";
416 cx.state.tokenize = tokenQuasi;
417 return cont(quasi);
418 }
419 }
420 function arrowBody(type) {
421 findFatArrow(cx.stream, cx.state);
422 return pass(type == "{" ? statement : expression);
423 }
424 function arrowBodyNoComma(type) {
425 findFatArrow(cx.stream, cx.state);
426 return pass(type == "{" ? statement : expressionNoComma);
427 }
428 function maybelabel(type) {
429 if (type == ":") return cont(poplex, statement);
430 return pass(maybeoperatorComma, expect(";"), poplex);
431 }
432 function property(type) {
433 if (type == "variable") {cx.marked = "property"; return cont();}
434 }
435 function objprop(type, value) {
436 if (type == "variable" || cx.style == "keyword") {
437 cx.marked = "property";
438 if (value == "get" || value == "set") return cont(getterSetter);
439 return cont(afterprop);
440 } else if (type == "number" || type == "string") {
441 cx.marked = jsonldMode ? "property" : (cx.style + " property");
442 return cont(afterprop);
443 } else if (type == "jsonld-keyword") {
444 return cont(afterprop);
445 } else if (type == "[") {
446 return cont(expression, expect("]"), afterprop);
447 }
448 }
449 function getterSetter(type) {
450 if (type != "variable") return pass(afterprop);
451 cx.marked = "property";
452 return cont(functiondef);
453 }
454 function afterprop(type) {
455 if (type == ":") return cont(expressionNoComma);
456 if (type == "(") return pass(functiondef);
457 }
458 function commasep(what, end) {
459 function proceed(type) {
460 if (type == ",") {
461 var lex = cx.state.lexical;
462 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
463 return cont(what, proceed);
464 }
465 if (type == end) return cont();
466 return cont(expect(end));
467 }
468 return function(type) {
469 if (type == end) return cont();
470 return pass(what, proceed);
471 };
472 }
473 function contCommasep(what, end, info) {
474 for (var i = 3; i < arguments.length; i++)
475 cx.cc.push(arguments[i]);
476 return cont(pushlex(end, info), commasep(what, end), poplex);
477 }
478 function block(type) {
479 if (type == "}") return cont();
480 return pass(statement, block);
481 }
482 function maybetype(type) {
483 if (isTS && type == ":") return cont(typedef);
484 }
485 function typedef(type) {
486 if (type == "variable"){cx.marked = "variable-3"; return cont();}
487 }
488 function vardef() {
489 return pass(pattern, maybetype, maybeAssign, vardefCont);
490 }
491 function pattern(type, value) {
492 if (type == "variable") { register(value); return cont(); }
493 if (type == "[") return contCommasep(pattern, "]");
494 if (type == "{") return contCommasep(proppattern, "}");
495 }
496 function proppattern(type, value) {
497 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
498 register(value);
499 return cont(maybeAssign);
500 }
501 if (type == "variable") cx.marked = "property";
502 return cont(expect(":"), pattern, maybeAssign);
503 }
504 function maybeAssign(_type, value) {
505 if (value == "=") return cont(expressionNoComma);
506 }
507 function vardefCont(type) {
508 if (type == ",") return cont(vardef);
509 }
510 function maybeelse(type, value) {
511 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
512 }
513 function forspec(type) {
514 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
515 }
516 function forspec1(type) {
517 if (type == "var") return cont(vardef, expect(";"), forspec2);
518 if (type == ";") return cont(forspec2);
519 if (type == "variable") return cont(formaybeinof);
520 return pass(expression, expect(";"), forspec2);
521 }
522 function formaybeinof(_type, value) {
523 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
524 return cont(maybeoperatorComma, forspec2);
525 }
526 function forspec2(type, value) {
527 if (type == ";") return cont(forspec3);
528 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
529 return pass(expression, expect(";"), forspec3);
530 }
531 function forspec3(type) {
532 if (type != ")") cont(expression);
533 }
534 function functiondef(type, value) {
535 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
536 if (type == "variable") {register(value); return cont(functiondef);}
537 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
538 }
539 function funarg(type) {
540 if (type == "spread") return cont(funarg);
541 return pass(pattern, maybetype);
542 }
543 function className(type, value) {
544 if (type == "variable") {register(value); return cont(classNameAfter);}
545 }
546 function classNameAfter(type, value) {
547 if (value == "extends") return cont(expression, classNameAfter);
548 if (type == "{") return cont(pushlex("}"), classBody, poplex);
549 }
550 function classBody(type, value) {
551 if (type == "variable" || cx.style == "keyword") {
552 if (value == "static") {
553 cx.marked = "keyword";
554 return cont(classBody);
555 }
556 cx.marked = "property";
557 if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
558 return cont(functiondef, classBody);
559 }
560 if (value == "*") {
561 cx.marked = "keyword";
562 return cont(classBody);
563 }
564 if (type == ";") return cont(classBody);
565 if (type == "}") return cont();
566 }
567 function classGetterSetter(type) {
568 if (type != "variable") return pass();
569 cx.marked = "property";
570 return cont();
571 }
572 function afterModule(type, value) {
573 if (type == "string") return cont(statement);
574 if (type == "variable") { register(value); return cont(maybeFrom); }
575 }
576 function afterExport(_type, value) {
577 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
578 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
579 return pass(statement);
580 }
581 function afterImport(type) {
582 if (type == "string") return cont();
583 return pass(importSpec, maybeFrom);
584 }
585 function importSpec(type, value) {
586 if (type == "{") return contCommasep(importSpec, "}");
587 if (type == "variable") register(value);
588 if (value == "*") cx.marked = "keyword";
589 return cont(maybeAs);
590 }
591 function maybeAs(_type, value) {
592 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
593 }
594 function maybeFrom(_type, value) {
595 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
596 }
597 function arrayLiteral(type) {
598 if (type == "]") return cont();
599 return pass(expressionNoComma, maybeArrayComprehension);
600 }
601 function maybeArrayComprehension(type) {
602 if (type == "for") return pass(comprehension, expect("]"));
603 if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
604 return pass(commasep(expressionNoComma, "]"));
605 }
606 function comprehension(type) {
607 if (type == "for") return cont(forspec, comprehension);
608 if (type == "if") return cont(expression, comprehension);
609 }
610
611 function isContinuedStatement(state, textAfter) {
612 return state.lastType == "operator" || state.lastType == "," ||
613 isOperatorChar.test(textAfter.charAt(0)) ||
614 /[,.]/.test(textAfter.charAt(0));
615 }
616
617 // Interface
618
619 return {
620 startState: function(basecolumn) {
621 var state = {
622 tokenize: tokenBase,
623 lastType: "sof",
624 cc: [],
625 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
626 localVars: parserConfig.localVars,
627 context: parserConfig.localVars && {vars: parserConfig.localVars},
628 indented: 0
629 };
630 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
631 state.globalVars = parserConfig.globalVars;
632 return state;
633 },
634
635 token: function(stream, state) {
636 if (stream.sol()) {
637 if (!state.lexical.hasOwnProperty("align"))
638 state.lexical.align = false;
639 state.indented = stream.indentation();
640 findFatArrow(stream, state);
641 }
642 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
643 var style = state.tokenize(stream, state);
644 if (type == "comment") return style;
645 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
646 return parseJS(state, style, type, content, stream);
647 },
648
649 indent: function(state, textAfter) {
650 if (state.tokenize == tokenComment) return CodeMirror.Pass;
651 if (state.tokenize != tokenBase) return 0;
652 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
653 // Kludge to prevent 'maybelse' from blocking lexical scope pops
654 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
655 var c = state.cc[i];
656 if (c == poplex) lexical = lexical.prev;
657 else if (c != maybeelse) break;
658 }
659 if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
660 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
661 lexical = lexical.prev;
662 var type = lexical.type, closing = firstChar == type;
663
664 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
665 else if (type == "form" && firstChar == "{") return lexical.indented;
666 else if (type == "form") return lexical.indented + indentUnit;
667 else if (type == "stat")
668 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
669 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
670 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
671 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
672 else return lexical.indented + (closing ? 0 : indentUnit);
673 },
674
675 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
676 blockCommentStart: jsonMode ? null : "/*",
677 blockCommentEnd: jsonMode ? null : "*/",
678 lineComment: jsonMode ? null : "//",
679 fold: "brace",
680 closeBrackets: "()[]{}''\"\"``",
681
682 helperType: jsonMode ? "json" : "javascript",
683 jsonldMode: jsonldMode,
684 jsonMode: jsonMode
685 };
686});
687
688CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
689
690CodeMirror.defineMIME("text/javascript", "javascript");
691CodeMirror.defineMIME("text/ecmascript", "javascript");
692CodeMirror.defineMIME("application/javascript", "javascript");
693CodeMirror.defineMIME("application/x-javascript", "javascript");
694CodeMirror.defineMIME("application/ecmascript", "javascript");
695CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
696CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
697CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
698CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
699CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
700
701});
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/neo.css b/sources/samples/toolbarconfigurator/lib/codemirror/neo.css
new file mode 100644
index 0000000..d019aab
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/neo.css
@@ -0,0 +1,36 @@
1/* neo theme for codemirror */
2
3/* Color scheme */
4
5.cm-s-neo.CodeMirror {
6 background-color:#ffffff;
7 color:#2e383c;
8 line-height:1.4375;
9}
10.cm-s-neo .cm-comment {color:#75787b}
11.cm-s-neo .cm-keyword, .cm-s-neo .cm-property {color:#1d75b3}
12.cm-s-neo .cm-atom,.cm-s-neo .cm-number {color:#75438a}
13.cm-s-neo .cm-node,.cm-s-neo .cm-tag {color:#9c3328}
14.cm-s-neo .cm-string {color:#b35e14}
15.cm-s-neo .cm-variable,.cm-s-neo .cm-qualifier {color:#047d65}
16
17
18/* Editor styling */
19
20.cm-s-neo pre {
21 padding:0;
22}
23
24.cm-s-neo .CodeMirror-gutters {
25 border:none;
26 border-right:10px solid transparent;
27 background-color:transparent;
28}
29
30.cm-s-neo .CodeMirror-linenumber {
31 padding:0;
32 color:#e0e2e5;
33}
34
35.cm-s-neo .CodeMirror-guttermarker { color: #1d75b3; }
36.cm-s-neo .CodeMirror-guttermarker-subtle { color: #e0e2e5; }
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/show-hint.css b/sources/samples/toolbarconfigurator/lib/codemirror/show-hint.css
new file mode 100644
index 0000000..924e638
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/show-hint.css
@@ -0,0 +1,38 @@
1.CodeMirror-hints {
2 position: absolute;
3 z-index: 10;
4 overflow: hidden;
5 list-style: none;
6
7 margin: 0;
8 padding: 2px;
9
10 -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
11 -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
12 box-shadow: 2px 3px 5px rgba(0,0,0,.2);
13 border-radius: 3px;
14 border: 1px solid silver;
15
16 background: white;
17 font-size: 90%;
18 font-family: monospace;
19
20 max-height: 20em;
21 overflow-y: auto;
22}
23
24.CodeMirror-hint {
25 margin: 0;
26 padding: 0 4px;
27 border-radius: 2px;
28 max-width: 19em;
29 overflow: hidden;
30 white-space: pre;
31 color: black;
32 cursor: pointer;
33}
34
35li.CodeMirror-hint-active {
36 background: #08f;
37 color: white;
38}
diff --git a/sources/samples/toolbarconfigurator/lib/codemirror/show-hint.js b/sources/samples/toolbarconfigurator/lib/codemirror/show-hint.js
new file mode 100644
index 0000000..539181f
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/lib/codemirror/show-hint.js
@@ -0,0 +1,392 @@
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: http://codemirror.net/LICENSE
3
4(function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11})(function(CodeMirror) {
12 "use strict";
13
14 var HINT_ELEMENT_CLASS = "CodeMirror-hint";
15 var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
16
17 // This is the old interface, kept around for now to stay
18 // backwards-compatible.
19 CodeMirror.showHint = function(cm, getHints, options) {
20 if (!getHints) return cm.showHint(options);
21 if (options && options.async) getHints.async = true;
22 var newOpts = {hint: getHints};
23 if (options) for (var prop in options) newOpts[prop] = options[prop];
24 return cm.showHint(newOpts);
25 };
26
27 CodeMirror.defineExtension("showHint", function(options) {
28 // We want a single cursor position.
29 if (this.listSelections().length > 1 || this.somethingSelected()) return;
30
31 if (this.state.completionActive) this.state.completionActive.close();
32 var completion = this.state.completionActive = new Completion(this, options);
33 if (!completion.options.hint) return;
34
35 CodeMirror.signal(this, "startCompletion", this);
36 completion.update();
37 });
38
39 function Completion(cm, options) {
40 this.cm = cm;
41 this.options = this.buildOptions(options);
42 this.widget = null;
43 this.debounce = 0;
44 this.tick = 0;
45 this.startPos = this.cm.getCursor();
46 this.startLen = this.cm.getLine(this.startPos.line).length;
47
48 var self = this;
49 cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
50 }
51
52 var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
53 return setTimeout(fn, 1000/60);
54 };
55 var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
56
57 Completion.prototype = {
58 close: function() {
59 if (!this.active()) return;
60 this.cm.state.completionActive = null;
61 this.tick = null;
62 this.cm.off("cursorActivity", this.activityFunc);
63
64 if (this.widget) this.widget.close();
65 CodeMirror.signal(this.cm, "endCompletion", this.cm);
66 },
67
68 active: function() {
69 return this.cm.state.completionActive == this;
70 },
71
72 pick: function(data, i) {
73 var completion = data.list[i];
74 if (completion.hint) completion.hint(this.cm, data, completion);
75 else this.cm.replaceRange(getText(completion), completion.from || data.from,
76 completion.to || data.to, "complete");
77 CodeMirror.signal(data, "pick", completion);
78 this.close();
79 },
80
81 showHints: function(data) {
82 if (!data || !data.list.length || !this.active()) return this.close();
83
84 if (this.options.completeSingle && data.list.length == 1)
85 this.pick(data, 0);
86 else
87 this.showWidget(data);
88 },
89
90 cursorActivity: function() {
91 if (this.debounce) {
92 cancelAnimationFrame(this.debounce);
93 this.debounce = 0;
94 }
95
96 var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
97 if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
98 pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
99 (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
100 this.close();
101 } else {
102 var self = this;
103 this.debounce = requestAnimationFrame(function() {self.update();});
104 if (this.widget) this.widget.disable();
105 }
106 },
107
108 update: function() {
109 if (this.tick == null) return;
110 if (this.data) CodeMirror.signal(this.data, "update");
111 if (!this.options.hint.async) {
112 this.finishUpdate(this.options.hint(this.cm, this.options), myTick);
113 } else {
114 var myTick = ++this.tick, self = this;
115 this.options.hint(this.cm, function(data) {
116 if (self.tick == myTick) self.finishUpdate(data);
117 }, this.options);
118 }
119 },
120
121 finishUpdate: function(data) {
122 this.data = data;
123 var picked = this.widget && this.widget.picked;
124 if (this.widget) this.widget.close();
125 if (data && data.list.length) {
126 if (picked && data.list.length == 1) this.pick(data, 0);
127 else this.widget = new Widget(this, data);
128 }
129 },
130
131 showWidget: function(data) {
132 this.data = data;
133 this.widget = new Widget(this, data);
134 CodeMirror.signal(data, "shown");
135 },
136
137 buildOptions: function(options) {
138 var editor = this.cm.options.hintOptions;
139 var out = {};
140 for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
141 if (editor) for (var prop in editor)
142 if (editor[prop] !== undefined) out[prop] = editor[prop];
143 if (options) for (var prop in options)
144 if (options[prop] !== undefined) out[prop] = options[prop];
145 return out;
146 }
147 };
148
149 function getText(completion) {
150 if (typeof completion == "string") return completion;
151 else return completion.text;
152 }
153
154 function buildKeyMap(completion, handle) {
155 var baseMap = {
156 Up: function() {handle.moveFocus(-1);},
157 Down: function() {handle.moveFocus(1);},
158 PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
159 PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
160 Home: function() {handle.setFocus(0);},
161 End: function() {handle.setFocus(handle.length - 1);},
162 Enter: handle.pick,
163 Tab: handle.pick,
164 Esc: handle.close
165 };
166 var custom = completion.options.customKeys;
167 var ourMap = custom ? {} : baseMap;
168 function addBinding(key, val) {
169 var bound;
170 if (typeof val != "string")
171 bound = function(cm) { return val(cm, handle); };
172 // This mechanism is deprecated
173 else if (baseMap.hasOwnProperty(val))
174 bound = baseMap[val];
175 else
176 bound = val;
177 ourMap[key] = bound;
178 }
179 if (custom)
180 for (var key in custom) if (custom.hasOwnProperty(key))
181 addBinding(key, custom[key]);
182 var extra = completion.options.extraKeys;
183 if (extra)
184 for (var key in extra) if (extra.hasOwnProperty(key))
185 addBinding(key, extra[key]);
186 return ourMap;
187 }
188
189 function getHintElement(hintsElement, el) {
190 while (el && el != hintsElement) {
191 if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
192 el = el.parentNode;
193 }
194 }
195
196 function Widget(completion, data) {
197 this.completion = completion;
198 this.data = data;
199 this.picked = false;
200 var widget = this, cm = completion.cm;
201
202 var hints = this.hints = document.createElement("ul");
203 hints.className = "CodeMirror-hints";
204 this.selectedHint = data.selectedHint || 0;
205
206 var completions = data.list;
207 for (var i = 0; i < completions.length; ++i) {
208 var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
209 var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
210 if (cur.className != null) className = cur.className + " " + className;
211 elt.className = className;
212 if (cur.render) cur.render(elt, data, cur);
213 else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
214 elt.hintId = i;
215 }
216
217 var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
218 var left = pos.left, top = pos.bottom, below = true;
219 hints.style.left = left + "px";
220 hints.style.top = top + "px";
221 // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
222 var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
223 var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
224 (completion.options.container || document.body).appendChild(hints);
225 var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
226 if (overlapY > 0) {
227 var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
228 if (curTop - height > 0) { // Fits above cursor
229 hints.style.top = (top = pos.top - height) + "px";
230 below = false;
231 } else if (height > winH) {
232 hints.style.height = (winH - 5) + "px";
233 hints.style.top = (top = pos.bottom - box.top) + "px";
234 var cursor = cm.getCursor();
235 if (data.from.ch != cursor.ch) {
236 pos = cm.cursorCoords(cursor);
237 hints.style.left = (left = pos.left) + "px";
238 box = hints.getBoundingClientRect();
239 }
240 }
241 }
242 var overlapX = box.right - winW;
243 if (overlapX > 0) {
244 if (box.right - box.left > winW) {
245 hints.style.width = (winW - 5) + "px";
246 overlapX -= (box.right - box.left) - winW;
247 }
248 hints.style.left = (left = pos.left - overlapX) + "px";
249 }
250
251 cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
252 moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
253 setFocus: function(n) { widget.changeActive(n); },
254 menuSize: function() { return widget.screenAmount(); },
255 length: completions.length,
256 close: function() { completion.close(); },
257 pick: function() { widget.pick(); },
258 data: data
259 }));
260
261 if (completion.options.closeOnUnfocus) {
262 var closingOnBlur;
263 cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
264 cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
265 }
266
267 var startScroll = cm.getScrollInfo();
268 cm.on("scroll", this.onScroll = function() {
269 var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
270 var newTop = top + startScroll.top - curScroll.top;
271 var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
272 if (!below) point += hints.offsetHeight;
273 if (point <= editor.top || point >= editor.bottom) return completion.close();
274 hints.style.top = newTop + "px";
275 hints.style.left = (left + startScroll.left - curScroll.left) + "px";
276 });
277
278 CodeMirror.on(hints, "dblclick", function(e) {
279 var t = getHintElement(hints, e.target || e.srcElement);
280 if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
281 });
282
283 CodeMirror.on(hints, "click", function(e) {
284 var t = getHintElement(hints, e.target || e.srcElement);
285 if (t && t.hintId != null) {
286 widget.changeActive(t.hintId);
287 if (completion.options.completeOnSingleClick) widget.pick();
288 }
289 });
290
291 CodeMirror.on(hints, "mousedown", function() {
292 setTimeout(function(){cm.focus();}, 20);
293 });
294
295 CodeMirror.signal(data, "select", completions[0], hints.firstChild);
296 return true;
297 }
298
299 Widget.prototype = {
300 close: function() {
301 if (this.completion.widget != this) return;
302 this.completion.widget = null;
303 this.hints.parentNode.removeChild(this.hints);
304 this.completion.cm.removeKeyMap(this.keyMap);
305
306 var cm = this.completion.cm;
307 if (this.completion.options.closeOnUnfocus) {
308 cm.off("blur", this.onBlur);
309 cm.off("focus", this.onFocus);
310 }
311 cm.off("scroll", this.onScroll);
312 },
313
314 disable: function() {
315 this.completion.cm.removeKeyMap(this.keyMap);
316 var widget = this;
317 this.keyMap = {Enter: function() { widget.picked = true; }};
318 this.completion.cm.addKeyMap(this.keyMap);
319 },
320
321 pick: function() {
322 this.completion.pick(this.data, this.selectedHint);
323 },
324
325 changeActive: function(i, avoidWrap) {
326 if (i >= this.data.list.length)
327 i = avoidWrap ? this.data.list.length - 1 : 0;
328 else if (i < 0)
329 i = avoidWrap ? 0 : this.data.list.length - 1;
330 if (this.selectedHint == i) return;
331 var node = this.hints.childNodes[this.selectedHint];
332 node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
333 node = this.hints.childNodes[this.selectedHint = i];
334 node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
335 if (node.offsetTop < this.hints.scrollTop)
336 this.hints.scrollTop = node.offsetTop - 3;
337 else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
338 this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
339 CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
340 },
341
342 screenAmount: function() {
343 return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
344 }
345 };
346
347 CodeMirror.registerHelper("hint", "auto", function(cm, options) {
348 var helpers = cm.getHelpers(cm.getCursor(), "hint"), words;
349 if (helpers.length) {
350 for (var i = 0; i < helpers.length; i++) {
351 var cur = helpers[i](cm, options);
352 if (cur && cur.list.length) return cur;
353 }
354 } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
355 if (words) return CodeMirror.hint.fromList(cm, {words: words});
356 } else if (CodeMirror.hint.anyword) {
357 return CodeMirror.hint.anyword(cm, options);
358 }
359 });
360
361 CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
362 var cur = cm.getCursor(), token = cm.getTokenAt(cur);
363 var found = [];
364 for (var i = 0; i < options.words.length; i++) {
365 var word = options.words[i];
366 if (word.slice(0, token.string.length) == token.string)
367 found.push(word);
368 }
369
370 if (found.length) return {
371 list: found,
372 from: CodeMirror.Pos(cur.line, token.start),
373 to: CodeMirror.Pos(cur.line, token.end)
374 };
375 });
376
377 CodeMirror.commands.autocomplete = CodeMirror.showHint;
378
379 var defaultOptions = {
380 hint: CodeMirror.hint.auto,
381 completeSingle: true,
382 alignWithWord: true,
383 closeCharacters: /[\s()\[\]{};:>,]/,
384 closeOnUnfocus: true,
385 completeOnSingleClick: false,
386 container: null,
387 customKeys: null,
388 extraKeys: null
389 };
390
391 CodeMirror.defineOption("hintOptions", null);
392});
diff --git a/sources/samples/toolbarconfigurator/package.json b/sources/samples/toolbarconfigurator/package.json
new file mode 100644
index 0000000..1fceff9
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/package.json
@@ -0,0 +1,12 @@
1{
2 "name": "ckeditor-toolbarconfigurator",
3 "version": "0.0.0",
4 "description": "",
5 "author": "CKSource (http://cksource.com/)",
6 "license": "For licensing, see LICENSE.md or http://ckeditor.com/license.",
7 "devDependencies": {
8 "benderjs": "~0.2.1",
9 "benderjs-chai": "~0.2.0",
10 "benderjs-mocha": "~0.1.2"
11 }
12}
diff --git a/sources/samples/toolbarconfigurator/tests/one.js b/sources/samples/toolbarconfigurator/tests/one.js
new file mode 100644
index 0000000..1d3837f
--- /dev/null
+++ b/sources/samples/toolbarconfigurator/tests/one.js
@@ -0,0 +1,9 @@
1/* global describe, it, expect, ToolbarConfigurator */
2
3describe( 'Full toolbar configurator', function() {
4 var FTE = ToolbarConfigurator.FullToolbarEditor;
5
6 it( 'exists', function() {
7 expect( FTE ).to.be.a( 'function' );
8 } );
9} );