PageSourceSearch

https://journalpro.ru/bitrix/js/fileman/light_editor/le_core.js?166871264376724

js journalpro.ru collected 2026-10-02 11:36:07 UTC 76,724 bytes, 2,983 lines download raw bytes

1function JCLightHTMLEditor(arConfig) {this.Init(arConfig);}
2
3JCLightHTMLEditor.items = {};
4
5JCLightHTMLEditor.prototype = {
6Init: function(arConfig)
7{
8	this.id = arConfig.id;
9	JCLightHTMLEditor.items[this.id] = this;
10
11	var _this = this;
12	this.arConfig = arConfig;
13	this.bxTags = {};
14	this.bFocused = false;
15	arConfig.timeoutCount = arConfig.timeoutCount || 0;
16
17	this.bPopup = false;
18	this.buttonsIndex = {};
19	this.parseAlign = true;
20	this.parseTable = true;
21	this.lastCursorId = 'bxed-last-cursor';
22	this.bHandleOnPaste = this.arConfig.bHandleOnPaste !== false;
23
24	this.arBBTags = ['p', 'u', 'div', 'table', 'tr', 'td', 'th', 'img', 'a', 'center', 'left', 'right', 'justify'];
25	this._turnOffCssCount = 0;
26
27	if (this.arConfig.arBBTags)
28		this.arBBTags = this.arBBTags.concat(this.arConfig.arBBTags);
29
30	this.arConfig.width = this.arConfig.width ? parseInt(this.arConfig.width) + (this.arConfig.width.indexOf('%') == -1 ? "px" : '%') : "100%";
31	this.arConfig.height = this.arConfig.height ? parseInt(this.arConfig.height) + (this.arConfig.height.indexOf('%') == -1 ? "px" : '%') : "100%";
32	this.SetConstants();
33	this.sEditorMode = 'html';
34	this.toolbarLineCount = 1;
35
36	console.log('arConfig', arConfig);
37	this.CACHE = {};
38	this.arVideos = {};
39
40	// Set content from config;
41	this.content = this.arConfig.content;
42	this.oSpecialParsers = {};
43	BX.onCustomEvent(window, 'LHE_OnBeforeParsersInit', [this]);
44
45	this.oSpecialParsers.cursor = {
46		Parse: function(sName, sContent, pLEditor)
47		{
48			return sContent.replace(/#BXCURSOR#/ig, '<span id="' + pLEditor.lastCursorId + '"></span>');
49		},
50		UnParse: function(bxTag, pNode, pLEditor)
51		{
52			return '#BXCURSOR#';
53		}
54	};
55
56	if (arConfig.parsers)
57	{
58		for (var p in arConfig.parsers)
59		{
60			if (arConfig.parsers[p])
61				this.oSpecialParsers[p] = arConfig.parsers[p];
62		}
63	}
64
65	this.bDialogOpened = false;
66
67	// Sceleton
68	this.pFrame = BX('bxlhe_frame_' + this.id);
69	if (!this.pFrame)
70	{
71		if (arConfig.timeoutCount < 100)
72		{
73			setTimeout(function()
74			{
75				arConfig.timeoutCount++;
76				_this.Init(arConfig);
77			}, 1);
78		}
79		return;
80	}
81
82	this.pFrame.style.display = "block";
83
84	this.pFrame.style.width = this.arConfig.width;
85	this.pFrame.style.height = this.arConfig.height;
86
87	this.pFrameTable = this.pFrame.firstChild;
88	this.pButtonsCell = this.pFrameTable.rows[0].cells[0];
89	this.pButtonsCont = this.pButtonsCell.firstChild;
90	this.pEditCont = this.pFrameTable.rows[1].cells[0];
91
92	if (this.arConfig.height.indexOf('%') == -1)
93	{
94		var h = parseInt(this.arConfig.height) - this.toolbarLineCount * 27;
95		if (h > 0)
96			this.pEditCont.style.height = h + 'px';
97	}
98
99	// iFrame
100	this.CreateFrame();
101
102	// Textarea
103	this.pSourceDiv = this.pEditCont.appendChild(BX.create("DIV", {props: {className: 'lha-source-div' }}));
104	this.pTextarea = this.pSourceDiv.appendChild(BX.create("TEXTAREA", {props: {className: 'lha-textarea', rows: 25, id: this.arConfig.inputId}}));
105	this.pHiddenInput = this.pFrame.appendChild(BX.create("INPUT", {props: {type: 'hidden', name: this.arConfig.inputName}}));
106
107	this.pTextarea.onfocus = function(){_this.bTextareaFocus = true;};
108	this.pTextarea.onblur = function(){_this.bTextareaFocus = false;};
109
110	this.pTextarea.style.fontFamily = this.arConfig.fontFamily;
111	this.pTextarea.style.fontSize = this.arConfig.fontSize;
112	this.pTextarea.style.fontSize = this.arConfig.lineHeight;
113
114	if (this.pHiddenInput.form)
115	{
116		BX.bind(this.pHiddenInput.form, 'submit', function(){
117			try{
118				_this.SaveContent();
119				_this.pHiddenInput.value = _this.pTextarea.value = _this.pHiddenInput.value.replace(/#BXCURSOR#/ig, '');
120			}
121			catch(e){}
122		});
123	}
124
125	// Sort smiles
126	if (this.arConfig.arSmiles && this.arConfig.arSmiles.length > 0)
127	{
128		this.sortedSmiles = [];
129		var i, l, smile, j, k, arCodes;
130		for (i = 0, l = this.arConfig.arSmiles.length; i < l; i++)
131		{
132			smile = this.arConfig.arSmiles[i];
133			if (!smile['codes'] || smile['codes'] == smile['code'])
134			{
135				this.sortedSmiles.push(smile);
136			}
137			else if(smile['codes'].length > 0)
138			{
139				arCodes = smile['codes'].split(' ');
140				for(j = 0, k = arCodes.length; j < k; j++)
141					this.sortedSmiles.push({name: smile.name, path: smile.path, code: arCodes[j]});
142			}
143		}
144
145		//this.sortedSmiles = BX.clone(this.arConfig.arSmiles);
146		this.sortedSmiles = this.sortedSmiles.sort(function(a, b){return b.code.length - a.code.length;});
147	}
148
149	if (!this.arConfig.bBBCode && this.arConfig.bConvertContentFromBBCodes
149)
150		this.arConfig.bBBCode = true;
151
152	this.bBBCode = this.arConfig.bBBCode;
153	if (this.bBBCode)
154	{
155		if (this.InitBBCode && typeof this.InitBBCode == 'function')
156			this.InitBBCode();
157	}
158
159	this.bBBParseImageSize = this.arConfig.bBBParseImageSize;
160
161	if (this.arConfig.bResizable)
162	{
163		if (this.arConfig.bManualResize)
164		{
165			this.pResizer = BX('bxlhe_resize_' + this.id);
166			/*this.pResizer.style.width = this.arConfig.width;*/
167			this.pResizer.title = BX.message.ResizerTitle;
168
169			if (!this.arConfig.minHeight || parseInt(this.arConfig.minHeight) <= 0)
170				this.arConfig.minHeight = 100;
171			if (!this.arConfig.maxHeight || parseInt(this.arConfig.maxHeight) <= 0)
172				this.arConfig.maxHeight = 2000;
173
174			this.pResizer.unselectable = "on";
175			this.pResizer.ondragstart = function (e){return BX.PreventDefault(e);};
176			this.pResizer.onmousedown = function(){_this.InitResizer(); return false;};
177		}
178
179		if (this.arConfig.bAutoResize)
180		{
181			BX.bind(this.pTextarea, 'keydown', BX.proxy(this.AutoResize, this));
182			BX.addCustomEvent(this, 'onShow', BX.proxy(this.AutoResize, this));
183		}
184	}
185
186	// Add buttons
187	this.AddButtons();
188
189	// Check if ALIGN tags allowed
190	this.parseAlign = !this.arConfig.bBBCode || !!(this.buttonsIndex['Justify'] || this.buttonsIndex['JustifyLeft']);
191	this.parseTable = !this.arConfig.bBBCode || !!this.buttonsIndex['Table'];
192
193	if (!this.parseAlign || !this.parseTable)
194	{
195		var arBBTags = [];
196		for (var k in this.arBBTags)
197		{
198			// Align tags
199			if (!this.parseAlign && (
200				this.arBBTags[k] == 'center' || this.arBBTags[k] ==  'left' ||
201				this.arBBTags[k] ==  'right' || this.arBBTags[k] == 'justify'
202			))
203				continue;
204
205			// Table tags
206			if (!this.parseTable && (
207				this.arBBTags[k] == 'table' || this.arBBTags[k] ==  'tr' ||
208					this.arBBTags[k] ==  'td' || this.arBBTags[k] == 'th'
209				))
210				continue;
211
212			arBBTags.push(this.arBBTags[k]);
213		}
214		this.arBBTags = arBBTags;
215	}
216
217	this.SetContent(this.content);
218	this.SetEditorContent(this.content);
219	this.oTransOverlay = new LHETransOverlay({}, this);
220	// TODO: Fix it
221	//this.oContextMenu = new LHEContextMenu({zIndex: 1000}, this);
222
223	BX.onCustomEvent(window, 'LHE_OnInit', [this, false]);
224
225	// Init events
226	BX.bind(this.pEditorDocument, 'click', BX.proxy(this.OnClick, this));
227	BX.bind(this.pEditorDocument, 'mousedown', BX.proxy(this.OnMousedown, this));
228	//BX.bind(this.pEditorDocument, 'contextmenu', BX.proxy(this.OnContextMenu, this));
229
230	if (this.arConfig.bSaveOnBlur)
231		BX.bind(document, "mousedown", BX.proxy(this.OnDocMousedown, this));
232
233	if (this.arConfig.ctrlEnterHandler && typeof window[this.arConfig.ctrlEnterHandler] == 'function')
234		this.ctrlEnterHandler = window[this.arConfig.ctrlEnterHandler];
235
236	// Android < 4.x
237	if (BX.browser.IsAndroid() && /Android\s[1-3].[0-9]/i.test(navigator.userAgent))
238	{
239		this.arConfig.bSetDefaultCodeView = true;
240	}
241
242	if (this.arConfig.bSetDefaultCodeView)
243	{
244		if (this.sourseBut)
245			this.sourseBut.oBut.handler(this.sourseBut);
246		else
247			this.SetView('code');
248	}
249
250	BX.ready(function(){
251		if (_this.pFrame.offsetWidth == 0 && _this.pFrame.offsetWidth == 0)
252		{
253			_this.onShowInterval = setInterval(function(){
254				if (_this.pFrame.offsetWidth != 0 && _this.pFrame.offsetWidth != 0)
255				{
256					BX.onCustomEvent(_this, 'onShow');
257					clearInterval(_this.onShowInterval);
258				}
259			}, 500);
260		}
261		else
262		{
263			BX.onCustomEvent(_this, 'onShow');
264		}
265	});
266
267	this.adjustBodyInterval = 1000;
268	this._AdjustBodyWidth();
269	BX.removeClass(this.pButtonsCont, "lhe-stat-toolbar-cont-preload"); /**/
270},
271
272CreateFrame: function()
273{
274	if (this.iFrame && this.iFrame.parentNode)
275	{
276		this.pEditCont.removeChild(this.iFrame);
277		this.iFrame = null;
278	}
279
280	this.iFrame = this.pEditCont.appendChild(BX.create("IFRAME", {props: { id: 'LHE_iframe_' + this.id, className: 'lha-iframe', src: "javascript:void(0)", frameborder: 0}}));
281
282	if (this.iFrame.contentDocument && !BX.browser.IsIE())
283		this.pEditorDocument = this.iFrame.contentDocument;
284	else
285		this.pEditorDocument = this.iFrame.contentWindow.document;
286	this.pEditorWindow = this.iFrame.contentWindow;
287},
288
289ReInit: function(content)
290{
291	if (typeof content == 'undefined')
292		content = '';
293	this.SetContent(content);
294	this.CreateFrame();
295	this.SetEditorContent(this.content);
296	this.SetFocus();
297
298	BX.onCustomEvent(window, 'LHE_OnInit', [this, true]);
299},
300
301SetConstants: function()
302{
303	//this.reBlockElements = /^(BR|TITLE|TABLE|SCRIPT|TR|TBODY|P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI)$/i;
304	this.reBlockElements = /^(TITLE|TABLE|SCRIPT|TR|TBODY|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI)$/i;
305	this.oneGif = this.arConfig.oneGif;
306	this.imagePath = this.arConfig.imagePath;
307
308	if (!this.arConfig.fontFamily)
309	{
310		const editorContainer = BX('bxlhe_frame_' + this.id);
311		const styleContainer = editorContainer ? editorContainer : document.body;
312		const primaryFont = BX.Dom.style(styleContainer, '--ui-font-family-primary');
313		const fallbackFont = BX.Dom.style(styleContainer, '--ui-font-family-helvetica');
314
315		const currentFont = BX.Type.isStringFilled(primaryFont) ? primaryFont : fallbackFont;
316		if (BX.Type.isStringFilled(currentFont))
317		{
318			this.arConfig.fontFamily = currentFont;
319		}
320	}
321
322	if (!this.arConfig.fontSize)
323		this.arConfig.fontSize = '14px';
324	if (!this.arConfig.lineHeight)
325		this.arConfig.lineHeight = '16px';
326
327	this.arColors = [
328		'#FF0000', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#FF00FF', '#FFFFFF', '#EBEBEB', '#E1E1E1', '#D7D7D7', '#CCCCCC', '#C2C2C2', '#B7B7B7', '#ACACAC', '#A0A0A0', '#959595',
329		'#EE1D24', '#FFF100', '#00A650', '#00AEEF', '#2F3192', '#ED008C', '#898989', '#7D7D7D', '#707070', '#626262', '#555', '#464646', '#363636', '#262626', '#111', '#000000',
330		'#F7977A', '#FBAD82', '#FDC68C', '#FFF799', '#C6DF9C', '#A4D49D', '#81CA9D', '#7BCDC9', '#6CCFF7', '#7CA6D8', '#8293CA', '#8881BE', '#A286BD', '#BC8CBF', '#F49BC1', '#F5999D',
331		'#F16C4D', '#F68E54', '#FBAF5A', '#FFF467', '#ACD372', '#7DC473', '#39B778', '#16BCB4', '#00BFF3', '#438CCB', '#5573B7', '#5E5CA7', '#855FA8', '#A763A9', '#EF6EA8', '#F16D7E',
332		'#EE1D24', '#F16522', '#F7941D', '#FFF100', '#8FC63D', '#37B44A', '#00A650', '#00A99E', '#00AEEF', '#0072BC', '#0054A5', '#2F3192', '#652C91', '#91278F', '#ED008C', '#EE105A',
333		'#9D0A0F', '#A1410D', '#A36209', '#ABA000', '#588528', '#197B30', '#007236', '#00736A', '#0076A4', '#004A80', '#003370', '#1D1363', '#450E61', '#62055F', '#9E005C', '#9D0039',
334		'#790000', '#7B3000', '#7C4900', '#827A00', '#3E6617', '#045F20', '#005824', '#005951', '#005B7E', '#003562', '#002056', '#0C004B', '#30004A', '#4B0048', '#7A0045', '#7A0026'
335	];
336
337	this.systemCSS = "img.bxed-anchor{background-image: url(" + this.imagePath + "lhe_iconkit.gif)!important; background-position: -260px 0!important; height: 20px!important; width: 20px!important;}\n" +
338		"body{font-family:" + this.arConfig.fontFamily + "; font-size: " + this.arConfig.fontSize + "; line-height:" + this.arConfig.lineHeight + "}\n" +
339		"p{padding:0!important; margin: 0!important;}\n" +
340		"span.bxed-noscript{color: #0000a0!important; padding: 2px!important; font-style:italic!important; font-size: 90%!important;}\n" +
341		"span.bxed-noindex{color: #004000!important; padding: 2px!important; font-style:italic!important; font-size: 90%!important;}\n" +
342		"img.bxed-flash{border: 1px solid #B6B6B8!important; background: url(" + this.imagePath + "flash.gif) #E2DFDA center center no-repeat !important;}\n" +
343		"table{border: 1px solid #B6B6B8!important; border-collapse: collapse;}\n" +
344		"table td{border: 1px solid #B6B6B8!important; padding: 2px 5px;}\n" +
345		"img.bxed-video{border: 1px solid #B6B6B8!important; background-color: #E2DFDA!important; background-image: url(" + this.imagePath + "video.gif); background-position: center center!important; background-repeat:no-repeat!important;}\n" +
346		"img.bxed-hr{padding: 2px!important; width: 100%!important; height: 2px!important;}\n";
347
348	if (this.arConfig.documentCSS)
349		this.systemCSS += "\n" + this.arConfig.documentCSS;
350
351	this.tabNbsp = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"; // &nbsp; x 6
352	this.tabNbspRe1 = new RegExp(String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160), 'ig'); //
353	this.tabNbspRe2 = new RegExp(String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + ' ', 'ig'); //
354},
355
356OnMousedown: function(e)
357{
358	if (!e)
359		e = window.event;
360	this.bFocused = true;
361},
362
363OnClick: function(e)
364{
365	this.bFocused = true;
366	this.CheckBr();
367},
368
369OnDblClick: function(e)
370{
371	return;
372},
373
374OnContextMenu: function(e, pElement)
375{
376	return;
377	var
378		_this = this,
379		oFramePos,
380		x, y;
381	if (!e) e = this.pEditorWindow.event;
382
383	if(e.pageX || e.pageY)
384	{
385		x = e.pageX - this.pEditorDocument.body.scrollLeft;
386		y = e.pageY - this.pEditorDocument.body.scrollTop;
387	}
388	else if(e.clientX || e.clientY)
389	{
390		x = e.clientX;
391		y = e.clientY;
392	}
393
394	oFramePos = this.CACHE['frame_pos'];
395	if (!oFramePos)
396		this.CACHE['frame_pos'] = oFramePos = BX.pos(this.pEditCont);
397
398	x += oFramePos.left;
399	y += oFramePos.top;
400
401	var targ;
402	if (e.target)
403		targ = e.target;
404	else if (e.srcElement)
405		targ = e.srcElement;
406	if (targ.nodeType == 3) // defeat Safari bug
407		targ = targ.parentNode;
408
409	if (!targ || !targ.nodeName)
410		return;
411	var res = this.oContextMenu.Show({oPos: {left : x, top : y}, pElement: targ});
412
413	return BX.PreventDefault(e);
414},
415
416OnKeyDown: function(e)
417{
418	if(!e)
419		e = window.event;
420	BX.onCustomEvent(this, 'OnDocumentKeyDown', [e]);
421
422	var key = e.which || e.keyCode;
423	if (e.ctrlKey && !e.shiftKey && !e.altKey)
424	{
425		// if (!BX.browser.IsIE() && !BX.browser.IsOpera())
426		// {
427		switch (key)
428		{
429			case 66 : // B
430			case 98 : // b
431				this.executeCommand('Bold');
432				return BX.PreventDefault(e);
433			case 105 : // i
434			case 73 : // I
435				this.executeCommand('Italic');
436				return BX.PreventDefault(e);
437			case 117 : // u
438			case 85 : // U
439				this.executeCommand('Underline');
440				return BX.PreventDefault(e);
441			case 81 : // Q - quote
442				if (this.quoteBut)
443				{
444					this.quoteBut.oBut.handler(this.quoteBut);
445					return BX.PreventDefault(e);
446				}
447		}
448		//}
449	}
450
451	if (this.bHandleOnPaste
452		&&
453		(
454			(e.ctrlKey && !e.shiftKey && !e.altKey && e.keyCode == 86) /* Ctrl+V */
455				||
456				(!e.ctrlKey && e.shiftKey && !e.altKey && e.keyCode == 45) /*Shift+Ins*/
457				||
458				(e.metaKey && !e.shiftKey && !e.altKey && e.keyCode == 86) /* Cmd+V */
459			)
460		)
461	{
462		this.OnPaste();
463	}
464
465	// Shift +Del - Deleting code fragment in WYSIWYG
466	if (this.bCodeBut && e.shiftKey && e.keyCode == 46 /* Del*/)
467	{
468		var pSel = this.GetSelectionObject();
469		if (pSel)
470		{
471			if (pSel.className == 'lhe-code')
472			{
473				pSel.parentNode.removeChild(pSel);
474				return BX.PreventDefault(e);
475			}
476			else if(pSel.parentNode)
477			{
478				var pCode = BX.findParent(pSel, {className: 'lhe-code'});
479				if (pCode)
480				{
481					pCode.parentNode.removeChild(pCode);
482					return BX.PreventDefault(e);
483				}
484			}
485		}
486	}
487
488	// Tab
489	if (key == 9 && this.arConfig.bReplaceTabToNbsp)
490	{
491		this.InsertHTML(this.tabNbsp);
492		return BX.PreventDefault(e);
493	}
494
495	if (this.bCodeBut && e.keyCode == 13)
496	{
497		if (BX.browser.IsIE() || BX.browser.IsSafari() || BX.browser.IsChrome())
498		{
499			var pElement = this.GetSelectionObject();
500			if (pElement)
501			{
502				var bFind = false;
503				if (pElement && pElement.nodeName && pElement.nodeName.toLowerCase() == 'pre')
504					bFind = true;
505
506				if (!bFind)
507					bFind = !!BX.findParent(pElement, {tagName: 'pre'});
508
509				if (bFind)
510				{
511					if (BX.browser.IsIE())
512						this.InsertHTML("<br/><img src=\"" + this.oneGif + "\" height=\"20\" width=\"1\"/>");
513					else if (BX.browser.IsSafari() || BX.browser.IsChrome())
514						this.InsertHTML(" \r\n");
515
516					return BX.PreventDefault(e);
517				}
518			}
519		}
520	}
521
522	// Ctrl + Enter
523	if ((e.keyCode == 13 || e.keyCode == 10) && e.ctrlKey && this.ctrlEnterHandler)
524	{
525		this.SaveContent();
526		this.ctrlEnterHandler();
527	}
528
529	if (this.arConfig.bAutoResize && this.arConfig.bResizable)
530	{
531		if (this._resizeTimeout)
532		{
533			clearTimeout(this._resizeTimeout);
534			this._resizeTimeout = null;
535		}
536
537		this._resizeTimeout = setTimeout(BX.proxy(this.AutoResize, this), 200);
538	}
539
540	if (this._CheckBrTimeout)
541	{
542		clearTimeout(this._CheckBrTimeout);
543		this._CheckBrTimeout = null;
544	}
545
546	this._CheckBrTimeout = setTimeout(BX.proxy(this.CheckBr, this), 1000);
547},
548
549OnDocMousedown: function(e)
550{
551	if (this.bFocused)
552	{
553		if (!e)
554			e = window.event;
555
556		var pEl;
557		if (e.target)
558			pEl = e.target;
559		else if (e.srcElement)
560			pEl = e.srcElement;
561		if (pEl.nodeType == 3)
562			pEl = pEl.parentNode;
563
564		if (!this.bPopup && !BX.findParent(pEl, {className: 'bxlhe-frame'}))
565		{
566			this.SaveContent();
567			this.bFocused = false;
568		}
569	}
570},
571
572SetView: function(sType)
573{
574	if (this.sEditorMode == sType)
575		return;
576
577	this.SaveContent();
578	if (sType == 'code')
579	{
580		this.iFrame.style.display = "none";
581		this.pSourceDiv.style.display = "block";
582		this.SetCodeEditorContent(this.GetContent());
583	}
584	else
585	{
586		this.iFrame.style.display = "block";
587		this.pSourceDiv.style.display = "none";
588		this.SetEditorContent(this.GetContent());
589		this.CheckBr();
590	}
591	this.sEditorMode = sType;
592	BX.onCustomEvent(this, "OnChangeView");
593},
594
595SaveContent: function()
596{
597	var sContent = this.sEditorMode == 'code' ? this.GetCodeEditorContent() : this.GetEditorContent();
598	if (this.bBBCode)
599		sContent = this.OptimizeBB(sContent);
600
601	this.SetContent(sContent);
602
603	BX.onCustomEvent(this, 'OnSaveContent', [sContent]);
604},
605
606SetContent: function(sContent)
607{
608	this.pHiddenInput.value = this.pTextarea.value = this.content = sContent;
609},
610
611GetContent: function()
612{
613	return this.content.toString();
614},
615
616SetEditorContent: function(sContent)
617{
618	if (this.pEditorDocument)
619	{
620		sContent = this.ParseContent(sContent);
621
622		if (this.pEditorDocument.designMode)
623		{
624			try{
625				this.pEditorDocument.designMode = 'off';
626			}catch(e){alert('SetEditorContent: designMode=\'off\'');}
627		}
628
629		this.pEditorDocument.open();
630		this.pEditorDocument.write('<html><head></head><body>' + sContent + '</body></html>');
631		this.pEditorDocument.close();
632
633		this.pEditorDocument.body.style.padding = "8px";
634		this.pEditorDocument.body.style.margin = "0";
635		this.pEditorDocument.body.style.borderWidth = "0";
636
637		this.pEditorDocument.body.style.fontFamily = this.arConfig.fontFamily;
638		this.pEditorDocument.body.style.fontSize = this.arConfig.fontSize;
639		this.pEditorDocument.body.style.lineHeight = this.arConfig.lineHeight;
640
641		// Set events
642		BX.bind(this.pEditorDocument, 'keydown', BX.proxy(this.OnKeyDown, this));
643
644		if(BX.browser.IsIE())
645		{
646			if (this.bHandleOnPaste)
647				BX.bind(this.pEditorDocument.body, 'paste', BX.proxy(this.OnPaste, this));
648			this.pEditorDocument.body.contentEditable = true;
649		}
650		else if (this.pEditorDocument.designMode)
651		{
652			this.pEditorDocument.designMode = "on";
653			this._TurnOffStyleWithCSS(true);
654		}
655
656		if (this.arConfig.bConvertContentFromBBCodes)
657			this.ShutdownBBCode();
658	}
659},
660
661_TurnOffStyleWithCSS: function(bTimeout)
662{
663	try{
664		this._turnOffCssCount++;
665		if (this._turnOffCssCount < 5 && bTimeout !== false)
666			bTimeout = true;
667
668		this.pEditorDocument.execCommand("styleWithCSS", false, false);
669		try{this.pEditorDocument.execCommand("useCSS", false, true);}catch(e){}
670	}
671	catch(e)
672	{
673		if (bTimeout === true)
674			setTimeout(BX.proxy(this._TurnOffStyleWithCSS, this), 500);
675	}
676},
677
678_AdjustBodyWidth: function()
679{
680	if (!BX.browser.IsChrome())
681	{
682		if (this.pEditorDocument && this.pEditorDocument.body)
683		{
684			var html = this.pEditorDocument.body.innerHTML;
685			if (html != this.lastEditedBodyHtml)
686			{
687				this.adjustBodyInterval = 500;
688				var _this = this;
689				this.pEditorDocument.body.style.width = null;
690				this.lastEditedBodyHtml = html;
691				setTimeout(function(){
692					var scrollWidth = BX.GetWindowScrollSize(_this.pEditorDocument).scrollWidth - 16;
693					if (scrollWidth > 0)
694						_this.pEditorDocument.body.style.width = scrollWidth + 'px';
695				}, 50);
696			}
697			else
698			{
699				this.adjustBodyInterval = 5000;
700			}
701		}
702
703		setTimeout(BX.proxy(this._AdjustBodyWidth, this), this.adjustBodyInterval)
704	}
705},
706
707GetEditorContent: function()
708{
709	var sContent = this.UnParseContent();
710	return sContent;
711},
712
713SetCodeEditorContent: function(sContent)
714{
715	this.pHiddenInput.value = this.pTextarea.value = sContent;
716},
717
718GetCodeEditorContent: function()
719{
720	return this.pTextarea.value;
721},
722
723OptimizeHTML: function(str)
724{
725	var
726		iter = 0,
727		bReplasing = true,
728		arTags = ['b', 'em', 'font', 'h\\d', 'i', 'li', 'ol', 'p', 'small', 'span', 'strong', 'u', 'ul'],
729		replaceEmptyTags = function(){i--; bReplasing = true; return ' ';},
730		re, tagName, i, l;
731
732	while(iter++ < 20 && bReplasing)
733	{
734		bReplasing = false;
735		for (i = 0, l = arTags.length; i < l; i++)
736		{
737			tagName = arTags[i];
738			re = new RegExp('<'+tagName+'[^>]*?>\\s*?</'+tagName+'>', 'ig');
739			str = str.replace(re, replaceEmptyTags);
740
741			re = new RegExp('<' + tagName + '\\s+?[^>]*?/>', 'ig');
742			str = str.replace(re, replaceEmptyTags);
743
744			// Replace <b>text1</b>    <b>text2</b> ===>>  <b>text1 text2</b>
745			re = new RegExp('<((' + tagName + '+?)(?:\\s+?[^>]*?)?)>([\\s\\S]+?)<\\/\\2>\\s*?<\\1>([\\s\\S]+?)<\\/\\2>', 'ig');
746			str = str.replace(re, function(str, b1, b2, b3, b4)
747				{
748					bReplasing = true;
749					return '<' + b1 + '>' + b3 + ' ' + b4 + '</' + b2 + '>';
750				}
751			);
752		}
753	}
754	return str;
755},
756
757_RecursiveDomWalker: function(pNode, pParentNode)
758{
759	var oNode =
760	{
761		arAttributes : {},
762		arNodes : [],
763		type : null,
764		text : "",
765		arStyle : {}
766	};
767
768	switch(pNode.nodeType)
769	{
770		case 9:
771			oNode.type = 'document';
772			break;
773		case 1:
774			if(pNode.tagName.length <= 0 || pNode.tagName.substring(0, 1) == "/")
775				return;
776
777			oNode.text = pNode.tagName.toLowerCase();
778			if (oNode.text == 'script')
779				break;
780
781			oNode.type = 'element';
782			var
783				attr = pNode.attributes,
784				j, l = attr.length;
785
786			if (pNode.nodeName.toLowerCase() == 'a' && pNode.innerHTML == '' && (this.bBBCode || !pNode.getAttribute("name")))
787				return;
788
789			for(j = 0; j < l; j++)
790			{
791				if(attr[j].specified || (oNode.text == "input" && attr[j].nodeName.toLowerCase()=="value"))
792				{
793					var attrName = attr[j].nodeName.toLowerCase();
794
795					if(attrName == "style")
796					{
797						oNode.arAttributes[attrName] = pNode.style.cssText;
798						oNode.arStyle = pNode.style;
799
800						if(oNode.arStyle.display == 'none')
801						{
802							oNode.type = 'text';
803							oNode.text = '';
804							break;
805						}
806
807						if(oNode.arStyle.textAlign && (oNode.text == 'div' || oNode.text == 'p' || oNode.text == 'span'))
808						{
809							var align = oNode.arStyle.textAlign;
810							BX.util.in_array(oNode.arStyle.textAlign, ['left', 'right', 'center', 'justify'])
811							{
812								oNode.arStyle = {};
813								oNode.text = 'span';
814								oNode.arAttributes['style'] = 'text-align:' + align + ';display:block;';
815								oNode.arStyle.textAlign = align;
816								oNode.arStyle.display = 'block';
817							}
818						}
819					}
820					else if(attrName=="src" || attrName=="href"  || attrName=="width"  || attrName=="height")
821					{
822						oNode.arAttributes[attrName] = pNode.getAttribute(attrName, 2);
823					}
824					else if(!this.bBBCode && attrName == 'align' && BX.util.in_array(attr[j].nodeValue, ['left', 'right', 'center', 'justify']))
825					{
826						oNode.text = 'span';
827						oNode.arAttributes['style'] = 'text-align:' + attr[j].nodeValue + ';display:block;';
828						oNode.arStyle.textAlign = attr[j].nodeValue;
829						oNode.arStyle.display = 'block';
830					}
831					else
832					{
833						oNode.arAttributes[attrName] = attr[j].nodeValue;
834					}
835				}
836			}
837			break;
838		case 3:
839			oNode.type = 'text';
840			var res = pNode.nodeValue;
841
842			if (this.arConfig.bReplaceTabToNbsp)
843			{
844				res = res.replace(this.tabNbspRe1, "\t");
845				res = res.replace(this.tabNbspRe2, "\t");
846			}
847
848			if(!pParentNode || (pParentNode.text != 'pre' && pParentNode.arAttributes['class'] != 'lhe-code'))
849			{
850				res = res.replace(/\n+/g, ' ');
851				res = res.replace(/ +/g, ' ');
852			}
853
854			oNode.text = res;
855			break;
856	}
857
858	if (oNode.type != 'text')
859	{
860		var
861			arChilds = pNode.childNodes,
862			i, l = arChilds.length;
863
864		for(i = 0; i < l; i++)
865			oNode.arNodes.push(this._RecursiveDomWalker(arChilds[i], oNode));
866	}
867
868	return oNode;
869},
870
871_RecursiveGetHTML: function(pNode)
872{
873	if (!pNode || typeof pNode != 'object' || !pNode.arAttributes)
874		return "";
875
876	var ob, res = "", id = pNode.arAttributes["id"];
877
878	if (pNode.text == 'img' && !id) // Images pasted by Ctrl+V
879		id = this.SetBxTag(false, {tag: 'img', params: {src: pNode.arAttributes["src"]}});
880
881	if (id)
882	{
883		var bxTag = this.GetBxTag(id);
884		if(bxTag.tag)
885		{
886			var parser = this.oSpecialParsers[bxTag.tag];
887			if (parser && parser.UnParse)
888				return parser.UnParse(bxTag, pNode, this);
889			else if (bxTag.params && bxTag.params.value)
890				return '\n' + bxTag.params.value + '\n';
891			else
892				return '';
893		}
894	}
895
896	if (pNode.arAttributes["_moz_editor_bogus_node"])
897		return '';
898
899	if (this.bBBCode)
900	{
901		var bbRes = this.UnParseNodeBB(pNode);
902		if (bbRes !== false)
903			return bbRes;
904	}
905
906	bFormatted = true;
907
908	if (pNode.text.toLowerCase() != 'body')
909		res = this.GetNodeHTMLLeft(pNode);
910
911	var bNewLine = false;
912
913	var sIndent = '';
914	if (typeof pNode.bFormatted != 'undefined')
915		bFormatted = !!pNode.bFormatted;
916
917	if (bFormatted && pNode.type != 'text')
918	{
919		if (this.reBlockElements.test(pNode.text) && !(pNode.oParent && pNode.oParent.text && pNode.oParent.text.toLowerCase() == 'pre'))
920		{
921			for (var j = 0; j < pNode.iLevel - 3; j++)
922				sIndent += "  ";
923			bNewLine = true;
924			res = "\r\n" + sIndent + res;
925		}
926	}
927
928	for (var i = 0; i < pNode.arNodes.length; i++)
929		res += this._RecursiveGetHTML(pNode.arNodes[i]);
930
931	if (pNode.text.toLowerCase() != 'body')
932		res += this.GetNodeHTMLRight(pNode);
933
934	if (bNewLine)
935		res += "\r\n" + (sIndent == '' ? '' : sIndent.substr(2));
936
937	return res;
938},
939
940// Redeclared in BBCode mode
941GetNodeHTMLLeft: function(pNode)
942{
943	if(pNode.type == 'text')
944		return BX.util.htmlspecialchars(pNode.text);
945
946	var atrVal, attrName, res;
947
948	if(pNode.type == 'element')
949	{
950		res = "<" + pNode.text;
951
952		for(attrName in pNode.arAttributes)
953		{
954			atrVal = pNode.arAttributes[attrName];
955			if(attrName.substring(0,4).toLowerCase() == '_moz')
956				continue;
957
958			if(pNode.text.toUpperCase()=='BR' && attrName.toLowerCase() == 'type' && atrVal == '_moz')
959				continue;
960
961			if(attrName == 'style')
962			{
963				if (atrVal.length > 0 && atrVal.indexOf('-moz') != -1) // Kill -moz* styles from firefox
964					atrVal = BX.util.trim(atrVal.replace(/-moz.*?;/ig, ''));
965
966				if (pNode.text == 'td') // Kill border-image: none; styles from firefox for <td>
967					atrVal = BX.util.trim(atrVal.replace(/border-image:\s*none;/ig, ''));
968
969				if(atrVal.length <= 0)
970					continue;
971			}
972
973			res += ' ' + attrName + '="' + (pNode.bDontUseSpecialchars ? atrVal : BX.util.htmlspecialchars(atrVal)) + '"';
974		}
975
976		if(pNode.arNodes.length <= 0 && !this.IsPairNode(pNode.text))
977			return res + " />";
978		return res + ">";
979	}
980	return "";
981},
982
983// Redeclared in BBCode mode
984GetNodeHTMLRight: function(pNode)
985{
986	if(pNode.type == 'element' && (pNode.arNodes.length>0 || this.IsPairNode(pNode.text)))
987		return "</" + pNode.text + ">";
988	return "";
989},
990
991IsPairNode: function(text)
992{
993	if(text.substr(0, 1) == 'h' || text == 'br' || text == 'img' || text == 'input')
994		return false;
995	return true;
996},
997
998executeCommand: function(commandName, sValue)
999{
1000	this.SetFocus();
1001	//try{
1002	var res = this.pEditorWindow.document.execCommand(commandName, false, sValue);
1003	//}catch(e){};
1004	this.SetFocus();
1005	//this.OnEvent("OnSelectionChange");
1006	//this.OnChange("executeCommand", commandName);
1007
1008	if (this.arConfig.bAutoResize && this.arConfig.bResizable)
1009		this.AutoResize();
1010
1011	return res;
1012},
1013
1014queryCommand: function(commandName)
1015{
1016	var sValue = '';
1017	if (!this.pEditorDocument.queryCommandEnabled || !this.pEditorDocument.queryCommandValue)
1018		return null;
1019
1020	if(!this.pEditorDocument.queryCommandEnabled(commandName))
1021		return null;
1022
1023	return this.pEditorDocument.queryCommandValue(commandName);
1024},
1025
1026SetFocus: function()
1027{
1028	if (this.sEditorMode != 'html')
1029		return;
1030
1031	BX.focus(this.pEditorWindow.focus ? this.pEditorWindow : this.pEditorDocument.body);
1032	this.bFocused = true;
1033},
1034
1035SetFocusToEnd: function()
1036{
1037	this.CheckBr();
1038	var ss = BX.GetWindowScrollSize(this.pEditorDocument);
1039	this.pEditorWindow.scrollTo(0, ss.scrollHeight);
1040
1041	this.SetFocus();
1042	this.SelectElement(this.pEditorDocument.body.lastChild);
1043},
1044
1045SetCursorFF: function()
1046{
1047	if (this.sEditorMode != 'code' && !BX.browser.IsIE())
1048	{
1049		var _this = this;
1050		try{
1051			this.iFrame.blur();
1052			this.iFrame.focus();
1053
1054			setTimeout(function(){
1055				_this.iFrame.blur();
1056				_this.iFrame.focus();
1057			}, 600);
1058
1059			setTimeout(function(){
1060				_this.iFrame.blur();
1061				_this.iFrame.focus();
1062			}, 1000);
1063		}catch(e){}
1064	}
1065},
1066
1067CheckBr: function()
1068{
1069	if (this.CheckBrTimeout)
1070	{
1071		clearTimeout(this.CheckBrTimeout);
1072		this.CheckBrTimeout = false;
1073	}
1074
1075	var _this = this;
1076	this.CheckBrTimeout = setTimeout(function()
1077	{
1078		var lastChild = _this.pEditorDocument.body.lastChild;
1079		if (lastChild && lastChild.nodeType == 1)
1080		{
1081			var nn = lastChild.nodeName.toUpperCase();
1082			var reBlockElements = /^(TITLE|TABLE|SCRIPT|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|BLOCKQUOTE|FORM|CENTER|)$/i;
1083			if (reBlockElements.test(nn))
1084				_this.pEditorDocument.body.appendChild(_this.pEditorDocument.createElement("BR"));
1085		}
1086	}, 200);
1087},
1088
1089ParseContent: function(sContent, bJustParse) // HTML -> WYSIWYG
1090{
1091	var _this = this;
1092	var arCodes = [];
1093	sContent = sContent.replace(/\[code\]((?:\s|\S)*?)\[\/code\]/ig, function(str, code)
1094	{
1095		var strId = '';
1096		if (!_this.bBBCode)
1097			strId = " id=\"" + _this.SetBxTag(false, {tag: "code"}) + "\" ";
1098
1099		arCodes.push('<pre ' + strId + 'class="lhe-code" title="' + BX.message.CodeDel + '">' + BX.util.htmlspecialchars(code) + '</pre>');
1100		return '#BX_CODE' + (arCodes.length - 1) + '#';
1101	});
1102
1103	if (!bJustParse)
1104		BX.onCustomEvent(this, 'OnParseContent');
1105
1106	if (this.arConfig.bBBCode)
1107		sContent = this.ParseBB(sContent);
1108
1109	sContent = sContent.replace(/(<td[^>]*>)\s*(<\/td>)/ig, "$1<br _moz_editor_bogus_node=\"on\">$2");
1110
1111	if (this.arConfig.bReplaceTabToNbsp)
1112		sContent = sContent.replace(/\t/ig, this.tabNbsp);
1113
1114	if (!BX.browser.IsIE())
1115	{
1116		sContent = sContent.replace(/<hr[^>]*>/ig, function(sContent)
1117			{
1118				return '<img class="bxed-hr" src="' + _this.imagePath + 'break_page.gif" id="' + _this.SetBxTag(false, {tag: "hr", params: {value : sContent}}) + '"/>';
1119			}
1120		);
1121	}
1122
1123	for (var p in this.oSpecialParsers)
1124	{
1125		if (this.oSpecialParsers[p] && this.oSpecialParsers[p].Parse)
1126			sContent = this.oSpecialParsers[p].Parse(p, sContent, this);
1127	}
1128
1129	if (!bJustParse)
1130		setTimeout(function(){
1131			_this.AppendCSS(_this.systemCSS);
1132			// Hack for chrome: we have to unset font family
1133			// because than user paste text - chrome wraps it with [FONT=.....
1134			setTimeout(function(){
1135				_this.pEditorDocument.body.style.fontFamily = '';
1136				_this.pEditorDocument.body.style.fontSize = '';
1137			}, 1);
1138		}, 300);
1139
1140	if (arCodes.length > 0) // Replace back CODE content without modifications
1141		sContent = sContent.replace(/#BX_CODE(\d+)#/ig, function(s, num){return arCodes[num] || s;});
1142
1143	if (this.bBBCode)
1144	{
1145		sContent = sContent.replace(/&amp;#91;/ig, "[");
1146		sContent = sContent.replace(/&amp;#93;/ig, "]");
1147	}
1148
1149	sContent = BX.util.trim(sContent);
1150
1151	// Add <br> in the end of the message if text not ends with <br>
1152	if (this.arConfig.bBBCode && !sContent.match(/(<br[^>]*>)$/ig))
1153		sContent += '<br/>';
1154
1155	return sContent;
1156},
1157
1158UnParseContent: function() // WYSIWYG - > html
1159{
1160	BX.onCustomEvent(this, 'OnUnParseContent');
1161
1162	var sContent = this._RecursiveGetHTML(this._RecursiveDomWalker(this.pEditorDocument.body, false));
1163
1164	if (this.bBBCode)
1165	{
1166		if (!BX.browser.IsIE())
1167			sContent = sContent.replace(/\r/ig, '');
1168		sContent = sContent.replace(/\n/ig, '');
1169	}
1170
1171	var arDivRules = [
1172		['#BR#(#TAG_BEGIN#)', "$1"], // 111<br><div>... => 111<>
1173		['(#TAG_BEGIN#)(?:#BR#)*?(#TAG_END#)', "$1$2"], // [DIV]#BR#[/DIV]  ==> [DIV][/DIV]
1174		['(#TAG_BEGIN#)([\\s\\S]*?)#TAG_END#(?:\\n|\\r|\\s)*?#TAG_BEGIN#([\\s\\S]*?)(#TAG_END#)', function(str, s1, s2,s3,s4){return s1 + s2 + '#BR#' + s3 + s4;}, true],
1175		['^#TAG_BEGIN#', ""], //kill [DIV] in the begining of the text
1176		['([\\s\\S]*?(\\[\\/\\w+\\])*?)#TAG_BEGIN#([\\s\\S]*?)#TAG_END#([\\s\\S]*?)', function(str, s1, s2,s3,s4)
1177		{
1178			if (s2 && s2.toLowerCase && s2.toLowerCase() == '[/list]')
1179				return s1 + s3 + '#BR#' + s4;
1180			return s1 + '#BR#' + s3 + '#BR#' + s4;
1181		}, true], // [/list][DIV]wwww[/div]wwww => [/list]wwww#BR#wwwww, text[DIV]wwww[/div]wwww => text#BR#www#BR#
1182		['#TAG_END#', "#BR#"] // [/DIV] ==> \n
1183	];
1184
1185	var re, i, l = arDivRules.length, str;
1186	if (this.bBBCode)
1187	{
1188		//
1189		if (BX.browser.IsOpera())
1190			sContent = sContent.replace(/(?:#BR#)*?\[\/P\]/ig, "[/P]"); // #BR#[/P]  ==> [/P] for opera
1191
1192		for (i = 0; i < l; i++)
1193		{
1194			re = arDivRules[i][0];
1195			re = re.replace(/#TAG_BEGIN#/g, '\\[P\\]');
1196			re = re.replace(/#TAG_END#/g, '\\[\\/P\\]');
1197			re = re.replace(/\\\\/ig, '\\\\');
1198			re = new RegExp(re, 'igm');
1199			if (arDivRules[i][2] === true)
1200				while(true)
1201				{
1202					str = sContent.replace(re, arDivRules[i][1]);
1203					if (str == sContent)
1204						break;
1205					else
1206						sContent = str;
1207				}
1208			else
1209				sContent = sContent.replace(re, arDivRules[i][1]);
1210		}
1211		sContent = sContent.replace(/^((?:\s|\S)*?)(?:\n|\r|\s)+$/ig, "$1\n\n"); //kill multiple \n in the end
1212
1213		// Handle  [DIV] tags from safari, chrome
1214		for (i = 0; i < l; i++)
1215		{
1216			re = arDivRules[i][0];
1217			re = re.replace(/#TAG_BEGIN#/g, '\\[DIV\\]');
1218			re = re.replace(/#TAG_END#/g, '\\[\\/DIV\\]');
1219			re = re.replace(/\\\\/ig, '\\\\');
1220
1221			if (arDivRules[i][2] === true)
1222				while(true)
1223				{
1224					str = sContent.replace(new RegExp(re, 'igm'), arDivRules[i][1]);
1225					if (str == sContent)
1226						break;
1227					else
1228						sContent = str;
1229				}
1230			else
1231				sContent = sContent.replace(new RegExp(re, 'igm'), arDivRules[i][1]);
1232		}
1233
1234		sContent = sContent.replace(/#BR#/ig, "\n");
1235		sContent = sContent.replace(/\[DIV]/ig, "");
1236		sContent = BX.util.htmlspecialcharsback(sContent);
1237	}
1238
1239	this.__sContent = sContent;
1240	BX.onCustomEvent(this, 'OnUnParseContentAfter');
1241	sContent = this.__sContent;
1242	return sContent;
1243},
1244
1245InitResizer: function()
1246{
1247	this.oTransOverlay.Show();
1248
1249	var
1250		_this = this,
1251		coreContPos = BX.pos(this.pFrame),
1252		newHeight = false;
1253
1254	var MouseMove = function(e)
1255	{
1256		e = e || window.event;
1257		BX.fixEventPageY(e);
1258		newHeight = e.pageY - coreContPos.top;
1259
1260		// New height
1261		if (newHeight < _this.arConfig.minHeight)
1262		{
1263			newHeight = _this.arConfig.minHeight;
1264			document.body.style.cursor = "not-allowed";
1265		}
1266		else if (newHeight > _this.arConfig.maxHeight)
1267		{
1268			newHeight = _this.arConfig.maxHeight;
1269			document.body.style.cursor = "not-allowed";
1270		}
1271		else
1272		{
1273			document.body.style.cursor = "n-resize";
1274		}
1275
1276		_this.pFrame.style.height = newHeight + "px";
1277		_this.ResizeFrame(newHeight);
1278	};
1279
1280	var MouseUp = function(e)
1281	{
1282		if (_this.arConfig.autoResizeSaveSize)
1283			BX.userOptions.save('fileman', 'LHESize_' + _this.id, 'height', newHeight);
1284		_this.arConfig.height = newHeight;
1285
1286		document.body.style.cursor = "";
1287		if (_this.oTransOverlay && _this.oTransOverlay.bShowed)
1288			_this.oTransOverlay.Hide();
1289
1290		BX.unbind(document, "mousemove", MouseMove);
1291		BX.unbind(document, "mouseup", MouseUp);
1292	};
1293
1294	BX.bind(document, "mousemove", MouseMove);
1295	BX.bind(document, "mouseup", MouseUp);
1296},
1297
1298AutoResize: function()
1299{
1300	var
1301		heightOffset = parseInt(this.arConfig.autoResizeOffset || 80),
1302		maxHeight = parseInt(this.arConfig.autoResizeMaxHeight || 0),
1303		minHeight = parseInt(this.arConfig.autoResizeMinHeight || 50),
1304		newHeight,
1305		_this = this;
1306
1307	if (this.autoResizeTimeout)
1308		clearTimeout(this.autoResizeTimeout);
1309
1310	this.autoResizeTimeout = setTimeout(function()
1311	{
1312		if (_this.sEditorMode == 'html')
1313		{
1314			//newHeight = _this.pEditorDocument.body.offsetHeight + heightOffset;
1315			newHeight = _this.pEditorDocument.body.offsetHeight;
1316			var
1317				body = _this.pEditorDocument.body,
1318				node = body.lastChild,
1319				offsetTop = false, i;
1320
1321			while (true)
1322			{
1323				if (!node)
1324					break;
1325				if (node.offsetTop)
1326				{
1327					offsetTop = node.offsetTop + (node.offsetHeight || 0);
1328					newHeight = offsetTop + heightOffset;
1329					break;
1330				}
1331				else
1332				{
1333					node = node.previousSibling;
1334				}
1335			}
1336
1337			var oEdSize = BX.GetWindowSize(_this.pEditorDocument);
1338			if (oEdSize.scrollHeight - oEdSize.innerHeight > 5)
1339				newHeight = Math.max(oEdSize.scrollHeight + heightOffset, newHeight);
1340		}
1341		else
1342		{
1343			newHeight = (_this.pTextarea.value.split("\n").length /* rows count*/ + 5) * 17;
1344		}
1345
1346		if (newHeight > parseInt(_this.arConfig.height))
1347		{
1348			if (BX.browser.IsIOS())
1349				maxHeight = Infinity;
1350			else if (!maxHeight || maxHeight < 10)
1351				maxHeight = Math.round(BX.GetWindowInnerSize().innerHeight * 0.9); // 90% from screen height
1352
1353			newHeight = Math.min(newHeight, maxHeight);
1354			newHeight = Math.max(newHeight, minHeight);
1355
1356			_this.SmoothResizeFrame(newHeight);
1357		}
1358	}, 300);
1359},
1360
1361MousePos: function (e)
1362{
1363	if(window.event)
1364		e = window.event;
1365
1366	if(e.pageX || e.pageY)
1367	{
1368		e.realX = e.pageX;
1369		e.realY = e.pageY;
1370	}
1371	else if(e.clientX || e.clientY)
1372	{
1373		e.realX = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
1374		e.realY = e.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop;
1375	}
1376	return e;
1377},
1378
1379SmoothResizeFrame: function(height)
1380{
1381	var
1382		_this = this,
1383		curHeight = parseInt(this.pFrame.offsetHeight),
1384		count = 0,
1385		bRise = height > curHeight,
1386		timeInt = BX.browser.IsIE() ? 50 : 50,
1387		dy = 5;
1388
1389	if (!bRise)
1390		return;
1391
1392	if (this.smoothResizeInterval)
1393		clearInterval(this.smoothResizeInterval);
1394
1395	this.smoothResizeInterval = setInterval(function()
1396		{
1397			if (bRise)
1398			{
1399				curHeight += Math.round(dy * count);
1400				if (curHeight > height)
1401				{
1402					clearInterval(_this.smoothResizeInterval);
1403					if (curHeight > height)
1404						curHeight = height;
1405				}
1406			}
1407			else
1408			{
1409				curHeight -= Math.round(dy * count);
1410				if (curHeight < height)
1411				{
1412					curHeight = height;
1413					clearInterval(_this.smoothResizeInterval);
1414				}
1415			}
1416
1417			_this.pFrame.style.height = curHeight + "px";
1418			_this.ResizeFrame(curHeight);
1419			count++;
1420		},
1421		timeInt
1422	);
1423},
1424
1425ResizeFrame: function(newHeight)
1426{
1427	var
1428		deltaWidth = 7,
1429		resizeHeight = this.arConfig.bManualResize ? 3 : 0, // resize row
1430		height = newHeight || parseInt(this.pFrame.offsetHeight),
1431		width = this.pFrame.offsetWidth;
1432
1433	this.pFrameTable.style.height = height + 'px';
1434	var contHeight = height - this.buttonsHeight - resizeHeight;
1435
1436	if (contHeight > 0)
1437	{
1438		this.pEditCont.style.height = contHeight + 'px';
1439		this.pTextarea.style.height = contHeight + 'px';
1440	}
1441
1442	this.pTextarea.style.width = (width > deltaWidth) ? (width - deltaWidth) + 'px' : 'auto';
1443	this.pButtonsCell.style.height = this.buttonsHeight + 'px';
1444
1445	/*if (this.arConfig.bResizable)
1446	 this.pResizer.parentNode.style.height = resizeHeight + 'px';*/
1447},
1448
1449AddButtons: function()
1450{
1451	var
1452		i, l, butId, grInd, arButtons,
1453		toolbarConfig = this.arConfig.toolbarConfig;
1454	this.buttonsCount = 0;
1455
1456	if(!toolbarConfig)
1457		toolbarConfig = [
1458			//'Source',
1459			'Bold', 'Italic', 'Underline', 'Strike', 'RemoveFormat', 'InsertHR',
1460			'Anchor',
1461			'CreateLink', 'DeleteLink', 'Image', //'SpecialChar',
1462			'Justify',
1463			'InsertOrderedList', 'InsertUnorderedList', 'Outdent', 'Indent',
1464			'BackColor', 'ForeColor',
1465			'Video',
1466			'StyleList', 'HeaderList',
1467			'FontList', 'FontSizeList',
1468			'Table'
1469			//smiles:['SmileList']
1470		];
1471
1472	if (oBXLEditorUtils.oTune && oBXLEditorUtils.oTune[this.id])
1473	{
1474		var
1475			ripButtons = oBXLEditorUtils.oTune[this.id].ripButtons,
1476			addButtons = oBXLEditorUtils.oTune[this.id].buttons;
1477
1478		if (ripButtons)
1479		{
1480			i = 0;
1481			while(i < toolbarConfig.length)
1482			{
1483				if (ripButtons[toolbarConfig[i]])
1484					toolbarConfig = BX.util.deleteFromArray(toolbarConfig, i);
1485				else
1486					i++;
1487			}
1488		}
1489
1490		if (addButtons)
1491		{
1492			for (var j = 0, n = addButtons.length; j < n; j++)
1493			{
1494				if (addButtons[j].ind == -1 || addButtons[j].ind >= toolbarConfig.length)
1495					toolbarConfig.push(addButtons[j].but.id);
1496				else
1497					toolbarConfig = BX.util.insertIntoArray(toolbarConfig, addButtons[j].ind, addButtons[j].but.id);
1498			}
1499		}
1500	}
1501
1502	var
1503		begWidth = 0,
1504		endWidth = 0, // 4
1505		curLineWidth = begWidth, pCont,
1506		butContWidth = parseInt(this.pButtonsCont.offsetWidth);
1507
1508	this.ToolbarStartLine(true);
1509	for(i in toolbarConfig)
1510	{
1511		butId = toolbarConfig[i];
1512		if (typeof butId != 'string' || !toolbarConfig.hasOwnProperty(i))
1513			continue;
1514
1515		if (butId == '=|=')
1516		{
1517			this.ToolbarNewLine();
1518			curLineWidth = begWidth;
1519		}
1520		else if (LHEButtons[butId])
1521		{
1522			if (this.bBBCode && LHEButtons[butId].bBBHide)
1523				continue;
1524
1525			this.buttonsIndex[butId] = i;
1526			pCont = this.AddButton(LHEButtons[butId], butId);
1527			if (pCont)
1528			{
1529				curLineWidth += parseInt(pCont.style.width) || 23;
1530				if (curLineWidth + endWidth > butContWidth && butContWidth > 0)
1531				{
1532					butContWidth = parseInt(this.pButtonsCont.offsetWidth); // Doublecheck
1533					if (curLineWidth + endWidth > butContWidth && butContWidth > 0)
1534					{
1535						this.ToolbarNewLine();
1536						this.pButtonsCont.appendChild(pCont);
1537						curLineWidth = begWidth;
1538					}
1539				}
1540			}
1541		}
1542	}
1543	this.ToolbarEndLine();
1544
1545	if (typeof this.arConfig.controlButtonsHeight == 'undefined')
1546		this.buttonsHeight = this.toolbarLineCount * 27;
1547	else
1548		this.buttonsHeight = parseInt(this.arConfig.controlButtonsHeight || 0
1548);
1549
1550	this.arConfig.minHeight += this.buttonsHeight;
1551	this.arConfig.maxHeight += this.buttonsHeight;
1552
1553	BX.addCustomEvent(this, 'onShow', BX.proxy(this.ResizeFrame, this));
1554},
1555
1556AddButton: function(oBut, buttonId)
1557{
1558	if (oBut.parser && oBut.parser.obj)
1559		this.oSpecialParsers[oBut.parser.name] = oBut.parser.obj;
1560
1561	this.buttonsCount++;
1562	var result;
1563	if (!oBut.type || !oBut.type == 'button')
1564	{
1565		if (buttonId == 'Code')
1566			this.bCodeBut = true;
1567
1568		var pButton = new window.LHEButton(oBut, this);
1569		if (pButton && pButton.oBut)
1570		{
1571			if (buttonId == 'Source')
1572				this.sourseBut = pButton;
1573			else if(buttonId == 'Quote')
1574				this.quoteBut = pButton;
1575
1576			result = this.pButtonsCont.appendChild(pButton.pCont);
1577		}
1578	}
1579	else if (oBut.type == 'Colorpicker')
1580	{
1581		var pColorpicker = new window.LHEColorPicker(oBut, this);
1582		result =  this.pButtonsCont.appendChild(pColorpicker.pCont);
1583	}
1584	else if (oBut.type == 'List')
1585	{
1586		var pList = new window.LHEList(oBut, this);
1587		result =  this.pButtonsCont.appendChild(pList.pCont);
1588	}
1589
1590	if (oBut.parsers)
1591	{
1592		for(var i = 0, cnt = oBut.parsers.length; i < cnt; i++)
1593			if (oBut.parsers[i] && oBut.parsers[i].obj)
1594				this.oSpecialParsers[oBut.parsers[i].name] = oBut.parsers[i].obj;
1595	}
1596
1597	return result;
1598},
1599
1600AddParser: function(parser)
1601{
1602	if (parser && parser.name && typeof parser.obj == 'object')
1603		this.oSpecialParsers[parser.name] = parser.obj;
1604},
1605
1606ToolbarStartLine: function(bFirst)
1607{
1608	// Hack for IE 7
1609	if (!bFirst && BX.browser.IsIE())
1610		this.pButtonsCont.appendChild(BX.create("IMG", {props: {src: this.oneGif, className: "lhe-line-ie"}}));
1611
1612	this.pButtonsCont.appendChild(BX.create("DIV", {props: {className: 'lhe-line-begin'}}));
1613},
1614
1615ToolbarEndLine: function()
1616{
1617	this.pButtonsCont.appendChild(BX.create("DIV", {props: {className: 'lhe-line-end'}}));
1618},
1619
1620ToolbarNewLine: function()
1621{
1622	this.toolbarLineCount++;
1623	this.ToolbarEndLine();
1624	this.ToolbarStartLine();
1625},
1626
1627OpenDialog: function(arParams)
1628{
1629	var oDialog = new window.LHEDialog(arParams, this);
1630},
1631
1632GetSelectionObject: function()
1633{
1634	var oSelection, oRange, root;
1635	if(this.pEditorDocument.selection) // IE
1636	{
1637		oSelection = this.pEditorDocument.selection;
1638		oRange = oSelection.createRange();
1639
1640		if(oSelection.type=="Control")
1641			return oRange.commonParentElement();
1642
1643		return oRange.parentElement();
1644	}
1645	else // FF
1646	{
1647		oSelection = this.pEditorWindow.getSelection();
1648		if(!oSelection)
1649			return false;
1650
1651		var container, i, rangeCount = oSelection.rangeCount, obj;
1652		for(var i = 0; i < rangeCount; i++)
1653		{
1654			oRange = oSelection.getRangeAt(i);
1655			container = oRange.startContainer;
1656			if(container.nodeType != 3)
1657			{
1658				if(container.nodeType == 1 && container.childNodes.length <= 0)
1659					obj = container;
1660				else
1661					obj = container.childNodes[oRange.startOffset];
1662			}
1663			else
1664			{
1665				temp = oRange.commonAncestorContainer;
1666				while(temp && temp.nodeType == 3)
1667					temp = temp.parentNode;
1668				obj = temp;
1669			}
1670			root = (i == 0) ? obj : BXFindParentElement(root, obj);
1671		}
1672		return root;
1673	}
1674},
1675
1676GetSelectionObjects: function()
1677{
1678	var oSelection;
1679	if(this.pEditorDocument.selection) // IE
1680	{
1681		oSelection = this.pEditorDocument.selection;
1682		var s = oSelection.createRange();
1683
1684		if(oSelection.type=="Control")
1685			return s.commonParentElement();
1686
1687		return s.parentElement();
1688	}
1689	else // FF
1690	{
1691		oSelection = this.pEditorWindow.getSelection();
1692		if(!oSelection)
1693			return false;
1694		var oRange;
1695		var container, temp;
1696		var res = [];
1697		for(var i = 0; i < oSelection.rangeCount; i++)
1698		{
1699			oRange = oSelection.getRangeAt(i);
1700			container = oRange.startContainer;
1701			if(container.nodeType != 3)
1702			{
1703				if(container.nodeType == 1 && container.childNodes.length <= 0)
1704					res[res.length] = container;
1705				else
1706					res[res.length] = container.childNodes[oRange.startOffset];
1707			}
1708			else
1709			{
1710				temp = oRange.commonAncestorContainer;
1711				while(temp && temp.nodeType == 3)
1712					temp = temp.parentNode;
1713				res[res.length] = temp;
1714			}
1715		}
1716		if(res.length > 1)
1717			return res;
1718		return res[0];
1719	}
1720},
1721
1722GetSelectionRange: function(doc, win)
1723{
1724	try{
1725		var
1726			oDoc = doc || this.pEditorDocument,
1727			oWin = win || this.pEditorWindow,
1728			oRange,
1729			oSel = this.GetSelection(oDoc, oWin);
1730
1731		if (oSel)
1732		{
1733			if (oDoc.createRange)
1734			{
1735				if (oSel.getRangeAt)
1736					oRange = oSel.getRangeAt(0);
1737				else
1738				{
1739					oRange = document.createRange();
1740					oRange.setStart(oSel.anchorNode, oSel.anchorOffset);
1741					oRange.setEnd(oSel.focusNode, oSel.focusOffset);
1742				}
1743			}
1744			else
1745				oRange = oSel.createRange();
1746		}
1747		else
1748		{
1749			oRange = false;
1750		}
1751
1752	} catch(e) {oRange = false;}
1753
1754	return oRange;
1755},
1756
1757SelectRange: function(oRange, doc, win)
1758{
1759	try{ // IE9 sometimes generete JS error
1760		if (!oRange)
1761			return;
1762
1763		var
1764			oDoc = doc || this.pEditorDocument,
1765			oWin = win || this.pEditorWindow;
1766
1767		this.ClearSelection(oDoc, oWin);
1768		if (oDoc.createRange) // FF
1769		{
1770			var oSel = oWin.getSelection();
1771			oSel.removeAllRanges();
1772			oSel.addRange(oRange);
1773		}
1774		else //IE
1775		{
1776			oRange.select();
1777		}
1778
1779	}catch(e){}
1780},
1781
1782SelectElement: function(pElement)
1783{
1784	try{
1785		var
1786			oRange,
1787			oDoc = this.pEditorDocument,
1788			oWin = this.pEditorWindow;
1789
1790		if(oWin.getSelection)
1791		{
1792			var oSel = oWin.getSelection();
1793			oSel.selectAllChildren(pElement);
1794			oRange = oSel.getRangeAt(0);
1795			if (oRange.selectNode)
1796				oRange.selectNode(pElement);
1797		}
1798		else
1799		{
1800			oDoc.selection.empty();
1801			oRange = oDoc.selection.createRange();
1802			oRange.moveToElementText(pElement);
1803			oRange.select();
1804		}
1805		return oRange;
1806	}catch(e){}
1807},
1808
1809GetSelectedText: function(oRange)
1810{
1811	// Get selected text
1812	var selectedText = '';
1813	if (oRange.startContainer && oRange.endContainer) // DOM Model
1814	{
1815		if (oRange.startContainer == oRange.endContainer && (oRange.endContainer.nodeType == 3 || oRange.endContainer.nodeType == 1)
1815)
1816			selectedText = oRange.startContainer.textContent.substring(oRange.startOffset, oRange.endOffset);
1817	}
1818	else // IE
1819	{
1820		if (oRange.text == oRange.htmlText)
1821			selectedText = oRange.text;
1822	}
1823	return selectedText || '';
1824},
1825
1826ClearSelection: function(doc, win)
1827{
1828	var
1829		oDoc = doc || this.pEditorDocument,
1830		oWin = win || this.pEditorWindow;
1831
1832	if (oWin.getSelection)
1833		oWin.getSelection().removeAllRanges();
1834	else
1835		oDoc.selection.empty();
1836},
1837
1838GetSelection: function(oDoc, oWin)
1839{
1840	if (!oDoc)
1841		oDoc = document;
1842	if (!oWin)
1843		oWin = window;
1844
1845	var oSel = false;
1846	if (oWin.getSelection)
1847		oSel = oWin.getSelection();
1848	else if (oDoc.getSelection)
1849		oSel = oDoc.getSelection();
1850	else if (oDoc.selection)
1851		oSel = oDoc.selection;
1852	return oSel;
1853},
1854
1855InsertHTML: function(sContent)
1856{
1857	try{ // Don't clear "try catch"... Some times browsers generetes failures
1858		this.SetFocus();
1859
1860		if(BX.browser.IsIE())
1861		{
1862			var oRng = this.pEditorDocument.selection.createRange();
1863			if (oRng.pasteHTML)
1864			{
1865				oRng.pasteHTML(sContent);
1866				oRng.collapse(false);
1867				oRng.select();
1868			}
1869		}
1870		else if(BX.browser.IsIE11())
1871		{
1872			this.PasteHtmlAtCaret(sContent);
1873		}
1874		else
1875		{
1876			this.pEditorWindow.document.execCommand('insertHTML', false, sContent);
1877		}
1878	}catch(e){}
1879
1880	if (this.arConfig.bAutoResize && this.arConfig.bResizable)
1881		this.AutoResize();
1882},
1883
1884PasteHtmlAtCaret: function(html, selectPastedContent)
1885{
1886	var
1887		win = this.pEditorWindow,
1888		doc = this.pEditorDocument,
1889		sel, range;
1890
1891	if (win.getSelection)
1892	{
1893		// IE9 and non-IE
1894		sel = win.getSelection();
1895		if (sel.getRangeAt && sel.rangeCount)
1896		{
1897			range = sel.getRangeAt(0);
1898			range.deleteContents();
1899
1900			// Range.createContextualFragment() would be useful here but is
1901			// only relatively recently standardized and is not supported in
1902			// some browsers (IE9, for one)
1903			var el = doc.createElement("div");
1904			el.innerHTML = html;
1905			var frag = doc.createDocumentFragment(), node, lastNode;
1906			while ((node = el.firstChild))
1907				lastNode = frag.appendChild(node);
1908
1909			var firstNode = frag.firstChild;
1910			range.insertNode(frag);
1911
1912			// Preserve the selection
1913			if (lastNode)
1914			{
1915				range = range.cloneRange();
1916				range.setStartAfter(lastNode);
1917				if (selectPastedContent)
1918					range.setStartBefore(firstNode);
1919				else
1920					range.collapse(true);
1921
1922				sel.removeAllRanges();
1923				sel.addRange(range);
1924			}
1925		}
1926	}
1927	else if ((sel = doc.selection) && sel.type != "Control")
1928	{
1929		// IE < 9
1930		var originalRange = sel.createRange();
1931		originalRange.collapse(true);
1932		sel.createRange().pasteHTML(html);
1933		if (selectPastedContent)
1934		{
1935			range = sel.createRange();
1936			range.setEndPoint("StartToStart", originalRange);
1937			range.select();
1938		}
1939	}
1940},
1941
1942AppendCSS: function(styles)
1943{
1944	styles = BX.util.trim(styles);
1945	if (styles.length <= 0)
1946		return false;
1947
1948	var
1949		pDoc = this.pEditorDocument,
1950		pHeads = pDoc.getElementsByTagName("HEAD");
1951
1952	if(pHeads.length != 1)
1953		return false;
1954
1955	if(BX.browser.IsIE())
1956	{
1957		setTimeout(function()
1958		{
1959			try{
1960				if (pDoc.styleSheets.length == 0)
1961					pHeads[0].appendChild(pDoc.createElement("STYLE"));
1962				pDoc.styleSheets[0].cssText += styles;
1963			}catch(e){}
1964		}, 100);
1965	}
1966	else
1967	{
1968		try{
1969			var xStyle = pDoc.createElement("STYLE");
1970			pHeads[0].appendChild(xStyle);
1971			xStyle.appendChild(pDoc.createTextNode(styles));
1972		}catch(e){}
1973	}
1974	return true;
1975},
1976
1977SetBxTag: function(pElement, params)
1978{
1979	var id;
1980	if (params.id || pElement && pElement.id)
1981		id = params.id || pElement.id;
1982
1983	if (!id)
1984		id = 'bxid_' + Math.round(Math.random() * 1000000);
1985	else if (this.bxTags[id] && !params.tag)
1986		params.tag = this.bxTags[id].tag;
1987
1988	params.id = id;
1989	if (pElement)
1990		pElement.id = params.id;
1991
1992	this.bxTags[params.id] = params;
1993	return params.id;
1994},
1995
1996GetBxTag: function(id)
1997{
1998	if (id)
1999	{
2000		if (typeof id != "string" && id.id)
2001			id = id.id;
2002
2003		if (id && id.length > 0 && this.bxTags[id] && this.bxTags[id].tag)
2004		{
2005			this.bxTags[id].tag = this.bxTags[id].tag.toLowerCase();
2006			return this.bxTags[id];
2007		}
2008	}
2009
2010	return {tag: false};
2011},
2012
2013GetAttributesList: function(str)
2014{
2015	str = str + " ";
2016
2017	var arParams = {}, arPHP = [], bPhp = false, _this = this;
2018	// 1. Replace PHP by #BXPHP#
2019	str = str.replace(/<\?.*?\?>/ig, function(s)
2020	{
2021		arPHP.push(s);
2022		return "#BXPHP" + (arPHP.length - 1) + "#";
2023	});
2024
2025	// 2.0 Parse params - without quotes
2026	str = str.replace(/([^\w]??)(\w+?)=([^\s\'"]+?)(\s)/ig, function(s, b0, b1, b2, b3)
2027	{
2028		b2 = b2.replace(/#BXPHP(\d+)#/ig, function(s, num){return arPHP[num] || s;});
2029		arParams[b1.toLowerCase()] = BX.util.htmlspecialcharsback(b2);
2030		return b0;
2031	});
2032
2033	// 2.1 Parse params
2034	str = str.replace(/([^\w]??)(\w+?)\s*=\s*("|\')([^\3]*?)\3/ig, function(s, b0, b1, b2, b3)
2035	{
2036		// 3. Replace PHP back
2037		b3 = b3.replace(/#BXPHP(\d+)#/ig, function(s, num){return arPHP[num] || s;});
2038		arParams[b1.toLowerCase()] = BX.util.htmlspecialcharsback(b3);
2039		return b0;
2040	});
2041
2042	return arParams;
2043},
2044
2045RidOfNode: function (pNode, bHard)
2046{
2047	if (!pNode || pNode.nodeType != 1)
2048		return;
2049
2050	var i, nodeName = pNode.tagName.toLowerCase(),
2051		nodes = ['span', 'strike', 'del', 'font', 'code', 'div'];
2052
2053	if (BX.util.in_array(nodeName, nodes)) // Check node names
2054	{
2055		if (bHard !== true)
2056		{
2057			for (i = pNode.attributes.length - 1; i >= 0; i--)
2058			{
2059				if (BX.util.trim(pNode.getAttribute(pNode.attributes[i].nodeName.toLowerCase())) != "")
2060					return false; // Node have attributes, so we cant get rid of it without loosing info
2061			}
2062		}
2063
2064		var arNodes = pNode.childNodes;
2065		while(arNodes.length > 0)
2066			pNode.parentNode.insertBefore(arNodes[0], pNode);
2067
2068		pNode.parentNode.removeChild(pNode);
2069		//this.OnEvent("OnSelectionChange");
2070		return true;
2071	}
2072
2073	return false;
2074},
2075
2076WrapSelectionWith: function (tagName, arAttributes)
2077{
2078	this.SetFocus();
2079	var oRange, oSelection;
2080
2081	if (!tagName)
2082		tagName = 'SPAN';
2083
2084	var sTag = 'FONT', i, pEl, arTags, arRes = [];
2085
2086	try{this.pEditorDocument.execCommand("styleWithCSS", false, false);}catch(e){}
2087	this.executeCommand("FontName", "bitrixtemp");
2088
2089	arTags = this.pEditorDocument.getElementsByTagName(sTag);
2090
2091	for(i = arTags.length - 1; i >= 0; i--)
2092	{
2093		if (arTags[i].getAttribute('face') != 'bitrixtemp')
2094			continue;
2095
2096		pEl = BX.create(tagName, arAttributes, this.pEditorDocument);
2097		arRes.push(pEl);
2098
2099		while(arTags[i].firstChild)
2100			pEl.appendChild(arTags[i].firstChild);
2101
2102		arTags[i].parentNode.insertBefore(pEl, arTags[i]);
2103		arTags[i].parentNode.removeChild(arTags[i]);
2104	}
2105
2106	if (this.arConfig.bAutoResize && this.arConfig.bResizable)
2107		this.AutoResize();
2108
2109	return arRes;
2110},
2111
2112SaveSelectionRange: function()
2113{
2114	if (this.sEditorMode == 'code')
2115		this.oPrevRangeText = this.GetSelectionRange(document, window);
2116	else
2117		this.oPrevRange = this.GetSelectionRange();
2118},
2119
2120RestoreSelectionRange: function()
2121{
2122	if (this.sEditorMode == 'code')
2123		this.IESetCarretPos(this.oPrevRangeText);
2124	else if(this.oPrevRange)
2125		this.SelectRange(this.oPrevRange);
2126},
2127
2128focus: function(el, bSelect)
2129{
2130	setTimeout(function()
2131	{
2132		try{
2133			el.focus();
2134			if(bSelect)
2135				el.select();
2136		}catch(e){}
2137	}, 100);
2138},
2139
2140// Methods below used in BB-mode
2141// Earlier was in bb.js
2142InitBBCode: function()
2143{
2144	this.stack = [];
2145	var _this = this;
2146	this.pTextarea.onkeydown = BX.proxy(this.OnKeyDownBB, this);
2147
2148	// Backup parser functions
2149	this._GetNodeHTMLLeft = this.GetNodeHTMLLeft;
2150	this._GetNodeHTMLRight = this.GetNodeHTMLRight;
2151
2152	this.GetNodeHTMLLeft = this.GetNodeHTMLLeftBB;
2153	this.GetNodeHTMLRight = this.GetNodeHTMLRightBB;
2154},
2155
2156ShutdownBBCode: function()
2157{
2158	this.bBBCode = false;
2159	this.arConfig.bBBCode = false;
2160
2161	this.pTextarea.onkeydown = null;
2162
2163	// Restore parser functions
2164	this.GetNodeHTMLLeft = this._GetNodeHTMLLeft;
2165	this.GetNodeHTMLRight = this._GetNodeHTMLRight;
2166
2167	this.arConfig.bConvertContentFromBBCodes = false;
2168},
2169
2170FormatBB: function(params)
2171{
2172	var
2173		pBut = params.pBut,
2174		value = params.value,
2175		tag = params.tag.toUpperCase(),
2176		tag_end = tag;
2177
2178	if (tag == 'FONT' || tag == 'COLOR' || tag == 'SIZE')
2179		tag += "=" + value;
2180
2181	if ((!BX.util.in_array(tag, this.stack) || this.GetTextSelection()) && !(tag == 'FONT' && value == 'none'))
2182	{
2183		if (!this.WrapWith("[" + tag + "]", "[/" + tag_end + "]"))
2184		{
2185			this.stack.push(tag);
2186
2187			if (pBut && pBut.Check)
2188				pBut.Check(true);
2189		}
2190	}
2191	else
2192	{
2193		var res = false;
2194		while (res = this.stack.pop())
2195		{
2196			this.WrapWith("[/" + res + "]", "");
2197			if (pBut && pBut.Check)
2198				pBut.Check(false);
2199
2200			if (res == tag)
2201				break;
2202		}
2203	}
2204},
2205
2206GetTextSelection: function()
2207{
2208	var res = false;
2209	if (typeof this.pTextarea.selectionStart != 'undefined')
2210	{
2211		res = this.pTextarea.value.substr(this.pTextarea.selectionStart, this.pTextarea.selectionEnd - this.pTextarea.selectionStart);
2212	}
2213	else if (document.selection && document.selection.createRange)
2214	{
2215		res = document.selection.createRange().text;
2216	}
2217	else if (window.getSelection)
2218	{
2219		res = window.getSelection();
2220		res = res.toString();
2221	}
2222
2223	return res;
2224},
2225
2226IESetCarretPos: function(oRange)
2227{
2228	if (!oRange || !BX.browser.IsIE() || oRange.text.length != 0 /* text selected*/)
2229		return;
2230
2231	oRange.moveStart('character', - this.pTextarea.value.length);
2232	var pos = oRange.text.length;
2233
2234	var range = this.pTextarea.createTextRange();
2235	range.collapse(true);
2236	range.moveEnd('character', pos);
2237	range.moveStart('character', pos);
2238	range.select();
2239},
2240
2241WrapWith: function (tagBegin, tagEnd, postText)
2242{
2243	if (!tagBegin)
2244		tagBegin = "";
2245	if (!tagEnd)
2246		tagEnd = ""
2247
2248	if (!postText)
2249		postText = "";
2250
2251	if (tagBegin.length <= 0 && tagEnd.length <= 0 && postText.length <= 0)
2252		return true;
2253
2254	var bReplaceText = !!postText;
2255	var sSelectionText = this.GetTextSelection();
2256
2257	if (!this.bTextareaFocus)
2258		this.pTextarea.focus(); // BUG IN IE
2259
2260	var isSelect = (sSelectionText ? 'select' : bReplaceText ? 'after' : 'in');
2261
2262	if (bReplaceText)
2263		postText = tagBegin + postText + tagEnd;
2264	else if (sSelectionText)
2265		postText = tagBegin + sSelectionText + tagEnd;
2266	else
2267		postText = tagBegin + tagEnd;
2268
2269	if (typeof this.pTextarea.selectionStart != 'undefined')
2270	{
2271		var
2272			currentScroll = this.pTextarea.scrollTop,
2273			start = this.pTextarea.selectionStart,
2274			end = this.pTextarea.selectionEnd;
2275
2276		this.pTextarea.value = this.pTextarea.value.substr(0, start) + postText + this.pTextarea.value.substr(end);
2277
2278		if (isSelect == 'select')
2279		{
2280			this.pTextarea.selectionStart = start;
2281			this.pTextarea.selectionEnd = start + postText.length;
2282		}
2283		else if (isSelect == 'in')
2284		{
2285			this.pTextarea.selectionStart = this.pTextarea.selectionEnd = start + tagBegin.length;
2286		}
2287		else
2288		{
2289			this.pTextarea.selectionStart = this.pTextarea.selectionEnd = start + postText.length;
2290		}
2291		this.pTextarea.scrollTop = currentScroll;
2292	}
2293	else if (document.selection && document.selection.createRange)
2294	{
2295		var sel = document.selection.createRange();
2296		var selection_copy = sel.duplicate();
2297		postText = postText.replace(/\r?\n/g, '\n');
2298		sel.text = postText;
2299		sel.setEndPoint('StartToStart', selection_copy);
2300		sel.setEndPoint('EndToEnd', selection_copy);
2301
2302		if (isSelect == 'select')
2303		{
2304			sel.collapse(true);
2305			postText = postText.replace(/\r\n/g, '1');
2306			sel.moveEnd('character', postText.length);
2307		}
2308		else if (isSelect == 'in')
2309		{
2310			sel.collapse(false);
2311			sel.moveEnd('character', tagBegin.length);
2312			sel.collapse(false);
2313		}
2314		else
2315		{
2316			sel.collapse(false);
2317			sel.moveEnd('character', postText.length);
2318			sel.collapse(false);
2319		}
2320		sel.select();
2321	}
2322	else
2323	{
2324		// failed - just stuff it at the end of the message
2325		this.pTextarea.value += postText;
2326	}
2327	return true;
2328},
2329
2330ParseBB: function (sContent)  // BBCode -> WYSIWYG
2331{
2332	sContent = BX.util.htmlspecialchars(sContent);
2333
2334	// Table
2335	sContent = sContent.replace(/[\r\n\s\t]?\[table\][\r\n\s\t]*?\[tr\]/ig, '[TABLE][TR]');
2336	sContent = sContent.replace(/\[tr\][\r\n\s\t]*?\[td\]/ig, '[TR][TD]');
2337	sContent = sContent.replace(/\[tr\][\r\n\s\t]*?\[th\]/ig, '[TR][TH]');
2338	sContent = sContent.replace(/\[\/td\][\r\n\s\t]*?\[td\]/ig, '[/TD][TD]');
2339	sContent = sContent.replace(/\[\/tr\][\r\n\s\t]*?\[tr\]/ig, '[/TR][TR]');
2340	sContent = sContent.replace(/\[\/td\][\r\n\s\t]*?\[\/tr\]/ig, '[/TD][/TR]');
2341	sContent = sContent.replace(/\[\/th\][\r\n\s\t]*?\[\/tr\]/ig, '[/TH][/TR]');
2342	sContent = sContent.replace(/\[\/tr\][\r\n\s\t]*?\[\/table\][\r\n\s\t]?/ig, '[/TR][/TABLE]');
2343
2344	// List
2345	sContent = sContent.replace(/[\r\n\s\t]*?\[\/list\]/ig, '[/LIST]');
2346	sContent = sContent.replace(/[\r\n\s\t]*?\[\*\]?/ig, '[*]');
2347
2348	var
2349		arSimpleTags = [
2350			'b','u', 'i', ['s', 'del'], // B, U, I, S
2351			'table', 'tr', 'td', 'th'//, // Table
2352		],
2353		bbTag, tag, i, l = arSimpleTags.length, re;
2354
2355	for (i = 0; i < l; i++)
2356	{
2357		if (typeof arSimpleTags[i] == 'object')
2358		{
2359			bbTag = arSimpleTags[i][0];
2360			tag = arSimpleTags[i][1];
2361		}
2362		else
2363		{
2364			bbTag = tag = arSimpleTags[i];
2365		}
2366
2367		sContent = sContent.replace(new RegExp('\\[(\\/?)' + bbTag + '\\]', 'ig'), "<$1" + tag + ">");
2368	}
2369
2370	// Link
2371	sContent = sContent.replace(/\[url\]((?:\s|\S)*?)\[\/url\]/ig, "<a href=\"$1\">$1</a>");
2372	sContent = sContent.replace(/\[url\s*=\s*((?:[^\[\]]*?(?:\[[^\]]+?\])*[^\[\]]*?)*)\s*\]((?:\s|\S)*?)\[\/url\]/ig, "<a href=\"$1\">$2</a>");
2373
2374	// Img
2375	var _this = this;
2376	sContent = sContent.replace(/\[img(?:\s*?width=(\d+)\s*?height=(\d+))?\]((?:\s|\S)*?)\[\/img\]/ig,
2377		function(str, w, h, src)
2378		{
2379			var strSize = "";
2380			w = parseInt(w);
2381			h = parseInt(h);
2382
2383			if (w && h && _this.bBBParseImageSize)
2384				strSize = ' width="' + w + '" height="' + h + '"';
2385
2386			return '<img  src="' + src + '"' + strSize + '/>';
2387		}
2388	);
2389
2390	// Font color
2391	i = 0;
2392	while (sContent.toLowerCase().indexOf('[color=') != -1 && sContent.toLowerCase().indexOf('[/color]') != -1 && i++ < 20)
2393		sContent = sContent.replace(/\[color=((?:\s|\S)*?)\]((?:\s|\S)*?)\[\/color\]/ig, "<font color=\"$1\">$2</font>");
2394
2395	// List
2396	i = 0;
2397	while (sContent.toLowerCase().indexOf('[list=') != -1 && sContent.toLowerCase().indexOf('[/list]') != -1 && i++ < 20)
2398		sContent = sContent.replace(/\[list=1\]((?:\s|\S)*?)\[\/list\]/ig, "<ol>$1</ol>");
2399
2400	i = 0;
2401	while (sContent.toLowerCase().indexOf('[list') != -1 && sContent.toLowerCase().indexOf('[/list]') != -1 && i++ < 20)
2402		sContent = sContent.replace(/\[list\]((?:\s|\S)*?)\[\/list\]/ig, "<ul>$1</ul>");
2403
2404	sContent = sContent.replace(/\[\*\]/ig, "<li>");
2405
2406	// Font
2407	i = 0;
2408	while (sContent.toLowerCase().indexOf('[font=') != -1 && sContent.toLowerCase().indexOf('[/font]') != -1 && i++ < 20)
2409		sContent = sContent.replace(/\[font=((?:\s|\S)*?)\]((?:\s|\S)*?)\[\/font\]/ig, "<font face=\"$1\">$2</font>");
2410
2411	// Font size
2412	i = 0;
2413	while (sContent.toLowerCase().indexOf('[size=') != -1 && sContent.toLowerCase().indexOf('[/size]') != -1 && i++ < 20)
2414		sContent = sContent.replace(/\[size=((?:\s|\S)*?)\]((?:\s|\S)*?)\[\/size\]/ig, "<font size=\"$1\">$2</font>");
2415
2416	// Replace \n => <br/>
2417	sContent = sContent.replace(/\n/ig, "<br />");
2418
2419	return sContent;
2420},
2421
2422UnParseNodeBB: function (pNode) // WYSIWYG -> BBCode
2423{
2424	if (pNode.text == "br")
2425		return "#BR#";
2426
2427	if (pNode.type == 'text')
2428		return false;
2429
2430	//[CODE] Handle code tag
2431	if (pNode.text == "pre" && pNode.arAttributes['class'] == 'lhe-code')
2432		return "[CODE]" + this.RecGetCodeContent(pNode) + "[/CODE]";
2433
2434	pNode.bbHide = true;
2435	if (pNode.text == 'font' && pNode.arAttributes.color)
2436	{
2437		pNode.bbHide = false;
2438		pNode.text = 'color';
2439		pNode.bbValue = pNode.arAttributes.color;
2440	}
2441	else if (pNode.text == 'font' && pNode.arAttributes.size)
2442	{
2443		pNode.bbHide = false;
2444		pNode.text = 'size';
2445		pNode.bbValue = pNode.arAttributes.size;
2446	}
2447	else if (pNode.text == 'font' && pNode.arAttributes.face)
2448	{
2449		pNode.bbHide = false;
2450		pNode.text = 'font';
2451		pNode.bbValue = pNode.arAttributes.face;
2452	}
2453	else if(pNode.text == 'del')
2454	{
2455		pNode.bbHide = false;
2456		pNode.text = 's';
2457	}
2458	else if(pNode.text == 'strong' || pNode.text == 'b')
2459	{
2460		pNode.bbHide = false;
2461		pNode.text = 'b';
2462	}
2463	else if(pNode.text == 'em' || pNode.text == 'i')
2464	{
2465		pNode.bbHide = false;
2466		pNode.text = 'i';
2467	}
2468	else if(pNode.text == 'blockquote')
2469	{
2470		pNode.bbHide = false;
2471		pNode.text = 'quote';
2472	}
2473	else if(pNode.text == 'ol')
2474	{
2475		pNode.bbHide = false;
2476		pNode.text = 'list';
2477		pNode.bbBreakLineRight = true;
2478		pNode.bbValue = '1';
2479	}
2480	else if(pNode.text == 'ul')
2481	{
2482		pNode.bbHide = false;
2483		pNode.text = 'list';
2484		pNode.bbBreakLineRight = true;
2485	}
2486	else if(pNode.text == 'li')
2487	{
2488		pNode.bbHide = false;
2489		pNode.text = '*';
2490		pNode.bbBreakLine = true;
2491		pNode.bbHideRight = true;
2492	}
2493	else if(pNode.text == 'a')
2494	{
2495		pNode.bbHide = false;
2496		pNode.text = 'url';
2497		pNode.bbValue = pNode.arAttributes.href;
2498	}
2499	else if(this.parseAlign
2500		&&
2501		(pNode.arAttributes.align || pNode.arStyle.textAlign)
2502		&&
2503		!(BX.util.in_array(pNode.text.toLowerCase(), ['table', 'tr', 'td', 'th']))
2504		)
2505	{
2506		var align = pNode.arStyle.textAlign || pNode.arAttributes.align;
2507		if (BX.util.in_array(align, ['left', 'right', 'center', 'justify']))
2508		{
2509			pNode.bbHide = false;
2510			pNode.text = align;
2511		}
2512		else
2513		{
2514			pNode.bbHide = !BX.util.in_array(pNode.text, this.arBBTags);
2515		}
2516	}
2517	else if(BX.util.in_array(pNode.text, this.arBBTags)) //'p', 'u', 'div', 'table', 'tr', 'img', 'td', 'a'
2518	{
2519		pNode.bbHide = false;
2520	}
2521
2522	return false;
2523},
2524
2525RecGetCodeContent: function(pNode) // WYSIWYG -> BBCode
2526{
2527	if (!pNode || !pNode.arNodes || !pNode.arNodes.length)
2528		return '';
2529
2530	var res = '';
2531	for (var i = 0; i < pNode.arNodes.length; i++)
2532	{
2533		if (pNode.arNodes[i].type == 'text')
2534			res += pNode.arNodes[i].text;
2535		else if (pNode.arNodes[i].type == 'element' && pNode.arNodes[i].text == "br")
2536			res += (this.bBBCode ? "#BR#" : "\n");
2537		else if (pNode.arNodes[i].arNodes)
2538			res += this.RecGetCodeContent(pNode.arNodes[i]);
2539	}
2540
2541	if (this.bBBCode)
2542	{
2543		if (BX.browser.IsIE())
2544			res = res.replace(/\r/ig, "#BR#");
2545		else
2546			res = res.replace(/\n/ig, "#BR#");
2547	}
2548	else if (BX.browser.IsIE())
2549	{
2550		res = res.replace(/\n/ig, "\r\n");
2551	}
2552
2553	return res;
2554},
2555
2556GetNodeHTMLLeftBB: function (pNode)
2557{
2558	if(pNode.type == 'text')
2559	{
2560		var text = BX.util.htmlspecialchars(pNode.text);
2561		text = text.replace(/\[/ig, "&#91;");
2562		text = text.replace(/\]/ig, "&#93;");
2563		return text;
2564	}
2565
2566	var res = "";
2567	if (pNode.bbBreakLine)
2568		res += "\n";
2569
2570	if(pNode.type == 'element' && !pNode.bbHide)
2571	{
2572		res += "[" + pNode.text.toUpperCase();
2573		if (pNode.bbValue)
2574			res += '=' + pNode.bbValue;
2575		res += "]";
2576	}
2577
2578	return res;
2579},
2580
2581GetNodeHTMLRightBB: function (pNode)
2582{
2583	var res = "";
2584	if (pNode.bbBreakLineRight)
2585		res += "\n";
2586
2587	if(pNode.type == 'element' && (pNode.arNodes.length > 0 || this.IsPairNode(pNode.text)) && !pNode.bbHide && !pNode.bbHideRight)
2588		res += "[/" + pNode.text.toUpperCase() + "]";
2589
2590	return res;
2591},
2592
2593OptimizeBB: function (str)
2594{
2595	// TODO: kill links without text and names
2596	// TODO: Kill multiple line ends
2597	var
2598		iter = 0,
2599		bReplasing = true,
2600		arTags = ['b', 'i', 'u', 's', 'color', 'font', 'size', 'quote'],
2601		replaceEmptyTags = function(){i--; bReplasing = true; return ' ';},
2602		re, tagName, i, l;
2603
2604	while(iter++ < 20 && bReplasing)
2605	{
2606		bReplasing = false;
2607		for (i = 0, l = arTags.length; i < l; i++)
2608		{
2609			tagName = arTags[i];
2610			// Replace empties: [b][/b]  ==> ""
2611			re = new RegExp('\\[' + tagName + '[^\\]]*?\\]\\s*?\\[/' + tagName + '\\]', 'ig');
2612			str = str.replace(re, replaceEmptyTags);
2613
2614			if (tagName !== 'quote')
2615			{
2616				re = new RegExp('\\[((' + tagName + '+?)(?:\\s+?[^\\]]*?)?)\\]([\\s\\S]+?)\\[\\/\\2\\](\\s*?)\\[\\1\\]([\\s\\S]+?)\\[\\/\\2\\]', 'ig');
2617				str = str.replace(re, function(str, b1, b2, b3, spacer, b4)
2618					{
2619						if (spacer.indexOf("\n") != -1)
2620							return str;
2621						bReplasing = true;
2622						return '[' + b1 + ']' + b3 + ' ' + b4 + '[/' + b2 + ']';
2623					}
2624				);
2625
2626				//Replace [b]1 [b]2[/b] 3[/b] ===>>  [b]1 2 3[/b]
2627				// re = new RegExp('(\\[' + tagName + '(?:\\s+?[^\\]]*?)?\\])([\\s\\S]+?)\\1([\\s\\S]+?)(\\[\\/' + tagName + '\\])([\\s\\S]+?)\\4', 'ig');
2628				// str = str.replace(re, function(str, b1, b2, b3, b4, b5)
2629				// {
2630				// bReplasing = true;
2631				// return b1 + b2 + b3 + b5 + b4;
2632				// }
2633				// );
2634			}
2635		}
2636	}
2637	//
2638	str = str.replace(/[\r\n\s\t]*?\[\/list\]/ig, "\n[/LIST]");
2639	str = str.replace(/[\r\n\s\t]*?\[\/list\]/ig, "\n[/LIST]");
2640
2641	// Cut "\n" in the end of the message (only for BB)
2642	str = str.replace(/\n*$/ig, '');
2643
2644	return str;
2645},
2646
2647RemoveFormatBB: function()
2648{
2649	var str = this.GetTextSelection();
2650	if (str)
2651	{
2652		var
2653			it = 0,
2654			arTags = ['b', 'i', 'u', 's', 'color', 'font', 'size'],
2655			i, l = arTags.length;
2656
2657		//[b]123[/b]  ==> 123
2658		while (it < 30)
2659		{
2660			str1 = str;
2661			for (i = 0; i < l; i++)
2662				str = str.replace(new RegExp('\\[(' + arTags[i] + ')[^\\]]*?\\]([\\s\\S]*?)\\[/\\1\\]', 'ig'), "$2");
2663
2664			if (str == str1)
2665				break;
2666			it++;
2667		}
2668
2669		this.WrapWith('', '', str);
2670	}
2671},
2672
2673OnKeyDownBB: function(e)
2674{
2675	if(!e) e = window.event;
2676
2677	var key = e.which || e.keyCode;
2678	if (e.ctrlKey && !e.shiftKey && !e.altKey)
2679	{
2680		switch (key)
2681		{
2682			case 66 : // B
2683			case 98 : // b
2684				this.FormatBB({tag: 'B'});
2685				return BX.PreventDefault(e);
2686			case 105 : // i
2687			case 73 : // I
2688				this.FormatBB({tag: 'I'});
2689				return BX.PreventDefault(e);
2690			case 117 : // u
2691			case 85 : // U
2692				this.FormatBB({tag: 'U'});
2693				return BX.PreventDefault(e);
2694			case 81 : // Q - quote
2695				this.FormatBB({tag: 'QUOTE'});
2696				return BX.PreventDefault(e);
2697		}
2698	}
2699
2700	// Tab
2701	if (key == 9)
2702	{
2703		this.WrapWith('', '', "\t");
2704		return BX.PreventDefault(e);
2705	}
2706
2707	// Ctrl + Enter
2708	if ((e.keyCode == 13 || e.keyCode == 10) && e.ctrlKey && this.ctrlEnterHandler)
2709	{
2710		this.SaveContent();
2711		this.ctrlEnterHandler();
2712	}
2713},
2714
2715GetCutHTML: function(e)
2716{
2717	if (this.curCutId)
2718	{
2719		var pCut = this.pEditorDocument.getElementById(this.curCutId);
2720		if (pCut)
2721		{
2722			pCut.parentNode.insertBefore(BX.create("BR", {}, this.pEditorDocument), pCut);
2723			pCut.parentNode.removeChild(pCut);
2724		}
2725	}
2726
2727	this.curCutId = this.SetBxTag(false, {tag: "cut"});
2728	return '<img src="' + this.oneGif+ '" class="bxed-cut" id="' + this.curCutId + '" title="' + BX.message.CutTitle + '"/>';
2729},
2730
2731OnPaste: function()
2732{
2733	if (this.bOnPasteProcessing)
2734		return;
2735
2736	this.bOnPasteProcessing = true;
2737	var _this = this;
2738	var scrollTop = this.pEditorDocument.body.scrollTop;
2739	setTimeout(function(){
2740		_this.bOnPasteProcessing = false;
2741		_this.InsertHTML('<span style="visibility: hidden;" id="' + _this.SetBxTag(false, {tag: "cursor"}) + '" ></span>');
2742
2743		_this.SaveContent();
2744		setTimeout(function()
2745		{
2746			var content = _this.GetContent();
2747
2748			if (/<\w[^>]*(( class="?MsoNormal"?)|(="mso-))/gi.test(content))
2749				content = _this.CleanWordText(content);
2750
2751			_this.SetEditorContent(content);
2752
2753			setTimeout(function()
2754			{
2755				try{
2756					var pCursor = _this.pEditorDocument.getElementById(_this.lastCursorId);
2757					if (pCursor && pCursor.parentNode)
2758					{
2759						var newScrollTop = pCursor.offsetTop - 30;
2760						if (newScrollTop > 0)
2761						{
2762							if (scrollTop > 0 && scrollTop + parseInt(_this.pFrame.offsetHeight) > newScrollTop)
2763								_this.pEditorDocument.body.scrollTop = scrollTop;
2764							else
2765								_this.pEditorDocument.body.scrollTop = newScrollTop;
2766						}
2767
2768						_this.SelectElement(pCursor);
2769						pCursor.parentNode.removeChild(pCursor);
2770						_this.SetFocus();
2771					}
2772				}catch(e){}
2773			}, 100);
2774
2775		}, 100);
2776	}, 100);
2777},
2778
2779CleanWordText: function(text)
2780{
2781	text = text.replace(/<(P|B|U|I|STRIKE)>&nbsp;<\/\1>/g, ' ');
2782	text = text.replace(/<o:p>([\s\S]*?)<\/o:p>/ig, "$1");
2783	//text = text.replace(/<o:p>[\s\S]*?<\/o:p>/ig, "&nbsp;");
2784
2785	text = text.replace(/<span[^>]*display:\s*?none[^>]*>([\s\S]*?)<\/span>/gi, ''); // Hide spans with display none
2786
2787	text = text.replace(/<!--\[[\s\S]*?\]-->/ig, ""); //<!--[.....]--> <!--[if gte mso 9]>...<![endif]-->
2788	text = text.replace(/<!\[[\s\S]*?\]>/ig, ""); //	<! [if !vml]>
2789	text = text.replace(/<\\?\?xml[^>]*>/ig, ""); //<xml...>, </xml...>
2790
2791	text = text.replace(/<o:p>\s*<\/o:p>/ig, "");
2792
2793	text = text.replace(/<\/?[a-z1-9]+:[^>]*>/gi, "");	//<o:p...>, </o:p>
2794	text = text.replace(/<([a-z1-9]+[^>]*) class=([^ |>]*)(.*?>)/gi, "<$1$3");
2795	text = text.replace(/<([a-z1-9]+[^>]*) [a-z]+:[a-z]+=([^ |>]*)(.*?>)/gi, "<$1$3"); //	xmlns:v="urn:schemas-microsoft-com:vml"
2796
2797	text = text.replace(/&nbsp;/ig, ' ');
2798	text = text.replace(/\s+?/gi, ' ');
2799
2800	// Remove mso-xxx styles.
2801	text = text.replace(/\s*mso-[^:]+:[^;"]+;?/gi, "");
2802
2803	// Remove margin styles.
2804	text = text.replace(/\s*margin: 0cm 0cm 0pt\s*;/gi, "");
2805	text = text.replace(/\s*margin: 0cm 0cm 0pt\s*"/gi, "\"");
2806
2807	text = text.replace(/\s*TEXT-INDENT: 0cm\s*;/gi, "");
2808	text = text.replace(/\s*TEXT-INDENT: 0cm\s*"/gi, "\"");
2809
2810
2811	text = text.replace(/\s*TEXT-ALIGN: [^\s;]+;?"/gi, "\"");
2812	text = text.replace(/\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"");
2813	text = text.replace(/\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"");
2814	text = text.replace(/\s*tab-stops:[^;"]*;?/gi, "");
2815	text = text.replace(/\s*tab-stops:[^"]*/gi, "");
2816
2817	text = text.replace(/<FONT[^>]*>([\s\S]*?)<\/FONT>/gi, '$1');
2818	text = text.replace(/\s*face="[^"]*"/gi, "");
2819	text = text.replace(/\s*face=[^ >]*/gi, "");
2820	text = text.replace(/\s*FONT-FAMILY:[^;"]*;?/gi, "");
2821
2822	// Remove Class attributes
2823	text = text.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3");
2824
2825	// Remove styles.
2826	text = text.replace(/<(\w[^>]*) style="([^\"]*)"([^>]*)/gi, "<$1$3");
2827
2828	// Remove empty styles.
2829	text = text.replace(/\s*style="\s*"/gi, '');
2830
2831	// Remove Lang attributes
2832	text = text.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3");
2833
2834	var iter = 0;
2835	while (text.toLowerCase().indexOf('<span') != -1 && text.toLowerCase().indexOf('</span>') != -1 && iter++ < 20)
2836		text = text.replace(/<span[^>]*?>([\s\S]*?)<\/span>/gi, '$1');
2837
2838	var
2839		_text,
2840		i, tag, arFormatTags = ['b', 'strong', 'i', 'u', 'font', 'span', 'strike'];
2841
2842	while (true)
2843	{
2844		_text = text;
2845		for (i in arFormatTags)
2846		{
2847			tag = arFormatTags[i];
2848			text = text.replace(new RegExp('<' + tag + '[^>]*?>(\\s*?)<\\/' + tag + '>', 'gi'), '$1');
2849			text = text.replace(new RegExp('<\\/' + tag + '[^>]*?>(\\s*?)<' + tag + '>', 'gi'), '$1');
2850		}
2851
2852		if (_text == text)
2853			break;
2854	}
2855
2856	// Remove empty tags
2857	text = text.replace(/<(?:[^\s>]+)[^>]*>([\s\n\t\r]*)<\/\1>/g, "$1");
2858	text = text.replace(/<(?:[^\s>]+)[^>]*>(\s*)<\/\1>/g, "$1");
2859	text = text.replace(/<(?:[^\s>]+)[^>]*>(\s*)<\/\1>/g, "$1");
2860
2861	//text = text.replace(/<\/?xml[^>]*>/gi, "");	//<xml...>, </xml...>
2862	text = text.replace(/<xml[^>]*?(?:>\s*?<\/xml)?(?:\/?)?>/ig, '');
2863	text = text.replace(/<meta[^>]*?(?:>\s*?<\/meta)?(?:\/?)?>/ig, '');
2864	text = text.replace(/<link[^>
2864]*?(?:>\s*?<\/link)?(?:\/?)?>/ig, '');
2865	text = text.replace(/<style[\s\S]*?<\/style>/ig, '');
2866
2867	text = text.replace(/<table([\s\S]*?)>/gi, "<table>");
2868	text = text.replace(/<tr([\s\S]*?)>/gi, "<tr>");
2869	text = text.replace(/(<td[\s\S]*?)width=("|')[\s\S]*?\2([\s\S]*?>)/gi, "$1$3");
2870	text = text.replace(/(<td[\s\S]*?)height=("|')[\s\S]*?\2([\s\S]*?>)/gi, "$1$3");
2871	text = text.replace(/(<td[\s\S]*?)style=("|')[\s\S]*?\2([\s\S]*?>)/gi, "$1$3");
2872	text = text.replace(/(<td[\s\S]*?)valign=("|')[\s\S]*?\2([\s\S]*?>)/gi, "$1$3");
2873	text = text.replace(/(<td[\s\S]*?)nowrap=("|')[\s\S]*?\2([\s\S]*?>)/gi, "$1$3");
2874	text = text.replace(/(<td[\s\S]*?)nowrap([\s\S]*?>)/gi, "$1$3");
2875
2876	text = text.replace(/(<col[\s\S]*?)width=("|')[\s\S]*?\2([\s\S]*?>)/gi, "$1$3");
2877	text = text.replace(/(<col[\s\S]*?)style=("|')[\s\S]*?\2([\s\S]*?>)/gi, "$1$3");
2878
2879	// For Opera (12.10+) only when in text we have reference links.
2880	if (BX.browser.IsOpera())
2881		text = text.replace(/REF\s+?_Ref\d+?[\s\S]*?MERGEFORMAT\s([\s\S]*?)\s[\s\S]*?<\/xml>/gi, " $1 ");
2882
2883	return text;
2884}
2885};
2886
2887BXLEditorUtils = function()
2888{
2889	this.oTune = {};
2890	this.setCurrentEditorId('default');
2891};
2892BXLEditorUtils.prototype = {
2893	setCurrentEditorId: function(id)
2894	{
2895		this.curId = id;
2896	},
2897
2898	prepare : function()
2899	{
2900		if (!this.oTune[this.curId])
2901			this.oTune[this.curId] =
2902			{
2903				buttons: [],
2904				ripButtons: {}
2905			};
2906	},
2907
2908	addButton : function(pBut, ind)
2909	{
2910		if (!pBut || !pBut.id)
2911			return false;
2912		if (typeof ind == 'undefined')
2913			ind = -1;
2914
2915		this.prepare();
2916		this.oTune[this.curId].buttons.push({but: pBut, ind: ind});
2917
2918		return true;
2919	},
2920
2921	removeButton: function(id)
2922	{
2923		this.prepare();
2924		this.oTune[this.curId].ripButtons[id] = true;
2925	}
2926};
2927oBXLEditorUtils = new BXLEditorUtils();
2928
2929function BXFindParentElement(pElement1, pElement2)
2930{
2931	var p, arr1 = [], arr2 = [];
2932	while((pElement1 = pElement1.parentNode) != null)
2933		arr1[arr1.length] = pElement1;
2934	while((pElement2 = pElement2.parentNode) != null)
2935		arr2[arr2.length] = pElement2;
2936
2937	var min, diff1 = 0, diff2 = 0;
2938	if(arr1.length<arr2.length)
2939	{
2940		min = arr1.length;
2941		diff2 = arr2.length - min;
2942	}
2943	else
2944	{
2945		min = arr2.length;
2946		diff1 = arr1.length - min;
2947	}
2948
2949	for(var i=0; i<min-1; i++)
2950	{
2951		if(BXElementEqual(arr1[i+diff1], arr2[i+diff2]))
2952			return arr1[i+diff1];
2953	}
2954	return arr1[0];
2955}
2956
2957window.BXFindParentByTagName = function (pElement, tagName)
2958{
2959	tagName = tagName.toUpperCase();
2960	while(pElement && (pElement.nodeType !=1 || pElement.tagName.toUpperCase() != tagName))
2961		pElement = pElement.parentNode;
2962	return pElement;
2963}
2964
2965
2966function SetAttr(pEl, attr, val)
2967{
2968	if(attr=='className' && !BX.browser.IsIE())
2969		attr = 'class';
2970
2971	if(val.length <= 0)
2972		pEl.removeAttribute(attr);
2973	else
2974		pEl.setAttribute(attr, val);
2975}
2976
2977function BXCutNode(pNode)
2978{
2979	while(pNode.childNodes.length > 0)
2980		pNode.parentNode.insertBefore(pNode.childNodes[0], pNode);
2981
2982	pNode.parentNode.removeChild(pNode);
2983}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.