fbfbc00895c357173a8e7f64a56b7bb4076a6bd4
[redakcja.git] / project / static / js / highlight.js
1         // change_to: "on" or "off"\r
2         EditArea.prototype.change_highlight= function(change_to){\r
3                 if(this.settings["syntax"].length==0 && change_to==false){\r
4                         this.switchClassSticky(_$("highlight"), 'editAreaButtonDisabled', true);\r
5                         this.switchClassSticky(_$("reset_highlight"), 'editAreaButtonDisabled', true);\r
6                         return false;\r
7                 }\r
8                 \r
9                 if(this.do_highlight==change_to)\r
10                         return false;\r
11         \r
12                         \r
13                 this.getIESelection();\r
14                 var pos_start= this.textarea.selectionStart;\r
15                 var pos_end= this.textarea.selectionEnd;\r
16                 \r
17                 if(this.do_highlight===true || change_to==false)\r
18                         this.disable_highlight();\r
19                 else\r
20                         this.enable_highlight();\r
21                 this.textarea.focus();\r
22                 this.textarea.selectionStart = pos_start;\r
23                 this.textarea.selectionEnd = pos_end;\r
24                 this.setIESelection();\r
25                                 \r
26         };\r
27         \r
28         EditArea.prototype.disable_highlight= function(displayOnly){\r
29                 var t= this, a=t.textarea, new_Obj, old_class, new_class;\r
30                         \r
31                 t.selection_field.innerHTML="";\r
32                 t.selection_field_text.innerHTML="";\r
33                 t.content_highlight.style.visibility="hidden";\r
34                 // replacing the node is far more faster than deleting it's content in firefox\r
35                 new_Obj= t.content_highlight.cloneNode(false);\r
36                 new_Obj.innerHTML= "";                  \r
37                 t.content_highlight.parentNode.insertBefore(new_Obj, t.content_highlight);\r
38                 t.content_highlight.parentNode.removeChild(t.content_highlight);        \r
39                 t.content_highlight= new_Obj;\r
40                 old_class= parent.getAttribute( a,"class" );\r
41                 if(old_class){\r
42                         new_class= old_class.replace( "hidden","" );\r
43                         parent.setAttribute( a, "class", new_class );\r
44                 }\r
45         \r
46                 a.style.backgroundColor="transparent";  // needed in order to see the bracket finders\r
47                 \r
48                 //var icon= document.getElementById("highlight");\r
49                 //setAttribute(icon, "class", getAttribute(icon, "class").replace(/ selected/g, "") );\r
50                 //t.restoreClass(icon);\r
51                 //t.switchClass(icon,'editAreaButtonNormal');\r
52                 t.switchClassSticky(_$("highlight"), 'editAreaButtonNormal', true);\r
53                 t.switchClassSticky(_$("reset_highlight"), 'editAreaButtonDisabled', true);\r
54         \r
55                 t.do_highlight=false;\r
56         \r
57                 t.switchClassSticky(_$("change_smooth_selection"), 'editAreaButtonSelected', true);\r
58                 if(typeof(t.smooth_selection_before_highlight)!="undefined" && t.smooth_selection_before_highlight===false){\r
59                         t.change_smooth_selection_mode(false);\r
60                 }\r
61                 \r
62         //      this.textarea.style.backgroundColor="#FFFFFF";\r
63         };\r
64 \r
65         EditArea.prototype.enable_highlight= function(){\r
66                 var t=this, a=t.textarea, new_class;\r
67                 t.show_waiting_screen();\r
68                         \r
69                 t.content_highlight.style.visibility="visible";\r
70                 new_class       =parent.getAttribute(a,"class")+" hidden";\r
71                 parent.setAttribute( a, "class", new_class );\r
72                 \r
73                 // IE can't manage mouse click outside text range without this\r
74                 if( t.isIE )\r
75                         a.style.backgroundColor="#FFFFFF";      \r
76 \r
77                 t.switchClassSticky(_$("highlight"), 'editAreaButtonSelected', false);\r
78                 t.switchClassSticky(_$("reset_highlight"), 'editAreaButtonNormal', false);\r
79                 \r
80                 t.smooth_selection_before_highlight=t.smooth_selection;\r
81                 if(!t.smooth_selection)\r
82                         t.change_smooth_selection_mode(true);\r
83                 t.switchClassSticky(_$("change_smooth_selection"), 'editAreaButtonDisabled', true);\r
84                 \r
85                 \r
86                 t.do_highlight=true;\r
87                 t.resync_highlight();\r
88                                         \r
89                 t.hide_waiting_screen();        \r
90         };\r
91         \r
92         /**\r
93          * Ask to update highlighted text\r
94          * @param Array infos - Array of datas returned by EditArea.get_selection_infos()\r
95          */\r
96         EditArea.prototype.maj_highlight= function(infos){\r
97                 // for speed mesure\r
98                 var debug_opti="",tps_start= new Date().getTime(), tps_middle_opti=new Date().getTime();\r
99                 var t=this, hightlighted_text, updated_highlight;       \r
100                 var textToHighlight=infos["full_text"], doSyntaxOpti = false, doHtmlOpti = false, stay_begin="", stay_end="", trace_new , trace_last;\r
101                 \r
102                 if(t.last_text_to_highlight==infos["full_text"] && t.resync_highlight!==true)\r
103                         return;\r
104                                         \r
105                 //  OPTIMISATION: will search to update only changed lines\r
106                 if(t.reload_highlight===true){\r
107                         t.reload_highlight=false;\r
108                 }else if(textToHighlight.length==0){\r
109                         textToHighlight="\n ";\r
110                 }else{\r
111                         // get text change datas\r
112                         changes = t.checkTextEvolution(t.last_text_to_highlight,textToHighlight);\r
113                         \r
114                         // check if it can only reparse the changed text\r
115                         trace_new               = t.get_syntax_trace(changes.newTextLine).replace(/\r/g, '');\r
116                         trace_last              = t.get_syntax_trace(changes.lastTextLine).replace(/\r/g, '');\r
117                         doSyntaxOpti    = ( trace_new == trace_last );\r
118                         \r
119                         // check if the difference comes only from a new line created \r
120                         // => we have to remember that the editor can automaticaly add tabulation or space after the new line) \r
121                         if( !doSyntaxOpti && trace_new == "\n"+trace_last && /^[ \t\s]*\n[ \t\s]*$/.test( changes.newText.replace(/\r/g, '') ) && changes.lastText =="" )\r
122                         {\r
123                                 doSyntaxOpti    = true;\r
124                         }\r
125                         \r
126                         // we do the syntax optimisation\r
127                         if( doSyntaxOpti ){\r
128                                                 \r
129                                 tps_middle_opti=new Date().getTime();   \r
130                         \r
131                                 stay_begin= t.last_hightlighted_text.split("\n").slice(0, changes.lineStart).join("\n");\r
132                                 if(changes.lineStart>0)\r
133                                         stay_begin+= "\n";\r
134                                 stay_end= t.last_hightlighted_text.split("\n").slice(changes.lineLastEnd+1).join("\n");\r
135                                 if(stay_end.length>0)\r
136                                         stay_end= "\n"+stay_end;\r
137                                         \r
138                                 // Final check to see that we're not in the middle of span tags\r
139                                 if( stay_begin.split('<span').length != stay_begin.split('</span').length \r
140                                         || stay_end.split('<span').length != stay_end.split('</span').length )\r
141                                 {\r
142                                         doSyntaxOpti    = false;\r
143                                         stay_end                = '';\r
144                                         stay_begin              = '';\r
145                                 }\r
146                                 else\r
147                                 {\r
148                                         if(stay_begin.length==0 && changes.posLastEnd==-1)\r
149                                                 changes.newTextLine+="\n";\r
150                                         textToHighlight=changes.newTextLine;\r
151                                 }\r
152                         }\r
153                         if(t.settings["debug"]){\r
154                                 var ch =changes;\r
155                                 debug_opti= ( doSyntaxOpti?"Optimisation": "No optimisation" )\r
156                                         +" start: "+ch.posStart +"("+ch.lineStart+")"\r
157                                         +" end_new: "+ ch.posNewEnd+"("+ch.lineNewEnd+")"\r
158                                         +" end_last: "+ ch.posLastEnd+"("+ch.lineLastEnd+")"\r
159                                         +"\nchanged_text: "+ch.newText+" => trace: "+trace_new\r
160                                         +"\nchanged_last_text: "+ch.lastText+" => trace: "+trace_last\r
161                                         //debug_opti+= "\nchanged: "+ infos["full_text"].substring(ch.posStart, ch.posNewEnd);\r
162                                         + "\nchanged_line: "+ch.newTextLine\r
163                                         + "\nlast_changed_line: "+ch.lastTextLine\r
164                                         +"\nstay_begin: "+ stay_begin.slice(-100)\r
165                                         +"\nstay_end: "+ stay_end.substr( 0, 100 );\r
166                                         //debug_opti="start: "+stay_begin_len+ "("+nb_line_start_unchanged+") end: "+ (stay_end_len)+ "("+(splited.length-nb_line_end_unchanged)+") ";\r
167                                         //debug_opti+="changed: "+ textToHighlight.substring(stay_begin_len, textToHighlight.length-stay_end_len)+" \n";\r
168                                         \r
169                                         //debug_opti+="changed: "+ stay_begin.substr(stay_begin.length-200)+ "----------"+ textToHighlight+"------------------"+ stay_end.substr(0,200) +"\n";\r
170                                         +"\n";\r
171                         }\r
172         \r
173                         \r
174                         // END OPTIMISATION\r
175                 }\r
176 \r
177                 tps_end_opti    = new Date().getTime(); \r
178                                 \r
179                 // apply highlight\r
180                 updated_highlight       = t.colorize_text(textToHighlight);\r
181                 tpsAfterReg                     = new Date().getTime();\r
182                 \r
183                 /***\r
184                  * see if we can optimize for updating only the required part of the HTML code\r
185                  * \r
186                  * The goal here will be to find the text node concerned by the modification and to update it\r
187                  */\r
188                 //-------------------------------------------\r
189                 // \r
190                 if( doSyntaxOpti )\r
191                 {\r
192                         try\r
193                         {\r
194                                 var replacedBloc, i, nbStart = '', nbEnd = '', newHtml, lengthOld, lengthNew;\r
195                                 replacedBloc            = t.last_hightlighted_text.substring( stay_begin.length, t.last_hightlighted_text.length - stay_end.length );\r
196                                 \r
197                                 lengthOld       = replacedBloc.length;\r
198                                 lengthNew       = updated_highlight.length;\r
199                                 \r
200                                 // find the identical caracters at the beginning\r
201                                 for( i=0; i < lengthOld && i < lengthNew && replacedBloc.charAt(i) == updated_highlight.charAt(i) ; i++ )\r
202                                 {\r
203                                 }\r
204                                 nbStart = i;\r
205                                 // find the identical caracters at the end\r
206                                 for( i=0; i + nbStart < lengthOld && i + nbStart < lengthNew && replacedBloc.charAt(lengthOld-i-1) == updated_highlight.charAt(lengthNew-i-1) ; i++ )\r
207                                 {\r
208                                 }\r
209                                 nbEnd   = i;\r
210                                 \r
211                                 // get the changes\r
212                                 lastHtml        = replacedBloc.substring( nbStart, lengthOld - nbEnd );\r
213                                 newHtml         = updated_highlight.substring( nbStart, lengthNew - nbEnd );\r
214                                 \r
215                                 \r
216                                 // We can do the optimisation only if we havn't touch to span elements\r
217                                 if( newHtml.indexOf('<span') == -1 && newHtml.indexOf('</span') == -1 \r
218                                         && lastHtml.indexOf('<span') == -1 && lastHtml.indexOf('</span') == -1 )\r
219                                 {\r
220                                         var beginStr, nbOpendedSpan, nbClosedSpan, nbUnchangedChars, span, textNode;\r
221                                         doHtmlOpti              = true;\r
222                                         beginStr                = t.last_hightlighted_text.substr( 0, stay_begin.length + nbStart );\r
223                         \r
224                                         nbOpendedSpan   = beginStr.split('<span').length - 1;\r
225                                         nbClosedSpan    = beginStr.split('</span').length - 1;\r
226                                         // retrieve the previously opened span (Add 1 for the first level span?)\r
227                                         span                    = t.content_highlight.getElementsByTagName('span')[ nbOpendedSpan ];\r
228                                         \r
229                                         //--------[\r
230                                         // get the textNode to update\r
231                                         \r
232                                         // if we're inside a span, we'll take the one that is opened (can be a parent of the current span)\r
233                                         parentSpan              = span;\r
234                                         maxStartOffset  = maxEndOffset = 0;\r
235                                         \r
236                                         // it will be in the child of the root node \r
237                                         if( nbOpendedSpan == nbClosedSpan )\r
238                                         {\r
239                                                 while( parentSpan.parentNode != t.content_highlight && parentSpan.parentNode.tagName != 'PRE' )\r
240                                                 {\r
241                                                         parentSpan      = parentSpan.parentNode;\r
242                                                 }\r
243                                         }\r
244                                         // get the last opened span\r
245                                         else\r
246                                         {\r
247                                                 maxStartOffset  = maxEndOffset = beginStr.length + 1;\r
248                                                 // move to parent node for each closed span found after the lastest open span\r
249                                                 nbClosed = beginStr.substr( Math.max( 0, beginStr.lastIndexOf( '<span', maxStartOffset - 1 ) ) ).split('</span').length - 1;\r
250                                                 while( nbClosed > 0 )\r
251                                                 {\r
252                                                         nbClosed--;\r
253                                                         parentSpan = parentSpan.parentNode;\r
254                                                 }\r
255                                                 \r
256                                                 // find the position of the last opended tag\r
257                                                 while( parentSpan.parentNode != t.content_highlight && parentSpan.parentNode.tagName != 'PRE' && ( tmpMaxStartOffset = Math.max( 0, beginStr.lastIndexOf( '<span', maxStartOffset - 1 ) ) ) < ( tmpMaxEndOffset = Math.max( 0, beginStr.lastIndexOf( '</span', maxEndOffset - 1 ) ) ) )\r
258                                                 {\r
259                                                         maxStartOffset  = tmpMaxStartOffset;\r
260                                                         maxEndOffset    = tmpMaxEndOffset;\r
261                                                 }\r
262                                         }\r
263                                         // Note: maxEndOffset is no more used but maxStartOffset will be used\r
264                                         \r
265                                         if( parentSpan.parentNode == t.content_highlight || parentSpan.parentNode.tagName == 'PRE' )\r
266                                         {\r
267                                                 maxStartOffset  = Math.max( 0, beginStr.indexOf( '<span' ) );\r
268                                         }\r
269                                         \r
270                                         // find the matching text node (this will be one that will be at the end of the beginStr\r
271                                         if( maxStartOffset == beginStr.length )\r
272                                         {\r
273                                                 nbSubSpanBefore = 0;\r
274                                         }\r
275                                         else\r
276                                         {\r
277                                                 lastEndPos                              = Math.max( 0, beginStr.lastIndexOf( '>', maxStartOffset ) );\r
278                 \r
279                                                 // count the number of sub spans\r
280                                                 nbSubSpanBefore                 = beginStr.substr( lastEndPos ).split('<span').length-1;\r
281                                         }\r
282                                         \r
283                                         // there is no sub-span before\r
284                                         if( nbSubSpanBefore == 0 )\r
285                                         {\r
286                                                 textNode        = parentSpan.firstChild;\r
287                                         }\r
288                                         // we need to find where is the text node modified\r
289                                         else\r
290                                         {\r
291                                                 // take the last direct child (no sub-child)\r
292                                                 lastSubSpan     = parentSpan.getElementsByTagName('span')[ nbSubSpanBefore - 1 ];\r
293                                                 while( lastSubSpan.parentNode != parentSpan )\r
294                                                 {\r
295                                                         lastSubSpan     = lastSubSpan.parentNode;\r
296                                                 }\r
297 \r
298                                                 // associate to next text node following the last sub span\r
299                                                 if( lastSubSpan.nextSibling == null || lastSubSpan.nextSibling.nodeType != 3 )\r
300                                                 {\r
301                                                         textNode        = document.createTextNode('');\r
302                                                         lastSubSpan.parentNode.insertBefore( textNode, lastSubSpan.nextSibling );\r
303                                                 }\r
304                                                 else\r
305                                                 {\r
306                                                         textNode        = lastSubSpan.nextSibling;\r
307                                                 }\r
308                                         }\r
309                                         //--------]\r
310                                         \r
311                                         \r
312                                         //--------[\r
313                                         // update the textNode content\r
314                                         \r
315                                         // number of caracters after the last opened of closed span\r
316                                         nbUnchangedChars = beginStr.length - Math.max( 0, beginStr.lastIndexOf( '>' ) + 1 );\r
317                                         \r
318                                         //      console.log( span, textNode, nbOpendedSpan,nbClosedSpan,  span.nextSibling, textNode.length, nbUnchangedChars, lastHtml, lastHtml.length, newHtml, newHtml.length );\r
319                                         //      alert( textNode.parentNode.className +'-'+ textNode.parentNode.tagName+"\n"+ textNode.data +"\n"+ nbUnchangedChars +"\n"+ lastHtml.length +"\n"+ newHtml +"\n"+ newHtml.length  );\r
320                                         \r
321                                         // IE only manage \r for cariage return in textNode and not \n or \r\n\r
322                                         if( t.isIE )\r
323                                         {\r
324                                                 nbUnchangedChars        -= ( beginStr.substr( beginStr.length - nbUnchangedChars ).split("\n").length - 1 );\r
325                                                 //alert( textNode.data.replace(/\r/g, '_r').replace(/\n/g, '_n')); \r
326                                                 textNode.replaceData( nbUnchangedChars, lastHtml.replace(/\n/g, '').length, newHtml.replace(/\n/g, '') );\r
327                                         }\r
328                                         else\r
329                                         {\r
330                                                 textNode.replaceData( nbUnchangedChars, lastHtml.length, newHtml );\r
331                                         }\r
332                                         //--------]\r
333                                 }\r
334                         }\r
335                         // an exception shouldn't occured but if replaceData failed at least it won't break everything\r
336                         catch( e )\r
337                         {\r
338                 //              throw e;\r
339                 //              console.log( e );\r
340                                 doHtmlOpti      = false;\r
341                         }\r
342                         \r
343                 }\r
344                 /*** END HTML update's optimisation ***/\r
345                 // end test\r
346                 \r
347         //                      console.log(  (TPS6-TPS5), (TPS5-TPS4), (TPS4-TPS3), (TPS3-TPS2), (TPS2-TPS1), _CPT );\r
348                 // get the new highlight content\r
349                 tpsAfterOpti2           = new Date().getTime();\r
350                 hightlighted_text       = stay_begin + updated_highlight + stay_end;\r
351                 if( !doHtmlOpti )\r
352                 {\r
353                         // update the content of the highlight div by first updating a clone node (as there is no display in the same time for t node it's quite faster (5*))\r
354                         var new_Obj= t.content_highlight.cloneNode(false);\r
355                         if( ( t.isIE && t.isIE < 8 ) || ( t.isOpera && t.isOpera < 9.6 ) )\r
356                                 new_Obj.innerHTML= "<pre><span class='"+ t.settings["syntax"] +"'>" + hightlighted_text + "</span></pre>";      \r
357                         else\r
358                                 new_Obj.innerHTML= "<span class='"+ t.settings["syntax"] +"'>"+ hightlighted_text +"</span>";\r
359         \r
360                         t.content_highlight.parentNode.replaceChild(new_Obj, t.content_highlight);\r
361                 \r
362                         t.content_highlight= new_Obj;\r
363                 }\r
364                 \r
365                 t.last_text_to_highlight= infos["full_text"];\r
366                 t.last_hightlighted_text= hightlighted_text;\r
367                 \r
368                 tps3=new Date().getTime();\r
369         \r
370                 if(t.settings["debug"]){\r
371                         //lineNumber=tab_text.length;\r
372                         //t.debug.value+=" \nNB char: "+_$("src").value.length+" Nb line: "+ lineNumber;\r
373                 \r
374                         t.debug.value= "Tps optimisation "+(tps_end_opti-tps_start)\r
375                                 +" | tps reg exp: "+ (tpsAfterReg-tps_end_opti)\r
376                                 +" | tps opti HTML : "+ (tpsAfterOpti2-tpsAfterReg) + ' '+ ( doHtmlOpti ? 'yes' : 'no' )\r
377                                 +" | tps update highlight content: "+ (tps3-tpsAfterOpti2)\r
378                                 +" | tpsTotal: "+ (tps3-tps_start)\r
379                                 + "("+tps3+")\n"+ debug_opti;\r
380                 //      t.debug.value+= "highlight\n"+hightlighted_text;*/\r
381                 }\r
382                 \r
383         };\r
384         \r
385         EditArea.prototype.resync_highlight= function(reload_now){\r
386                 this.reload_highlight=true;\r
387                 this.last_text_to_highlight="";\r
388                 this.focus();           \r
389                 if(reload_now)\r
390                         this.check_line_selection(false); \r
391         };      \r