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>
7 * Fernando M.A.d.S. <fermads@gmail.com>
8 * Michael Hurni <michael.hurni@gmail.com>
12 * This program is free software; you can redistribute it and/or modify it under the terms of the
13 * GNU Lesser General Public License as published by the Free Software Foundation.
15 * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php
22 // set initial vars and start sh
23 initialize : function() {
24 if(typeof(editor
)=='undefined' && !arguments
[0]) return;
25 body
= document
.getElementsByTagName('body')[0];
26 body
.innerHTML
= body
.innerHTML
.replace(/\n/g,"");
27 chars
= '|32|46|62|8|'; // charcodes that trigger syntax highlighting
28 cc
= '\u2009'; // carret char
29 editor
= document
.getElementsByTagName('pre')[0];
30 document
.designMode
= 'on';
31 document
.addEventListener('keypress', this.keyHandler
, true);
32 window
.addEventListener('scroll', function() { if(!CodePress
.scrolling
) CodePress
.syntaxHighlight('scroll') }, false);
33 completeChars
= this.getCompleteChars();
34 completeEndingChars
= this.getCompleteEndingChars();
38 keyHandler : function(evt
) {
39 keyCode
= evt
.keyCode
;
40 charCode
= evt
.charCode
;
41 fromChar
= String
.fromCharCode(charCode
);
43 if((evt
.ctrlKey
|| evt
.metaKey
) && evt
.shiftKey
&& charCode
!=90) { // shortcuts = ctrl||appleKey+shift+key!=z(undo)
44 CodePress
.shortcuts(charCode
?charCode
:keyCode
);
46 else if( (completeEndingChars
.indexOf('|'+fromChar
+'|')!= -1 || completeChars
.indexOf('|'+fromChar
+'|')!=-1) && CodePress
.autocomplete
) { // auto complete
47 if(!CodePress
.completeEnding(fromChar
))
48 CodePress
.complete(fromChar
);
50 else if(chars
.indexOf('|'+charCode
+'|')!=-1||keyCode
==13) { // syntax highlighting
51 top
.setTimeout(function(){CodePress
.syntaxHighlight('generic');},100);
53 else if(keyCode
==9 || evt
.tabKey
) { // snippets activation (tab)
54 CodePress
.snippets(evt
);
56 else if(keyCode
==46||keyCode
==8) { // save to history when delete or backspace pressed
57 CodePress
.actions
.history
[CodePress
.actions
.next()] = editor
.innerHTML
;
59 else if((charCode
==122||charCode
==121||charCode
==90) && evt
.ctrlKey
) { // undo and redo
60 (charCode
==121||evt
.shiftKey
) ? CodePress
.actions
.redo() : CodePress
.actions
.undo();
63 else if(charCode
==118 && evt
.ctrlKey
) { // handle paste
64 top
.setTimeout(function(){CodePress
.syntaxHighlight('generic');},100);
66 else if(charCode
==99 && evt
.ctrlKey
) { // handle cut
67 //alert(window.getSelection().getRangeAt(0).toString().replace(/\t/g,'FFF'));
72 // put cursor back to its original position after every parsing
73 findString : function() {
75 window
.getSelection().getRangeAt(0).deleteContents();
78 // split big files, highlighting parts of it
79 split : function(code
,flag
) {
81 this.scrolling
= true;
85 this.scrolling
= false;
86 mid
= code
.indexOf(cc
);
87 if(mid
-2000<0) {ini
=0;end
=4000;}
88 else if(mid
+2000>code
.length
) {ini
=code
.length
-4000;end
=code
.length
;}
89 else {ini
=mid
-2000;end
=mid
+2000;}
90 code
= code
.substring(ini
,end
);
95 getEditor : function() {
96 if(!document
.getElementsByTagName('pre')[0]) {
97 body
= document
.getElementsByTagName('body')[0];
98 if(!body
.innerHTML
) return body
;
99 if(body
.innerHTML
=="<br>") body
.innerHTML
= "<pre> </pre>";
100 else body
.innerHTML
= "<pre>"+body
.innerHTML
+"</pre>";
102 return document
.getElementsByTagName('pre')[0];
105 // syntax highlighting parser
106 syntaxHighlight : function(flag
) {
107 //if(document.designMode=='off') document.designMode='on'
108 if(flag
!= 'init') { window
.getSelection().getRangeAt(0).insertNode(document
.createTextNode(cc
));}
109 editor
= CodePress
.getEditor();
110 o
= editor
.innerHTML
;
111 o
= o
.replace(/<br>/g,'\n');
112 o
= o
.replace(/<.*?>/g,'');
113 x
= z
= this.split(o
,flag
);
114 x
= x
.replace(/\n/g,'<br>');
116 if(arguments
[1]&&arguments
[2]) x
= x
.replace(arguments
[1],arguments
[2]);
118 for(i
=0;i
<Language
.syntax
.length
;i
++)
119 x
= x
.replace(Language
.syntax
[i
].input
,Language
.syntax
[i
].output
);
121 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 getCompleteEndingChars : function() {
174 for(var i
=0;i
<Language
.complete
.length
;i
++)
175 cChars
+= '|'+Language
.complete
[i
].output
.charAt(Language
.complete
[i
].output
.length
-1);
179 completeEnding : function(trigger
) {
180 var range
= window
.getSelection().getRangeAt(0);
182 range
.setEnd(range
.endContainer
, range
.endOffset
+1)
187 var next_character
= range
.toString()
188 range
.setEnd(range
.endContainer
, range
.endOffset
-1)
189 if(next_character
!= trigger
) return false;
191 range
.setEnd(range
.endContainer
, range
.endOffset
+1)
192 range
.deleteContents();
197 shortcuts : function() {
198 var cCode
= arguments
[0];
199 if(cCode
==13) cCode
= '[enter]';
200 else if(cCode
==32) cCode
= '[space]';
201 else cCode
= '['+String
.fromCharCode(charCode
).toLowerCase()+']';
202 for(var i
=0;i
<Language
.shortcuts
.length
;i
++)
203 if(Language
.shortcuts
[i
].input
== cCode
)
204 this.insertCode(Language
.shortcuts
[i
].output
,false);
207 getRangeAndCaret : function() {
208 var range
= window
.getSelection().getRangeAt(0);
209 var range2
= range
.cloneRange();
210 var node
= range
.endContainer
;
211 var caret
= range
.endOffset
;
212 range2
.selectNode(node
);
213 return [range2
.toString(),caret
];
216 insertCode : function(code
,replaceCursorBefore
) {
217 var range
= window
.getSelection().getRangeAt(0);
218 var node
= window
.document
.createTextNode(code
);
219 var selct
= window
.getSelection();
220 var range2
= range
.cloneRange();
221 // Insert text at cursor position
222 selct
.removeAllRanges();
223 range
.deleteContents();
224 range
.insertNode(node
);
225 // Move the cursor to the end of text
226 range2
.selectNode(node
);
227 range2
.collapse(replaceCursorBefore
);
228 selct
.removeAllRanges();
229 selct
.addRange(range2
);
232 // get code from editor
233 getCode : function() {
234 if(!document
.getElementsByTagName('pre')[0] || editor
.innerHTML
== '')
235 editor
= CodePress
.getEditor();
236 var code
= editor
.innerHTML
;
237 code
= code
.replace(/<br>/g,'\n');
238 code
= code
.replace(/\u2009/g,'');
239 code
= code
.replace(/<.*?>/g,'');
240 code
= code
.replace(/</g,'<');
241 code
= code
.replace(/>/g,'>');
242 code
= code
.replace(/&/gi,'&');
246 // put code inside editor
247 setCode : function() {
248 var code
= arguments
[0];
249 code
= code
.replace(/\u2009/gi,'');
250 code
= code
.replace(/&/gi
,'&');
251 code
= code
.replace(/</g
,'<');
252 code
= code
.replace(/>/g
,'>');
253 editor
.innerHTML
= code
;
255 document
.getElementsByTagName('body')[0].innerHTML
= '';
258 // undo and redo methods
260 pos
: -1, // actual history position
261 history
: [], // history vector
264 editor
= CodePress
.getEditor();
265 if(editor
.innerHTML
.indexOf(cc
)==-1){
266 if(editor
.innerHTML
!= " ")
267 window
.getSelection().getRangeAt(0).insertNode(document
.createTextNode(cc
));
268 this.history
[this.pos
] = editor
.innerHTML
;
271 if(typeof(this.history
[this.pos
])=='undefined') this.pos
++;
272 editor
.innerHTML
= this.history
[this.pos
];
273 if(editor
.innerHTML
.indexOf(cc
)>-1) editor
.innerHTML
+=cc
;
274 CodePress
.findString();
278 // editor = CodePress.getEditor();
280 if(typeof(this.history
[this.pos
])=='undefined') this.pos
--;
281 editor
.innerHTML
= this.history
[this.pos
];
282 CodePress
.findString();
285 next : function() { // get next vector position and clean old ones
286 if(this.pos
>20) this.history
[this.pos
-21] = undefined;
293 window
.addEventListener('load', function() { CodePress
.initialize('new'); }, true);