27674f675f8d1dbc16eee9d4aeccb02724f48479
[redakcja.git] / project / static / js / edit_area.js
1 /******\r
2  *\r
3  *      EditArea \r
4  *      Developped by Christophe Dolivet\r
5  *      Released under LGPL, Apache and BSD licenses (use the one you want)\r
6  *\r
7 ******/\r
8 \r
9         function EditArea(){\r
10                 var t=this;\r
11                 t.error= false; // to know if load is interrrupt\r
12                 \r
13                 t.inlinePopup= [{popup_id: "area_search_replace", icon_id: "search"},\r
14                                                                         {popup_id: "edit_area_help", icon_id: "help"}];\r
15                 t.plugins= {};\r
16         \r
17                 t.line_number=0;\r
18                 \r
19                 parent.editAreaLoader.set_browser_infos(t);     // navigator identification\r
20                 // fix IE8 detection as we run in IE7 emulate mode through X-UA <meta> tag\r
21                 if( t.isIE >= 8 )\r
22                         t.isIE  = 7;\r
23                 \r
24                 t.last_selection={};            \r
25                 t.last_text_to_highlight="";\r
26                 t.last_hightlighted_text= "";\r
27                 t.syntax_list= [];\r
28                 t.allready_used_syntax= {};\r
29                 t.check_line_selection_timer= 50;       // the timer delay for modification and/or selection change detection\r
30                 \r
31                 t.textareaFocused= false;\r
32                 t.highlight_selection_line= null;\r
33                 t.previous= [];\r
34                 t.next= [];\r
35                 t.last_undo="";\r
36                 t.files= {};\r
37                 t.filesIdAssoc= {};\r
38                 t.curr_file= '';\r
39                 //t.loaded= false;\r
40                 t.assocBracket={};\r
41                 t.revertAssocBracket= {};               \r
42                 // bracket selection init \r
43                 t.assocBracket["("]=")";\r
44                 t.assocBracket["{"]="}";\r
45                 t.assocBracket["["]="]";                \r
46                 for(var index in t.assocBracket){\r
47                         t.revertAssocBracket[t.assocBracket[index]]=index;\r
48                 }\r
49                 t.is_editable= true;\r
50                 \r
51                 \r
52                 /*t.textarea="";        \r
53                 \r
54                 t.state="declare";\r
55                 t.code = []; // store highlight syntax for languagues*/\r
56                 // font datas\r
57                 t.lineHeight= 16;\r
58                 /*t.default_font_family= "monospace";\r
59                 t.default_font_size= 10;*/\r
60                 t.tab_nb_char= 8;       //nb of white spaces corresponding to a tabulation\r
61                 if(t.isOpera)\r
62                         t.tab_nb_char= 6;\r
63 \r
64                 t.is_tabbing= false;\r
65                 \r
66                 t.fullscreen= {'isFull': false};\r
67                 \r
68                 t.isResizing=false;     // resize var\r
69                 \r
70                 // init with settings and ID (area_id is a global var defined by editAreaLoader on iframe creation\r
71                 t.id= area_id;\r
72                 t.settings= editAreas[t.id]["settings"];\r
73                 \r
74                 if((""+t.settings['replace_tab_by_spaces']).match(/^[0-9]+$/))\r
75                 {\r
76                         t.tab_nb_char= t.settings['replace_tab_by_spaces'];\r
77                         t.tabulation="";\r
78                         for(var i=0; i<t.tab_nb_char; i++)\r
79                                 t.tabulation+=" ";\r
80                 }else{\r
81                         t.tabulation="\t";\r
82                 }\r
83                         \r
84                 // retrieve the init parameter for syntax\r
85                 if(t.settings["syntax_selection_allow"] && t.settings["syntax_selection_allow"].length>0)\r
86                         t.syntax_list= t.settings["syntax_selection_allow"].replace(/ /g,"").split(",");\r
87                 \r
88                 if(t.settings['syntax'])\r
89                         t.allready_used_syntax[t.settings['syntax']]=true;\r
90                 \r
91                 \r
92         };\r
93         EditArea.prototype.init= function(){\r
94                 var t=this, a, s=t.settings;\r
95                 t.textarea                      = _$("textarea");\r
96                 t.container                     = _$("container");\r
97                 t.result                        = _$("result");\r
98                 t.content_highlight     = _$("content_highlight");\r
99                 t.selection_field       = _$("selection_field");\r
100                 t.selection_field_text= _$("selection_field_text");\r
101                 t.processing_screen     = _$("processing");\r
102                 t.editor_area           = _$("editor");\r
103                 t.tab_browsing_area     = _$("tab_browsing_area");\r
104                 t.test_font_size        = _$("test_font_size");\r
105                 a = t.textarea;\r
106                 \r
107                 if(!s['is_editable'])\r
108                         t.set_editable(false);\r
109                 \r
110                 t.set_show_line_colors( s['show_line_colors'] );\r
111                 \r
112                 if(syntax_selec= _$("syntax_selection"))\r
113                 {\r
114                         // set up syntax selection lsit in the toolbar\r
115                         for(var i=0; i<t.syntax_list.length; i++) {\r
116                                 var syntax= t.syntax_list[i];\r
117                                 var option= document.createElement("option");\r
118                                 option.value= syntax;\r
119                                 if(syntax==s['syntax'])\r
120                                         option.selected= "selected";\r
121                                 option.innerHTML= t.get_translation("syntax_" + syntax, "word");\r
122                                 syntax_selec.appendChild(option);\r
123                         }\r
124                 }\r
125                 \r
126                 // add plugins buttons in the toolbar\r
127                 spans= parent.getChildren(_$("toolbar_1"), "span", "", "", "all", -1);\r
128                 \r
129                 for(var i=0; i<spans.length; i++){\r
130                 \r
131                         id=spans[i].id.replace(/tmp_tool_(.*)/, "$1");\r
132                         if(id!= spans[i].id){\r
133                                 for(var j in t.plugins){\r
134                                         if(typeof(t.plugins[j].get_control_html)=="function" ){\r
135                                                 html=t.plugins[j].get_control_html(id);\r
136                                                 if(html!=false){\r
137                                                         html= t.get_translation(html, "template");\r
138                                                         var new_span= document.createElement("span");\r
139                                                         new_span.innerHTML= html;                               \r
140                                                         var father= spans[i].parentNode;\r
141                                                         spans[i].parentNode.replaceChild(new_span, spans[i]);   \r
142                                                         break; // exit the for loop                                     \r
143                                                 }\r
144                                         }\r
145                                 }\r
146                         }\r
147                 }\r
148                 \r
149                 // init datas\r
150                 //a.value       = 'a';//editAreas[t.id]["textarea"].value;\r
151         \r
152                 if(s["debug"])\r
153                 {\r
154                         t.debug=parent.document.getElementById("edit_area_debug_"+t.id);\r
155                 }\r
156                 // init size            \r
157                 //this.update_size();\r
158                 \r
159                 if(_$("redo") != null)\r
160                         t.switchClassSticky(_$("redo"), 'editAreaButtonDisabled', true);\r
161                 \r
162                 // insert css rules for highlight mode          \r
163                 if(typeof(parent.editAreaLoader.syntax[s["syntax"]])!="undefined"){\r
164                         for(var i in parent.editAreaLoader.syntax){\r
165                                 if (typeof(parent.editAreaLoader.syntax[i]["styles"]) != "undefined"){\r
166                                         t.add_style(parent.editAreaLoader.syntax[i]["styles"]);\r
167                                 }\r
168                         }\r
169                 }\r
170         \r
171                 // init key events\r
172                 if(t.isOpera)\r
173                         _$("editor").onkeypress = keyDown;\r
174                 else\r
175                         _$("editor").onkeydown  = keyDown;\r
176 \r
177                 for(var i=0; i<t.inlinePopup.length; i++){\r
178                         if(t.isOpera)\r
179                                 _$(t.inlinePopup[i]["popup_id"]).onkeypress     = keyDown;\r
180                         else\r
181                                 _$(t.inlinePopup[i]["popup_id"]).onkeydown      = keyDown;\r
182                 }\r
183                 \r
184                 if(s["allow_resize"]=="both" || s["allow_resize"]=="x" || s["allow_resize"]=="y")\r
185                         t.allow_resize(true);\r
186                 \r
187                 parent.editAreaLoader.toggle(t.id, "on");\r
188                 //a.focus();\r
189                 // line selection init\r
190                 t.change_smooth_selection_mode(editArea.smooth_selection);\r
191                 // highlight\r
192                 t.execCommand("change_highlight", s["start_highlight"]);\r
193         \r
194                 // get font size datas          \r
195                 t.set_font(editArea.settings["font_family"], editArea.settings["font_size"]);\r
196                 \r
197                 // set unselectable text\r
198                 children= parent.getChildren(document.body, "", "selec", "none", "all", -1);\r
199                 for(var i=0; i<children.length; i++){\r
200                         if(t.isIE)\r
201                                 children[i].unselectable = true; // IE\r
202                         else\r
203                                 children[i].onmousedown= function(){return false};\r
204                 /*      children[i].style.MozUserSelect = "none"; // Moz\r
205                         children[i].style.KhtmlUserSelect = "none";  // Konqueror/Safari*/\r
206                 }\r
207                 \r
208                 a.spellcheck= s["gecko_spellcheck"];\r
209         \r
210                 /** Browser specific style fixes **/\r
211                 \r
212                 // fix rendering bug for highlighted lines beginning with no tabs\r
213                 if( t.isFirefox >= '3' ) {\r
214                         t.content_highlight.style.paddingLeft= "1px";\r
215                         t.selection_field.style.paddingLeft= "1px";\r
216                         t.selection_field_text.style.paddingLeft= "1px";\r
217                 }\r
218                 \r
219                 if(t.isIE && t.isIE < 8 ){\r
220                         a.style.marginTop= "-1px";\r
221                 }\r
222                 /*\r
223                 if(t.isOpera){\r
224                         t.editor_area.style.position= "absolute";\r
225                 }*/\r
226                 \r
227                 if( t.isSafari ){\r
228                         t.editor_area.style.position    = "absolute";\r
229                         a.style.marginLeft              ="-3px";\r
230                         if( t.isSafari < 3.2 ) // Safari 3.0 (3.1?)\r
231                                 a.style.marginTop       ="1px";\r
232                 }\r
233                 \r
234                 // si le textarea n'est pas grand, un click sous le textarea doit provoquer un focus sur le textarea\r
235                 parent.editAreaLoader.add_event(t.result, "click", function(e){ if((e.target || e.srcElement)==editArea.result) { editArea.area_select(editArea.textarea.value.length, 0);}  });\r
236                 \r
237                 if(s['is_multi_files']!=false)\r
238                         t.open_file({'id': t.curr_file, 'text': ''});\r
239         \r
240                 t.set_word_wrap( s['word_wrap'] );\r
241                 \r
242                 setTimeout("editArea.focus();editArea.manage_size();editArea.execCommand('EA_load');", 10);             \r
243                 //start checkup routine\r
244                 t.check_undo();\r
245                 t.check_line_selection(true);\r
246                 t.scroll_to_view();\r
247                 \r
248                 for(var i in t.plugins){\r
249                         if(typeof(t.plugins[i].onload)=="function")\r
250                                 t.plugins[i].onload();\r
251                 }\r
252                 if(s['fullscreen']==true)\r
253                         t.toggle_full_screen(true);\r
254         \r
255                 parent.editAreaLoader.add_event(window, "resize", editArea.update_size);\r
256                 parent.editAreaLoader.add_event(parent.window, "resize", editArea.update_size);\r
257                 parent.editAreaLoader.add_event(top.window, "resize", editArea.update_size);\r
258                 parent.editAreaLoader.add_event(window, "unload", function(){\r
259                         // in case where editAreaLoader have been already cleaned\r
260                         if( parent.editAreaLoader )\r
261                         {\r
262                                 parent.editAreaLoader.remove_event(parent.window, "resize", editArea.update_size);\r
263                                 parent.editAreaLoader.remove_event(top.window, "resize", editArea.update_size);\r
264                         }\r
265                         if(editAreas[editArea.id] && editAreas[editArea.id]["displayed"]){\r
266                                 editArea.execCommand("EA_unload");\r
267                         }\r
268                 });\r
269                 \r
270                 \r
271                 /*date= new Date();\r
272                 alert(date.getTime()- parent.editAreaLoader.start_time);*/\r
273         };\r
274         \r
275         \r
276         \r
277         //called by the toggle_on\r
278         EditArea.prototype.update_size= function(){\r
279                 var d=document,pd=parent.document,height,width,popup,maxLeft,maxTop;\r
280                 \r
281                 if( typeof editAreas != 'undefined' && editAreas[editArea.id] && editAreas[editArea.id]["displayed"]==true){\r
282                         if(editArea.fullscreen['isFull']){      \r
283                                 pd.getElementById("frame_"+editArea.id).style.width             = pd.getElementsByTagName("html")[0].clientWidth + "px";\r
284                                 pd.getElementById("frame_"+editArea.id).style.height    = pd.getElementsByTagName("html")[0].clientHeight + "px";\r
285                         }\r
286                         \r
287                         if(editArea.tab_browsing_area.style.display=='block' && ( !editArea.isIE || editArea.isIE >= 8 ) )\r
288                         {\r
289                                 editArea.tab_browsing_area.style.height = "0px";\r
290                                 editArea.tab_browsing_area.style.height = (editArea.result.offsetTop - editArea.tab_browsing_area.offsetTop -1)+"px";\r
291                         }\r
292                         \r
293                         height  = d.body.offsetHeight - editArea.get_all_toolbar_height() - 4;\r
294                         editArea.result.style.height    = height +"px";\r
295                         \r
296                         width   = d.body.offsetWidth -2;\r
297                         editArea.result.style.width             = width+"px";\r
298                         //alert("result h: "+ height+" w: "+width+"\ntoolbar h: "+this.get_all_toolbar_height()+"\nbody_h: "+document.body.offsetHeight);\r
299                         \r
300                         // check that the popups don't get out of the screen\r
301                         for( i=0; i < editArea.inlinePopup.length; i++ )\r
302                         {\r
303                                 popup   = _$(editArea.inlinePopup[i]["popup_id"]);\r
304                                 maxLeft = d.body.offsetWidth - popup.offsetWidth;\r
305                                 maxTop  = d.body.offsetHeight - popup.offsetHeight;\r
306                                 if( popup.offsetTop > maxTop )\r
307                                         popup.style.top         = maxTop+"px";\r
308                                 if( popup.offsetLeft > maxLeft )\r
309                                         popup.style.left        = maxLeft+"px";\r
310                         }\r
311                         \r
312                         editArea.manage_size( true );\r
313                         editArea.fixLinesHeight( editArea.textarea.value, 0,-1);\r
314                 }               \r
315         };\r
316         \r
317         \r
318         EditArea.prototype.manage_size= function(onlyOneTime){\r
319                 if(!editAreas[this.id])\r
320                         return false;\r
321                         \r
322                 if(editAreas[this.id]["displayed"]==true && this.textareaFocused)\r
323                 {\r
324                         var area_height,resized= false;\r
325                         \r
326                         //1) Manage display width\r
327                         //1.1) Calc the new width to use for display\r
328                         if( !this.settings['word_wrap'] )\r
329                         {\r
330                                 var area_width= this.textarea.scrollWidth;\r
331                                 area_height= this.textarea.scrollHeight;\r
332                                 // bug on old opera versions\r
333                                 if(this.isOpera && this.isOpera < 9.6 ){\r
334                                         area_width=10000;                                                               \r
335                                 }\r
336                                 //1.2) the width is not the same, we must resize elements\r
337                                 if(this.textarea.previous_scrollWidth!=area_width)\r
338                                 {       \r
339                                         this.container.style.width= area_width+"px";\r
340                                         this.textarea.style.width= area_width+"px";\r
341                                         this.content_highlight.style.width= area_width+"px";    \r
342                                         this.textarea.previous_scrollWidth=area_width;\r
343                                         resized=true;\r
344                                 }\r
345                         }\r
346                         // manage wrap width\r
347                         if( this.settings['word_wrap'] )\r
348                         {\r
349                                 newW=this.textarea.offsetWidth;\r
350                                 if( this.isFirefox || this.isIE )\r
351                                         newW-=2;\r
352                                 if( this.isSafari )\r
353                                         newW-=6;\r
354                                 this.content_highlight.style.width=this.selection_field_text.style.width=this.selection_field.style.width=this.test_font_size.style.width=newW+"px";\r
355                         }\r
356                         \r
357                         //2) Manage display height\r
358                         //2.1) Calc the new height to use for display\r
359                         if( this.isOpera || this.isFirefox || this.isSafari ) { \r
360                                 area_height= this.getLinePosTop( this.last_selection["nb_line"] + 1 );\r
361                         } else {\r
362                                 area_height = this.textarea.scrollHeight;\r
363                         }       \r
364                         //2.2) the width is not the same, we must resize elements \r
365                         if(this.textarea.previous_scrollHeight!=area_height)    \r
366                         {       \r
367                                 this.container.style.height= (area_height+2)+"px";\r
368                                 this.textarea.style.height= area_height+"px";\r
369                                 this.content_highlight.style.height= area_height+"px";  \r
370                                 this.textarea.previous_scrollHeight= area_height;\r
371                                 resized=true;\r
372                         }\r
373                 \r
374                         //3) if there is new lines, we add new line numbers in the line numeration area\r
375                         if(this.last_selection["nb_line"] >= this.line_number)\r
376                         {\r
377                                 var newLines= '', destDiv=_$("line_number"), start=this.line_number, end=this.last_selection["nb_line"]+100;\r
378                                 for( i = start+1; i < end; i++ )\r
379                                 {\r
380                                         newLines+='<div id="line_'+ i +'">'+i+"</div>";\r
381                                         this.line_number++;\r
382                                 }\r
383                                 destDiv.innerHTML= destDiv.innerHTML + newLines;\r
384                                 \r
385                                 this.fixLinesHeight( this.textarea.value, start, -1 );\r
386                         }\r
387                 \r
388                         //4) be sure the text is well displayed\r
389                         this.textarea.scrollTop="0px";\r
390                         this.textarea.scrollLeft="0px";\r
391                         if(resized==true){\r
392                                 this.scroll_to_view();\r
393                         }\r
394                 }\r
395                 \r
396                 if(!onlyOneTime)\r
397                         setTimeout("editArea.manage_size();", 100);\r
398         };\r
399         \r
400         EditArea.prototype.execCommand= function(cmd, param){\r
401                 \r
402                 for(var i in this.plugins){\r
403                         if(typeof(this.plugins[i].execCommand)=="function"){\r
404                                 if(!this.plugins[i].execCommand(cmd, param))\r
405                                         return;\r
406                         }\r
407                 }\r
408                 switch(cmd){\r
409                         case "save":\r
410                                 if(this.settings["save_callback"].length>0)\r
411                                         eval("parent."+this.settings["save_callback"]+"('"+ this.id +"', editArea.textarea.value);");\r
412                                 break;\r
413                         case "load":\r
414                                 if(this.settings["load_callback"].length>0)\r
415                                         eval("parent."+this.settings["load_callback"]+"('"+ this.id +"');");\r
416                                 break;\r
417                         case "onchange":\r
418                                 if(this.settings["change_callback"].length>0)\r
419                                         eval("parent."+this.settings["change_callback"]+"('"+ this.id +"');");\r
420                                 break;          \r
421                         case "EA_load":\r
422                                 if(this.settings["EA_load_callback"].length>0)\r
423                                         eval("parent."+this.settings["EA_load_callback"]+"('"+ this.id +"');");\r
424                                 break;\r
425                         case "EA_unload":\r
426                                 if(this.settings["EA_unload_callback"].length>0)\r
427                                         eval("parent."+this.settings["EA_unload_callback"]+"('"+ this.id +"');");\r
428                                 break;\r
429                         case "toggle_on":\r
430                                 if(this.settings["EA_toggle_on_callback"].length>0)\r
431                                         eval("parent."+this.settings["EA_toggle_on_callback"]+"('"+ this.id +"');");\r
432                                 break;\r
433                         case "toggle_off":\r
434                                 if(this.settings["EA_toggle_off_callback"].length>0)\r
435                                         eval("parent."+this.settings["EA_toggle_off_callback"]+"('"+ this.id +"');");\r
436                                 break;\r
437                         case "re_sync":\r
438                                 if(!this.do_highlight)\r
439                                         break;\r
440                         case "file_switch_on":\r
441                                 if(this.settings["EA_file_switch_on_callback"].length>0)\r
442                                         eval("parent."+this.settings["EA_file_switch_on_callback"]+"(param);");\r
443                                 break;\r
444                         case "file_switch_off":\r
445                                 if(this.settings["EA_file_switch_off_callback"].length>0)\r
446                                         eval("parent."+this.settings["EA_file_switch_off_callback"]+"(param);");\r
447                                 break;\r
448                         case "file_close":\r
449                                 if(this.settings["EA_file_close_callback"].length>0)\r
450                                         return eval("parent."+this.settings["EA_file_close_callback"]+"(param);");\r
451                                 break;\r
452                         \r
453                         default:\r
454                                 if(typeof(eval("editArea."+cmd))=="function")\r
455                                 {\r
456                                         if(this.settings["debug"])\r
457                                                 eval("editArea."+ cmd +"(param);");\r
458                                         else\r
459                                                 try{eval("editArea."+ cmd +"(param);");}catch(e){};\r
460                                 }\r
461                 }\r
462         };\r
463         \r
464         EditArea.prototype.get_translation= function(word, mode){\r
465                 if(mode=="template")\r
466                         return parent.editAreaLoader.translate(word, this.settings["language"], mode);\r
467                 else\r
468                         return parent.editAreaLoader.get_word_translation(word, this.settings["language"]);\r
469         };\r
470         \r
471         EditArea.prototype.add_plugin= function(plug_name, plug_obj){\r
472                 for(var i=0; i<this.settings["plugins"].length; i++){\r
473                         if(this.settings["plugins"][i]==plug_name){\r
474                                 this.plugins[plug_name]=plug_obj;\r
475                                 plug_obj.baseURL=parent.editAreaLoader.baseURL + "plugins/" + plug_name + "/";\r
476                                 if( typeof(plug_obj.init)=="function" )\r
477                                         plug_obj.init();\r
478                         }\r
479                 }\r
480         };\r
481         \r
482         EditArea.prototype.load_css= function(url){\r
483                 try{\r
484                         link = document.createElement("link");\r
485                         link.type = "text/css";\r
486                         link.rel= "stylesheet";\r
487                         link.media="all";\r
488                         link.href = url;\r
489                         head = document.getElementsByTagName("head");\r
490                         head[0].appendChild(link);\r
491                 }catch(e){\r
492                         document.write("<link href='"+ url +"' rel='stylesheet' type='text/css' />");\r
493                 }\r
494         };\r
495         \r
496         EditArea.prototype.load_script= function(url){\r
497                 try{\r
498                         script = document.createElement("script");\r
499                         script.type = "text/javascript";\r
500                         script.src  = url;\r
501                         script.charset= "UTF-8";\r
502                         head = document.getElementsByTagName("head");\r
503                         head[0].appendChild(script);\r
504                 }catch(e){\r
505                         document.write("<script type='text/javascript' src='" + url + "' charset=\"UTF-8\"><"+"/script>");\r
506                 }\r
507         };\r
508         \r
509         // add plugin translation to language translation array\r
510         EditArea.prototype.add_lang= function(language, values){\r
511                 if(!parent.editAreaLoader.lang[language])\r
512                         parent.editAreaLoader.lang[language]={};\r
513                 for(var i in values)\r
514                         parent.editAreaLoader.lang[language][i]= values[i];\r
515         };\r
516         \r
517         // short cut for document.getElementById()\r
518         function _$(id){return document.getElementById( id );};\r
519 \r
520         var editArea = new EditArea();  \r
521         parent.editAreaLoader.add_event(window, "load", init);\r
522         \r
523         function init(){                \r
524                 setTimeout("editArea.init();  ", 10);\r
525         };\r