2 * CodePress - Real Time Syntax Highlighting Editor written in JavaScript - http://codepress.org/
4 * Copyright (C) 2007 Fernando M.A.d.S. <fermads@gmail.com>
8 * Michael Hurni <michael.hurni@gmail.com>
10 * This program is free software; you can redistribute it and/or modify it under the terms of the
11 * GNU Lesser General Public License as published by the Free Software Foundation.
13 * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php
21 // set initial vars and start sh
22 initialize : function() {
23 if(typeof(editor
)=='undefined' && !arguments
[0]) return;
24 chars
= '|32|46|62|'; // charcodes that trigger syntax highlighting
25 cc
= '\u2009'; // control char
26 editor
= document
.getElementsByTagName('body')[0];
27 document
.designMode
= 'on';
28 document
.addEventListener('keyup', this.keyHandler
, true);
29 window
.addEventListener('scroll', function() { if(!CodePress
.scrolling
) CodePress
.syntaxHighlight('scroll') }, false);
30 completeChars
= this.getCompleteChars();
31 // CodePress.syntaxHighlight('init');
35 keyHandler : function(evt
) {
36 keyCode
= evt
.keyCode
;
37 charCode
= evt
.charCode
;
39 if((evt
.ctrlKey
|| evt
.metaKey
) && evt
.shiftKey
&& charCode
!=90) { // shortcuts = ctrl||appleKey+shift+key!=z(undo)
40 CodePress
.shortcuts(charCode
?charCode
:keyCode
);
42 else if(completeChars
.indexOf('|'+String
.fromCharCode(charCode
)+'|')!=-1 && CodePress
.autocomplete
) { // auto complete
43 CodePress
.complete(String
.fromCharCode(charCode
));
45 else if(chars
.indexOf('|'+charCode
+'|')!=-1||keyCode
==13) { // syntax highlighting
46 CodePress
.syntaxHighlight('generic');
48 else if(keyCode
==9 || evt
.tabKey
) { // snippets activation (tab)
49 CodePress
.snippets(evt
);
51 else if(keyCode
==46||keyCode
==8) { // save to history when delete or backspace pressed
52 CodePress
.actions
.history
[CodePress
.actions
.next()] = editor
.innerHTML
;
54 else if((charCode
==122||charCode
==121||charCode
==90) && evt
.ctrlKey
) { // undo and redo
55 (charCode
==121||evt
.shiftKey
) ? CodePress
.actions
.redo() : CodePress
.actions
.undo();
58 else if(keyCode
==86 && evt
.ctrlKey
) { // paste
59 // TODO: pasted text should be parsed and highlighted
63 // put cursor back to its original position after every parsing
64 findString : function() {
65 var sel
= window
.getSelection();
66 var range
= window
.document
.createRange();
67 var span
= window
.document
.getElementsByTagName('span')[0];
69 range
.selectNode(span
);
70 sel
.removeAllRanges();
72 span
.parentNode
.removeChild(span
);
74 //window.getSelection().getRangeAt(0).deleteContents();
77 // split big files, highlighting parts of it
78 split : function(code
,flag
) {
80 this.scrolling
= true;
84 this.scrolling
= false;
85 mid
= code
.indexOf('<SPAN>');
86 if(mid
-2000<0) {ini
=0;end
=4000;}
87 else if(mid
+2000>code
.length
) {ini
=code
.length
-4000;end
=code
.length
;}
88 else {ini
=mid
-2000;end
=mid
+2000;}
89 code
= code
.substring(ini
,end
);
94 // syntax highlighting parser
95 syntaxHighlight : function(flag
) {
96 //if(document.designMode=='off') document.designMode='on'
98 var span
= document
.createElement('span');
99 window
.getSelection().getRangeAt(0).insertNode(span
);
102 o
= editor
.innerHTML
;
103 // o = o.replace(/<br>/g,'\r\n');
104 // o = o.replace(/<(b|i|s|u|a|em|tt|ins|big|cite|strong)?>/g,'');
106 o
= o
.replace(/<(?!span|\/span|br).*?>/gi,'');
109 x
= z
= this.split(o
,flag
);
111 // x = x.replace(/\r\n/g,'<br>');
112 x
= x
.replace(/\t/g, ' ');
115 if(arguments
[1]&&arguments
[2]) x
= x
.replace(arguments
[1],arguments
[2]);
117 for(i
=0;i
<Language
.syntax
.length
;i
++)
118 x
= x
.replace(Language
.syntax
[i
].input
,Language
.syntax
[i
].output
);
120 editor
.innerHTML
= this.actions
.history
[this.actions
.next()] = (flag
=='scroll') ? x
: o
.split(z
).join(x
);
122 if(flag
!='init') this.findString();
125 getLastWord : function() {
126 var rangeAndCaret
= CodePress
.getRangeAndCaret();
127 words
= rangeAndCaret
[0].substring(rangeAndCaret
[1]-40,rangeAndCaret
[1]);
128 words
= words
.replace(/[\s\n\r\);\W]/g,'\n').split('\n');
129 return words
[words
.length
-1].replace(/[\W]/gi,'').toLowerCase();
132 snippets : function(evt
) {
133 var snippets
= Language
.snippets
;
134 var trigger
= this.getLastWord();
135 for (var i
=0; i
<snippets
.length
; i
++) {
136 if(snippets
[i
].input
== trigger
) {
137 var content
= snippets
[i
].output
.replace(/</g
,'<');
138 content
= content
.replace(/>/g
,'>');
139 if(content
.indexOf('$0')<0) content
+= cc
;
140 else content
= content
.replace(/\$0/,cc
);
141 content
= content
.replace(/\n/g,'<br>');
142 var pattern
= new RegExp(trigger
+cc
,'gi');
143 evt
.preventDefault(); // prevent the tab key from being added
144 this.syntaxHighlight('snippets',pattern
,content
);
149 readOnly : function() {
150 document
.designMode
= (arguments
[0]) ? 'off' : 'on';
153 complete : function(trigger
) {
154 window
.getSelection().getRangeAt(0).deleteContents();
155 var complete
= Language
.complete
;
156 for (var i
=0; i
<complete
.length
; i
++) {
157 if(complete
[i
].input
== trigger
) {
158 var pattern
= new RegExp('\\'+trigger
+cc
);
159 var content
= complete
[i
].output
.replace(/\$0/g,cc
);
160 parent
.setTimeout(function () { CodePress
.syntaxHighlight('complete',pattern
,content
)},0); // wait for char to appear on screen
165 getCompleteChars : function() {
167 for(var i
=0;i
<Language
.complete
.length
;i
++)
168 cChars
+= '|'+Language
.complete
[i
].input
;
172 shortcuts : function() {
173 var cCode
= arguments
[0];
174 if(cCode
==13) cCode
= '[enter]';
175 else if(cCode
==32) cCode
= '[space]';
176 else cCode
= '['+String
.fromCharCode(charCode
).toLowerCase()+']';
177 for(var i
=0;i
<Language
.shortcuts
.length
;i
++)
178 if(Language
.shortcuts
[i
].input
== cCode
)
179 this.insertCode(Language
.shortcuts
[i
].output
,false);
182 getRangeAndCaret : function() {
183 var range
= window
.getSelection().getRangeAt(0);
184 var range2
= range
.cloneRange();
185 var node
= range
.endContainer
;
186 var caret
= range
.endOffset
;
187 range2
.selectNode(node
);
188 return [range2
.toString(),caret
];
191 insertCode : function(code
,replaceCursorBefore
) {
192 var range
= window
.getSelection().getRangeAt(0);
193 var node
= window
.document
.createTextNode(code
);
194 var selct
= window
.getSelection();
195 var range2
= range
.cloneRange();
196 // Insert text at cursor position
197 selct
.removeAllRanges();
198 range
.deleteContents();
199 range
.insertNode(node
);
200 // Move the cursor to the end of text
201 range2
.selectNode(node
);
202 range2
.collapse(replaceCursorBefore
);
203 selct
.removeAllRanges();
204 selct
.addRange(range2
);
207 // get code from editor
208 getCode : function() {
209 var code
= editor
.innerHTML
;
210 code
= code
.replace(/<br>/g,'\n');
211 code
= code
.replace(/\u2009/g,'');
212 code
= code
.replace(/<.*?>/g,'');
213 code
= code
.replace(/</g,'<');
214 code
= code
.replace(/>/g,'>');
215 code
= code
.replace(/&/gi,'&');
219 // put code inside editor
220 setCode : function() {
221 var code
= arguments
[0];
222 code
= code
.replace(/\u2009/gi,'');
223 code
= code
.replace(/&/gi
,'&');
224 code
= code
.replace(/</g
,'<');
225 code
= code
.replace(/>/g
,'>');
226 editor
.innerHTML
= code
;
229 // undo and redo methods
231 pos
: -1, // actual history position
232 history
: [], // history vector
235 if(editor
.innerHTML
.indexOf(cc
)==-1){
236 window
.getSelection().getRangeAt(0).insertNode(document
.createTextNode(cc
));
237 this.history
[this.pos
] = editor
.innerHTML
;
240 if(typeof(this.history
[this.pos
])=='undefined') this.pos
++;
241 editor
.innerHTML
= this.history
[this.pos
];
242 CodePress
.findString();
247 if(typeof(this.history
[this.pos
])=='undefined') this.pos
--;
248 editor
.innerHTML
= this.history
[this.pos
];
249 CodePress
.findString();
252 next : function() { // get next vector position and clean old ones
253 if(this.pos
>20) this.history
[this.pos
-21] = undefined;
260 window
.addEventListener('load', function() { CodePress
.initialize('new'); }, true);