PageSourceSearch

https://voenchast.ru/local/templates/voenchast/components/bitrix/main.post.form/edit_post/script.js

js voenchast.ru collected 2026-10-09 22:41:37 UTC 92,358 bytes, 3,096 lines download raw bytes

1;(function(){
2	if (window["LHEPostForm"])
3		return;
4var repo = { controller : {}, handler : {}};
5BX.addCustomEvent(window, "BFileDLoadFormControllerWasBound", function(obj) { repo.controller[obj.id] = true;});
6BX.addCustomEvent(window, "WDLoadFormControllerInit", function(obj) { repo.controller[obj.CID] = obj; });
7BX.addCustomEvent(window, "WDLoadFormControllerWasBound", function(obj) { repo.controller[obj.CID] = true; });
8BX.addCustomEvent(window, "DiskDLoadFormControllerInit", function(obj) { repo.controller[obj.CID] = obj; });
9BX.addCustomEvent(window, "DiskLoadFormControllerWasBound", function(obj) { repo.controller[obj.CID] = true; });
10BX.addCustomEvent(window, 'OnEditorInitedBefore', function(editor) {
11	if (repo.handler[editor.id])
12	{
13		editor.__lhe_flags = ['OnEditorInitedBefore'];
14		if (repo.handler[editor.id]["params"] && repo.handler[editor.id]["params"]['LHEJsObjName']) // for custom templates
15			window[repo.handler[editor.id].params['LHEJsObjName']] = editor;
16		repo.handler[editor.id].OnEditorInitedBefore(editor);
17	}
18});
19var OnCreateIframeAfter = function(editor){
20	if (repo.handler[editor.id] && repo.handler[editor.id].editorIsLoaded !== true)
21	{
22		repo.handler[editor.id].editorIsLoaded = true;
23		repo.handler[editor.id].exec();
24		BX.onCustomEvent(repo.handler[editor.id], 'OnEditorIsLoaded', [repo.handler[editor.id], editor]);
25	}
26};
27BX.addCustomEvent(window, 'OnCreateIframeAfter', OnCreateIframeAfter);
28
29BX.addCustomEvent(window, 'OnEditorInitedAfter', function(editor, forced){
30	if (repo.handler[editor.id])
31	{
32		editor.__lhe_flags.push('OnEditorInitedAfter');
33		repo.handler[editor.id].OnEditorInitedAfter(editor);
34
35		if (repo.handler[editor.id].editorIsLoaded !== true && forced && editor.sandbox && editor.sandbox.inited)
36			OnCreateIframeAfter.apply(editor, [editor]);
37	}
38});
39BX.util.object_search = function(needle, haystack)
40{
41	for(var i in haystack)
42	{
43		if (haystack.hasOwnProperty(i))
44		{
45			if (haystack[i] == needle)
46				return true;
47			else if (typeof haystack[i] == "object" && haystack[i] !== null)
48			{
49				var result = BX.util.object_search_key(needle, haystack[i]);
50				if (result !== false)
51					return result;
52			}
53		}
54	}
55	return false;
56};
57var parserClass = function(bxTag, tag, additionalTags)
58{
59	additionalTags = (additionalTags && additionalTags.length > 0 ? additionalTags : []);
60	if (typeof tag == "object" && tag.length > 0)
61	{
62		var res;
63		while((res = tag.pop()) && res && tag.length > 0)
64		{
65			additionalTags.push(res);
66		}
67		tag = res;
68	}
69	additionalTags.push(tag);
70	this.exist = true;
71	this.bxTag = bxTag;
72	this.tag = tag;
73	this.tags = additionalTags;
74	this.regexp = new RegExp("\\[(" + additionalTags.join("|") + ")=((?:\\s|\\S)*?)(?:\\s*?WIDTH=(\\d+)\\s*?HEIGHT=(\\d+))?\\]", "ig");
75	this.code = '[' + tag + '=#ID##ADDITIONAL#]';
76	this.wysiwyg = '<span style="color: #2067B0; border-bottom: 1px dashed #2067B0; margin:0 2px;" id="#ID#"#ADDITIONAL#>#NAME#</span>';
77},
78diskController = function(manager, id, params)
79{
80	this.CID = this.id = id;
81	this.parser = (manager.parser['disk_file'] || null);
82	this.params = params;
83	this.node = BX('diskuf-selectdialog-' + id);
84	this.handler = repo.controller[id]; // BX.Disk.UF
85	this.manager = manager;
86	this.eventNode = this.manager.eventNode;
87	this.parserName = 'disk_file';
88	this.prefixNode = 'disk-edit-attach';
89	this.prefixHTMLNode = 'disk-attach-';
90	this.props = {
91		valueEditClassName : 'wd-inline-file',
92		securityCID : 'disk-upload-cid'
93	};
94	this.storage = 'disk';
95	this.fileToAttach = {};
96	this.xmlToAttach = {};
97	this.events = {
98		onInit : 'DiskDLoadFormControllerInit',
99		onShow : 'DiskLoadFormController',
100		onBound : 'DiskLoadFormControllerWasBound'};
101};
102diskController.prototype = {
103	parser : false,
104	eventNode : null,
105	values : {},
106	initialized : false,
107	functionsToExec : [],
108	exec : function(func, args)
109	{
110		if (typeof func == "function")
111			this.functionsToExec.push([func, args]);
112		if (this.handler && this.handler !== true)
113		{
114			var res;
115			while((res = this.functionsToExec.shift()) && res)
116				res[0].apply(this, res[1]);
117		}
118	},
119	init : function()
120	{
121		if (this.initialized !== true)
122		{
123			this.values = {};
124			this.functionsToExec = [];
125			this.initialized = true;
126			this.bindMainEvents(this.manager);
127			if (this.parser !== null)
128			{
129				this.bindEvents(this.manager);
130				return this.initValues();
131			}
132		}
133		return false;
134	},
135	initValues : function()
136	{
137		var values = BX.findChildren(this.node, {'className' : this.props.valueEditClassName}, true);
138		if (values && values.length > 0)
139		{
140			this.exec(this.runCheckText);
141			return true;
142		}
143		return false;
144	},
145	bindMainEvents : function(manager)
146	{
147		var __status = null;
148		BX.addCustomEvent(manager.eventNode, 'onReinitializeBefore', BX.proxy(this.clean, this));
149		// Pass event to show/hide controller
150		BX.addCustomEvent(manager.eventNode, 'onShowControllers', BX.proxy(function(status) {
151			__status = status;
152			BX.onCustomEvent(manager.eventNode, this.events.onShow, [status]);
153		}, this));
154
155		if (!repo.controller[this.id]) // in case controller has not bound yet
156		{
157			var func = BX.delegate(function(obj) {
158				if (obj["UID"] == this.id || obj["id"] == this.id)
159				{
160					if (__status === 'show' || __status === 'hide')
161					{
162						BX.onCustomEvent(manager.eventNode, this.events.onShow, [__status]);
163						__status = null;
164					}
165					BX.removeCustomEvent(window, this.events.onBound, func);
166				}
167			}, this);
168			BX.addCustomEvent(window, this.events.onBound, func);
169		}
170		if (BX["DD"] && this.storage == 'disk')
171		{
172			var dndF = BX.delegate(function(params)
173			{
174				if ((params["UID"] == this.id || params["id"] == this.id) &&
175					this.manager["dropZoneExists"] !== true)
176				{
177					BX.removeCustomEvent(window, this.events.onBound, dndF);
178					this.manager["dropZoneExists"] = true;
179
180					var controller = this.eventNode,
181						id = this.id;
182
183					diskController.dndCatcher = (diskController.dndCatcher || {});
184					diskController.dndCatcher[id] = {
185						"catch" : true,
186						files : [],
187						dropZone : null,
188						dropZoneMicro : null,
189						initdrag : BX.delegate(function()
190						{
191							diskController.dndCatcher[id].dropZone = new BX.DD.dropFiles(manager.eventNode);
192
193							BX.addCustomEvent(manager.eventNode, "OnImageDataUriHandle", BX.delegate(function(editor, imageBase64)
194							{
195								if (BX["UploaderUtils"])
196								{
197									var blob = BX.UploaderUtils.dataURLToBlob(imageBase64.src);
198									if (blob && blob.size > 0 && blob.type.indexOf("image/") == 0)
199									{
200										blob.name = (blob.name || imageBase64.title || ("image." + blob.type.substr(6)));
201										blob.referrerToEditor = imageBase64;
202										if (diskController.dndCatcher[id]["catch"] === true)
203											diskController.dndCatcher[id]["drop"]([blob]);
204										else if (this.handler && this.handler["addFile"])
205											this.handler.addFile(blob);
206
207										BX.onCustomEvent(editor, "OnImageDataUriCaught", [imageBase64]);
208									}
209								}
210							}, this));
211
212							BX.addCustomEvent(diskController.dndCatcher[id].dropZone, "dropFiles", diskController.dndCatcher[id]["drop"]);
213							BX.addCustomEvent(diskController.dndCatcher[id].dropZone, "dragEnter", diskController.dndCatcher[id]["dragover"]);
214							BX.addCustomEvent(diskController.dndCatcher[id].dropZone, "dragLeave", diskController.dndCatcher[id]["dragleave"]);
215
216							if (BX('micro' + manager.__divId))
217							{
218								diskController.dndCatcher[id].dropZoneMicro = new BX.DD.dropFiles(BX('micro' + manager.__divId));
219								BX.addCustomEvent(diskController.dndCatcher[id].dropZoneMicro, "dragEnter", function(){
220									BX.onCustomEvent(manager.eventNode, 'OnShowLHE', ['justShow'])
221								});
222							}
223							BX.unbind(document, "dragover", diskController.dndCatcher[id]["initdrag"]);
224
225							BX.onCustomEvent(manager.eventNode, "onDropZoneExists", []);
226						}, this),
227						dragover : BX.delegate(function() {
228							BX.addClass(manager.eventNode, "feed-add-post-dnd-over");
229							BX.onCustomEvent(manager.eventNode, "dragover", []);
230						}, this),
231						dragleave : BX.delegate(function() {
232							BX.removeClass(manager.eventNode, "feed-add-post-dnd-over");
233							BX.onCustomEvent(manager.eventNode, "dragleave", []);
234						}, this),
235						dragenterwindow : BX.delegate(function() {
236							BX.addClass(manager.eventNode, "feed-add-post-dnd-ready");
237							if (BX('micro' + manager.__divId)) {
238								BX.addClass(BX('micro' + manager.__divId), "feed-add-post-micro-dnd-ready");
239							}
240							BX.onCustomEvent(manager.eventNode, "dragenterwindow", []);
241						}, this),
242						dragleavewindow : BX.delegate(function(e) {
243							BX.removeClass(manager.eventNode, "feed-add-post-dnd-ready");
244							if (BX('micro' + manager.__divId)) {
245								BX.removeClass(BX('micro' + manager.__divId), "feed-add-post-micro-dnd-ready");
246							}
247							BX.onCustomEvent(manager.eventNode, "dragleavewindow", []);
248						}, this),
249						drop : BX.delegate(function(files)
250						{
251							BX.onCustomEvent(manager.eventNode, "drop", []);
252							BX.onCustomEvent(window, "dragWindowLeave");
253							BX.onCustomEvent(window, "__dragWindowLeave");
254							var result = 0;
255							if (files && files.length > 0)
256							{
257								if (diskController.dndCatcher[id]["catch"] === true)
258								{
259									diskController.dndCatcher[id].files = files;
260									result = 1
261								}
262								else
263								{
264									result = 2;
265								}
266								BX.onCustomEvent(manager.eventNode, this.events.onShow, ['show']);
267								BX.removeClass(manager.eventNode, "feed-add-post-dnd-ready feed-add-post-dnd-over");
268							}
269							return result;
270						}, this)
271					};
272
273					BX.ready(function(){ diskController.dndCatcher[id]["initdrag"](); } );
274
275					BX.addCustomEvent(controller, "OnIframeDrop", BX.delegate(function(e) {
276						BX.PreventDefault(e);
277						if (e["dataTransfer"] && e["dataTransfer"]["files"])
278						{
279							if (diskController.dndCatcher[id].drop(e["dataTransfer"]["files"]) === 2)
280							{
281								this.handler.agent.onChange(e["dataTransfer"]["files"]);
282							}
283						}
284					}, this));
285					BX.addCustomEvent(controller, "OnIframeDragOver", diskController.dndCatcher[id].dragover);
286					BX.addCustomEvent(controller, "OnIframeDragLeave", diskController.dndCatcher[id].dragleave);
287					if (!window["bxMpfDndCatcher"])
288					{
289						window["bxMpfDndCatcher"] = true;
290						var
291							isStarted = false,
292							start = function()
293							{
294								if (isStarted === false)
295								{
296									isStarted = true;
297									BX.bind(document, "dragleave", mousemove);
298									BX.bind(document, "dragover", mousemove);
299									BX.bind(document, "mouseout", stop);
300									BX.onCustomEvent(window, "dragWindowEnter");
301								}
302							},
303							stop = function(e)
304							{
305								BX.unbind(document, "dragleave", mousemove);
306								BX.unbind(document, "dragover", mousemove);
307								BX.unbind(document, "mouseout", stop);
308								isStarted = false;
309								BX.onCustomEvent(window, "dragWindowLeave");
310							},
311							mousemove = function(e)
312						{
313							if (mouseTimeout > 0)
314							{
315								clearTimeout(mouseTimeout);
316								mouseTimeout = 0;
317							}
318							BX.fixEventPageXY(e);
319							var fire = true;
320							if (e.pageX > 0 && e.pageY > 0)
321							{
322								var pos = BX.GetWindowSize();
323								if (e.pageY < pos.scrollHeight && e.pageX < pos.scrollWidth)
324								{
325									var top = (e.pageY - pos.scrollTop),
326										left = (e.pageX - pos.scrollLeft);
327									if (0 < top && top < (pos.innerHeight - 20) &&
328										0 < left && left < (pos.innerWidth - 20))
329									{
330										fire = false;
331									}
332								}
333							}
334							if (fire)
335							{
336								stop(e);
337							}
338							else
339							{
340								mouseTimeout = setTimeout(function(){mousemove({type : 'intervalLimit'});}, 100);
341							}
342						},
343							mouseTimeout = 0;
344						BX.bind(document, "dragenter", function(e)
345						{
346							if (window["bxMpfDndCatcher"] > 0)
347							{
348								clearTimeout(window["bxMpfDndCatcher"]);
349							}
350							var isFileTransfer = true;
351							if (e && e["dataTransfer"] && e["dataTransfer"]["types"])
352							{
353								for (var i = 0; i < e["dataTransfer"]["types"].length; i++)
354								{
355									if (e["dataTransfer"]["types"][i] == "Files")
356									{
357										isFileTransfer = true;
358										break;
359									}
360								}
361							}
362							if (isFileTransfer)
363							{
364								start();
365							}
366						});
367						BX.addCustomEvent(window, "__dragWindowLeave", stop);
368						BX.bind(document, "dragover", function(e)
369						{
370							return BX.PreventDefault(e);
371						});
372						BX.bind(document, "drop", function(e)
373						{
374							stop(e);
375							return BX.PreventDefault(e);
376						});
377					}
378					BX.addCustomEvent(window, "dragWindowEnter", diskController.dndCatcher[id].dragenterwindow);
379					BX.addCustomEvent(window, "dragWindowLeave", diskController.dndCatcher[id].dragleavewindow);
380
381					this.__initCatcher = BX.delegate(function(id, uploader) {
382						if (id == this.id)
383						{
384							BX.removeCustomEvent(manager.eventNode, 'onControllerInitialized', this.__initCatcher);
385							uploader.agent.initDropZone(manager.eventNode);
386							if (diskController.dndCatcher[id].files.length > 0)
387							{
388								uploader.agent.onChange(diskController.dndCatcher[id].files);
389								diskController.dndCatcher[id].files = [];
390							}
391							diskController.dndCatcher[id]["catch"] = false;
392
393							this.__initCatcher = null;
394
395						}
396					}, this);
397					BX.addCustomEvent(manager.eventNode, 'onControllerInitialized', this.__initCatcher);
398				}
399			}, this);
400			BX.addCustomEvent(window, this.events.onBound, dndF);
401			if (repo.controller[this.id])
402				dndF(repo.controller[this.id]);
403		}
404
405	},
406	bindEvents : function(manager)
407	{
408		this._catchHandler = BX.delegate(function(handler)
409		{
410			BX.removeCustomEvent(this.eventNode, this.events.onInit, this._catchHandler);
411			this.handler = handler;
412			var node = BX.findChild(BX(manager.formID), {attr: {id: this.props.securityCID}}, true, false);
413			if (node)
414				node.value = this.handler.CID;
415			this.exec();
416			var func = BX.delegate(function() { BX.onCustomEvent(manager.eventNode, "onUploadsHasBeenChanged", arguments); }, this);
417			BX.addCustomEvent(this.handler.agent, "onFileIsInited", func); // new uploader
418			BX.addCustomEvent(this.handler.agent, "ChangeFileInput", func); // old uploader
419			BX.onCustomEvent(manager.eventNode, 'onControllerInitialized', [this.id, handler]);
420		}, this);
421		if (typeof this.handler != "object" || !this.handler)
422			BX.addCustomEvent(manager.eventNode, this.events.onInit, this._catchHandler);
423		else
424			this._catchHandler(this.handler);
425
426		BX.addCustomEvent(manager.eventNode, 'OnFileUploadSuccess', BX.delegate(function(result, obj, blob) {if (this.id == obj.CID || this.id == obj.id) { blob = (blob || {}); blob.usePostfix = true; this.addFile(result, blob, obj); } }, this));
427		BX.addCustomEvent(manager.eventNode, 'OnFileUploadFailed', BX.delegate(function(result, obj, blob) { if (this.id == obj.CID || this.id == obj.id) { this.failFile(result, blob, obj); } }, this));
428		BX.addCustomEvent(manager.eventNode, 'OnFileUploadRemove', BX.delegate(function(result, obj) { if (this.id == obj.CID || this.id == obj.id) { this.deleteFile(result, {usePostfix : true}, obj); } }, this));
429		BX.addCustomEvent(this, "onFileIsInText", BX.proxy(function(file, inText) { this.adjustFile(this.checkFile(file), inText) }, this));
430	},
431	addFile : function(result, blob, obj)
432	{
433		var file = this.checkFile(result.element_id, result, blob);
434		if (file)
435		{
436			setTimeout(BX.proxy(function(){
437				this.bindFile(file);
438				this.adjustFile(file, false);
439			}, this), 100);
440			BX.onCustomEvent(this.eventNode, 'onFileIsAdded', [file, this, obj, blob]);
441		}
442		else
443		{
444			this.failFile(this, blob, obj);
445		}
446		return true;
447	},
448	failFile : function(node, blob, obj)
449	{
450		BX.onCustomEvent(this.eventNode, 'onFileIsFailed', [this, obj, blob]);
451	},
452	checkFile : function(id, result)
453	{
454		id = '' + (typeof id == "object" ? id.id : id);
455		if (typeof result == "object" &&
456			result !== null && id &&
457			result.element_name &&
458			BX(result.place))
459		{
460			var data = {
461					id : id,
462					name : result.element_name,
463					url : result.element_url,
464					type : 'isnotimage/xyz',
465					isImage : false,
466					place : BX(result.place, true),
467					xmlID : BX(result.place, true).getAttribute("bx-attach-xml-id"),
468					fileID : BX(result.place, true).getAttribute("bx-attach-file-id")
469				},
470				preview;
471			if (/(\.png|\.jpg|\.jpeg|\.gif|\.bmp)$/i.test(result.element_name) &&
472				(preview = BX.findChild(data.place, {'className': 'files-preview', 'tagName' : 'IMG'}, true, false)) && preview)
473			{
474				data.type = 'image/xyz';
475				data.lowsrc = preview.src;
476				data.element_url = data.src = preview.src.replace(/\Wwidth=(\d+)/, '').replace(/\Wheight=(\d+)/, '');
477				data.isImage = true;
478				data.width = parseInt(preview.getAttribute("data-bx-full-width") || preview.getAttribute("data-bx-width"));
479				data.height = parseInt(preview.getAttribute("data-bx-full-height") || preview.getAttribute("data-bx-height"));
480			}
481			if (data.xmlID)
482				this.xmlToAttach[data.xmlID + ''] = id;
483			if (data.fileID)
484				this.fileToAttach[data.fileID + ''] = id;
485
486			this.values[id] = data;
487		}
488		return (this.values[id] || false);
489	},
490	bindFile : function(file)
491	{
492		var node = file.place;
493		if (typeof file == "object" && node && !node.hasAttribute("bx-file-is-bound"))
494		{
495			var name_wrap = BX.findChild(node, {className: 'f-wrap'}, true, false),
496				img_wrap = BX.findChild(node, {className: 'files-preview'}, true, false);
497			if (name_wrap)
498			{
499				BX.bind(name_wrap, "click", BX.delegate(function(){this.insertFile(file.id);}, this));
500				name_wrap.style.cursor = "pointer";
501				name_wrap.title = BX.message('MPF_FILE');
502			}
503			if (img_wrap)
504			{
505				BX.bind(img_wrap, "click", BX.delegate(function(){this.insertFile(file.id);}, this));
506			}
507		}
508	},
509	adjustFile : function(file, inText)
510	{
511		var node = file.place;
512		if (inText === true || inText === false)
513		{
514			if (!file.info)
515				file.info = BX.findChild(file.place, {className: 'files-info'}, true, false);
516			node = file.info;
517			if (BX.type.isDomNode(node))
518			{
519				var id = 'check-in-text-' + file.id,
520					button = BX(id),
521					props = (inText === false ? {
522						attrs : {
523							'bx-file-is-in-text' : "N"
524						},
525						props : {
526							className : 'insert-btn'
527						},
528						html : '<span class="insert-btn-text">' + BX.message("MPF_FILE_INSERT_IN_TEXT") + '</span>'
529					} : {
530						attrs : {
531							'bx-file-is-in-text' : "Y"
532						},
533						props : {
534							className : 'insert-text'
535						},
536						html : '<span class="insert-btn-text">' + BX.message("MPF_FILE_IN_TEXT") + '</span>'
537					});
538				if (!button)
539				{
540					props.attrs.id = id;
541					props.events = {
542						click : BX.proxy(function(){this.insertFile(file.id);}, this)
543					};
544					node.appendChild(BX.create('SPAN', props));
545				}
546				else
547				{
548					BX.adjust(button, props);
549				}
550			}
551		}
552	},
553	insertFile : function(file)
554	{
555		BX.onCustomEvent(this.eventNode, 'onFileIsInserted', [this.checkFile(file), this]);
556	},
557	deleteFile : function(file, params)
558	{
559		file = this.checkFile(file, params);
560		if (file)
561		{
562			BX.onCustomEvent(this.eventNode, 'onFileIsDeleted', [file, this]);
563			this.values[file.id].place = null;
564			delete this.values[file.id].place;
565			this.values[file.id] = null;
566			delete this.values[file.id];
567			file = null;
568			return true;
569		}
570		return false;
571	},
572	reinitValues : function(text, files) // when data needs to be generated
573	{
574		var id, node, data = {};
575		while ((id = files.pop()) && id)
576		{
577			node = BX(this.prefixHTMLNode + id);
578			node = (node ? (node.tagName == "A" ? node : BX.findChild(node, {tagName : "IMG"}, true)) : null);
579			if (node)
580			{
581				data['E' + id] = {
582					type: 'file',
583					id: id,
584					name: node.getAttribute("data-bx-title"),
585					size: node.getAttribute("data-bx-size"),
586					sizeInt: node.getAttribute("data-bx-size"),
587					width: node.getAttribute("data-bx-width"),
588					height: node.getAttribute("data-bx-height"),
589					storage: 'disk',
590					previewUrl: (node.tagName == "A" ? '' : node.getAttribute("data-bx-src")),
591					fileId: node.getAttribute("bx-attach-file-id")
592				};
593				if (node.hasAttribute("bx-attach-xml-id"))
594					data['E' + id]["xmlId"] = node.getAttribute("bx-attach-xml-id");
595			}
596		}
597		this.handler.selectFile({}, {}, data);
598		this.runCheckText();
599	},
600	runCheckText : function()
601	{
602		if (!this._checkText)
603			this._checkText = BX.delegate(this.checkText, this);
604		this.manager.exec(this._checkText);
605	},
606	checkText : function()
607	{
608		var
609			text1, text = this.manager.getContent(),
610			needToReparse = [], reg, ii;
611		if (text != '')
612		{
613			text1 = text;
614			for (ii in this.xmlToAttach)
615			{
616				if (this.xmlToAttach.hasOwnProperty(ii))
617				{
618					text = text.
619						replace(
620							new RegExp('\\&\\#91\\;DOCUMENT ID=(' + ii + ')([WIDTHHEIGHT=0-9 ]*)\\&\\#93\\;','gim'),
621							'[' + this.parser["tag"] + '=' + this.xmlToAttach[ii] + "$2]").
622						replace(
623							new RegExp('\\[DOCUMENT ID=(' + ii + ')([WIDTHHEIGHT=0-9 ]*)\\]','gim'),
624							'[' + this.parser["tag"] + '=' + this.xmlToAttach[ii] + "$2]");
625				}
626			}
627			for (ii in this.fileToAttach)
628			{
629				if (this.fileToAttach.hasOwnProperty(ii))
630				{
631					text = text.
632						replace(
633							new RegExp('\\&\\#91\\;' + this.parser["tag"] + '=(' + ii + ')([WIDTHHEIGHT=0-9 ]*)\\&\\#93\\;','gim'),
634							'[' + this.parser["tag"] + '=' + this.fileToAttach[ii] + "$2]").
635						replace(
636							new RegExp('\\[' + this.parser["tag"] + '=(' + ii + ')([WIDTHHEIGHT=0-9 ]*)\\]','gim'),
637							'[' + this.parser["tag"] + '=' + this.fileToAttach[ii] + "$2]");
638				}
639			}
640			reg = new RegExp('(?:\\&\\#91\\;)(' + this.parser["tags"].join("|") + ')=([a-z=0-9 ]+)(?:\\&\\#93\\;)','gim');
641			if (reg.test(text))
642			{
643				for (ii in this.values)
644				{
645					if (this.values.hasOwnProperty(ii))
646					{
647						needToReparse.push(ii);
648					}
649				}
650				if (needToReparse.length > 0)
651				{
652					reg = new RegExp('(?:\\&\\#91\\;|\\[)(' + this.parser["tags"].join("|") + ')=(' + needToReparse.join("|") + ')([WIDTHHEIGHT=0-9 ]*)(?:\\&\\#93\\;|\\])','gim');
653					if (reg.test(text))
654						text = text.replace(reg, BX.delegate(function(str, tagName, id, add) { return '[' + tagName + '=' + id + add + ']'; }, this));
655				}
656			}
657
658			if (text1 != text)
659				BX.onCustomEvent(this.eventNode, 'onFileIsDetected', [text, this]);
660		}
661		return text;
662	},
663	clean : function()
664	{
665		if (this.handler && this.handler.values)
666		{
667			var res, files, ii, form = BX(this.manager.formID);
668			while ((res = this.handler.values.pop()) && res)
669			{
670				BX.remove(res);
671			}
672			if (this.handler.params && this.handler.params.controlName)
673			{
674				files = BX.findChildren(form, {
675					tagName : "INPUT",
676					attribute : {
677						name : this.handler.params.controlName
678					}
679				}, true);
680			}
681			if (files)
682			{
683				for (ii = 0; ii < files.length; ii++)
684				{
685					BX.remove(files[ii]);
686				}
687			}
688		}
689	},
690	reinit : function(text, data)
691	{
692		var files = [], name, ii;
693		for (name in data)
694		{
695			if (data.hasOwnProperty(name))
696			{
697				if (data[name]['USER_TYPE_ID'] == this.parserName && data[name]['VALUE'])
698				{
699					for (ii in data[name]['VALUE'])
700					{
701						if (data[name]['VALUE'].hasOwnProperty(ii))
702						{
703							files.push(data[name]['VALUE'][ii]);
704						}
705					}
706				}
707			}
708		}
709		if (files.length > 0)
710		{
711			this.exec(this.reinitValues, [text, files]);
712			return true;
713		}
714		return false;
715	}
716};
717var webdavController = function(manager, id, params)
718{
719	webdavController.superclass.constructor.apply(this, arguments);
720	this.parser = (manager.parser['webdav_element'] || null);
721	this.node = BX('wduf-selectdialog-' + id);
722	this.manager = manager;
723	this.parserName = 'webdav_element';
724	this.prefixNode = 'wd-doc';
725	this.prefixHTMLNode = 'wdif-doc-';
726	this.storage = 'webdav';
727	this.events = {
728		onInit : 'WDLoadFormControllerInit',
729		onShow : 'WDLoadFormController',
730		onBound : 'WDLoadFormControllerWasBound'};
731};
732BX.extend(webdavController, diskController);
733webdavController.prototype.reinitValues = function(text, files) // when data needs to be generated
734{
735	var id, node, data = {};
736	this.waitAnswerFromServer = [];
737	while ((id = files.pop()) && id)
738	{
739		node = BX(this.prefixHTMLNode + id);
740		node = (node ? (node.tagName == "A" ? node : BX.findChild(node, {tagName : "IMG"}, true)) : null);
741		if (node)
742		{
743			data['E' + id] = {
744				type: 'file',
745				id: id,
746				name: node.getAttribute("alt"),
747				storage: 'webdav',
748				size: node.getAttribute("data-bx-size"),
749				sizeInt: 1,
750				ext: '',
751				link: node.getAttribute("data-bx-document")
752			};
753			if (node.hasAttribute("bx-attach-xml-id"))
754				data['E' + id]["xmlId"] = node.getAttribute("bx-attach-xml-id");
755			this.waitAnswerFromServer.push(id);
756		}
757	}
758	if (this.waitAnswerFromServer.length > 0)
759	{
760		if (!this._defferCheckText)
761			this._defferCheckText = BX.delegate(this.defferCheckText, this);
762		BX.addCustomEvent(this.eventNode, 'OnFileUploadSuccess', this._defferCheckText);
763		this.handler.WDFD_SelectFile({}, {}, data);
764	}
765};
766webdavController.prototype.defferCheckText = function(result)
767{
768	var key = BX.util.array_search(result.element_id, this.waitAnswerFromServer);
769	if (key >= 0)
770	{
771		this.runCheckText();
772		this.waitAnswerFromServer = BX.util.deleteFromArray(this.waitAnswerFromServer, key);
773	}
774	if (this.waitAnswerFromServer.length <= 0)
775		BX.removeCustomEvent(this.eventNode, 'OnFileUploadSuccess', this._defferCheckText);
776};
777var fileController = function(manager, id, params)
778{
779	fileController.superclass.constructor.apply(this, arguments);
780	this.parser = (manager.parser['file'] ? manager.parser['file'] : (manager.parser['postimage']['exist'] ? manager.parser['postimage'] : null));
781	this.postfix = (params['postfix'] || '');
782	this.node = BX('file-selectdialog-' + id);
783	this.parserName = 'file';
784	this.prefixNode = 'wd-doc';
785	this.prefixHTMLNode = 'file-doc-';
786	this.props = {
787		valueEditClassName : 'file-inline-file',
788		securityCID : 'upload-cid'
789	};
790	this.storage = 'bfile';
791	this.events = {
792		onInit : 'BFileDLoadFormControllerInit',
793		onShow : 'BFileDLoadFormController',
794		onBound : 'BFileDLoadFormControllerWasBound'};
795};
796BX.extend(fileController, diskController);
797fileController.prototype.initValues = function(result)
798{
799	var values;
800	if (result !== true)
801	{
802		values = BX.findChildren(this.node, {'className' : this.props.valueEditClassName}, true);
803		if (values && values.length > 1)
804		{
805			this.exec(this.initValues, [true]);
806			return true;
807		}
808		return false;
809	}
810	values = (this.handler.agent.values || []);
811	var
812		file, node, data, id,
813		ID = {},
814		url = '/bitrix/components/bitrix/main.file.input/file.php?mfi_mode=down&cid='+this.handler.CID + '&sessid='+BX.bitrix_sessid();
815	for (var ii = 0; ii < values.length; ii++)
816	{
817		id = parseInt(values[ii].getAttribute("id").replace(this.prefixNode, ""));
818		if (ID['id' + id])
819			continue;
820		ID['id' + id] = "Y";
821		if (id > 0)
822		{
823			node = BX.findChild(values[ii], {'className': 'f-wrap'}, true, false);
824			if(!node)
825				continue;
826			data = {
827				element_id : id,
828				element_name : node.innerHTML,
829				parser : this.parser.bxTag,
830				storage : 'bfile',
831				element_url : (url + '&fileID=' + id)
832			};
833			file = this.addFile(data, {usePostfix : true, hasPreview : false});
834		}
835	}
836	this.runCheckText();
837	return true;
838};
839fileController.prototype.checkFile = function(id, result, param)
840{
841	id = '' + (typeof id == "object" ? id.id : id);
842	id = id + (param && param["usePostfix"] === true ? this.postfix : '');
843	if (typeof result == "object" &&
844		result !== null && id &&
845		result.element_name &&
846		BX(this.prefixNode + result.element_id, true))
847	{
848		var data = {
849				id : id,
850				name : result.element_name,
851				url : result.element_url,
852				type : 'isnotimage/xyz',
853				isImage : false,
854				place : BX(this.prefixNode + result.element_id, true)
855			},
856			preview;
857		if ((result['element_type'] && result['element_type'].indexOf('image/') === 0 || /(\.png|\.jpg|\.jpeg|\.gif|\.bmp)$/i.test(result.element_name)) &&
858			((preview = BX.findChild(data.place, {'tagName' : 'IMG'}, true, false)) && preview || (param && param["hasPreview"] === false)))
859		{
860			data.type = 'image/xyz';
861			data.src = (result['element_thumbnail'] || result['element_url']);
862			data.isImage = true;
863			data.hasPreview = false;
864			data.lowsrc = '';
865			data.width = '';
866			data.height = '';
867			if (BX(preview))
868			{
869				data.hasPreview = true;
870				data.lowsrc = (result['element_thumbnail'] || preview['src']);
871				data.width = parseInt(preview.getAttribute("data-bx-full-width"));
872				data.height = parseInt(preview.getAttribute("data-bx-full-height"));
873			}
874		}
875		else if (this.parser.bxTag == 'postimage')
876		{
877			return false;
878		}
879
880		this.values[id] = data;
881	}
882	return (this.values[id] || false);
883};
884fileController.prototype.bindFile = function(file)
885{
886	var node = (file && file['place'] ? file['place'] : null);
887	if (typeof file == "object" && node && !node.hasAttribute("bx-file-is-bound"))
888	{
889		if (file.isImage && file.hasPreview)
890		{
891			var
892				img_title = BX.findChild(node, {className: 'feed-add-img-title'}, true, false),
893				img_wrap = BX.findChild(node, {className: 'feed-add-img-wrap'}, true, false);
894			if (img_wrap)
895			{
896				BX.bind(img_wrap, "click", BX.proxy(function(){this.insertFile(file);}, this));
897				img_wrap.style.cursor = "pointer";
898				img_wrap.title = BX.message('MPF_IMAGE');
899			}
900			if (img_title)
901			{
902				BX.bind(img_title, "click", BX.delegate(function(){this.insertFile(file);}, this));
903				img_title.style.cursor = "pointer";
904				img_title.title = BX.message('MPF_IMAGE');
905			}
906		}
907		else
908			fileController.superclass.bindFile.apply(this, arguments);
909	}
910};
911fileController.prototype.clean = function()
912{
913	fileController.superclass.clean.apply(this, arguments);
914	if (this["handler"] && this.handler["agent"] && this.handler.agent["inputName"])
915	{
916		var files, ii, form = BX(this.manager.formID);
917		files = BX.findChildren(form, {
918			tagName : "INPUT",
919			attribute : {
920				name : this.handler.agent.inputName + "[]"
921			}
922		}, true);
923		if (files)
924		{
925			for (ii = 0; ii < files.length; ii++)
926			{
927				BX.remove(files[ii]);
928			}
929		}
930	}
931};
932
933var LHEPostForm = function(formID, params)
934{
935	this.params = params;
936	this.formID = formID;
937	this.showPinButton = !!BX('lhe_button_editor_' + this.formID);
938	if(this.showPinButton)
939	{
940		this.params.showPanelEditor = !!this.params.pinEditorPanel;
941	}
942	this.oEditorId = params['LHEJsObjId'];
943	this.__divId = (params['LHEJsObjName'] || params['LHEJsObjId']);
944	repo.handler[this.oEditorId] = this;
945	this.oEditor = LHEPostForm.getEditor(this.oEditorId);
946	this.urlPreview = this.initUrlPreview(params);
947
948	this.eventNode = BX('div' + this.__divId);
949	BX.addCustomEvent(this.eventNode, 'OnShowLHE', BX.delegate(this.OnShowLHE, this));
950	BX.addCustomEvent(this.eventNode, 'OnButtonClick', BX.delegate(this.OnButtonClick, this));
951	BX.addCustomEvent(this.eventNode, 'OnAfterShowLHE', function(status, handler) {
952		if (handler.oEditor && handler.oEditor["AllowBeforeUnloadHandler"])
953			handler.oEditor.AllowBeforeUnloadHandler();
954		if (handler.monitoringWakeUp === true)
955			handler.monitoringStart();
956	});
957	BX.addCustomEvent(this.eventNode, 'OnAfterHideLHE', function(status, handler) {
958		handler.monitoringWakeUp = handler.monitoringStop();
959		if (handler.oEditor && handler.oEditor["DenyBeforeUnloadHandler"])
960			handler.oEditor.DenyBeforeUnloadHandler();
961	});
962	this.initParsers(params);
963	this.initFiles(formID, params);
964
965	BX.ready(
966		BX.delegate(
967			function()
968			{
969				if (BX('lhe_button_submit_' + formID, true))
970				{
971					BX.bind(BX('lhe_button_submit_' + formID, true), 'mousedown', function(){ BX.addClass(this, 'feed-add-button-press'); });
972					BX.bind(BX('lhe_button_submit_' + formID, true), 'mouseup', function(){ BX.removeClass(this, 'feed-add-button-press'); });
973					BX.bind(BX('lhe_button_submit_' + formID, true), 'click', BX.proxy(function(e){
974						BX.onCustomEvent(this.eventNode, 'OnButtonClick', ['submit']);
975						return BX.PreventDefault(e);
976					}, this));
977				}
978				if (BX('lhe_button_cancel_' + formID, true))
979				{
980					BX.bind(BX('lhe_button_cancel_' + formID, true), 'mousedown', function(){ BX.addClass(this, 'feed-add-button-press'); });
981					BX.bind(BX('lhe_button_cancel_' + formID, true), 'mouseup', function(){ BX.removeClass(this, 'feed-add-button-press'); });
982					BX.bind(BX('lhe_button_cancel_' + formID, true), 'click', BX.proxy(function(e){
983						BX.onCustomEvent(this.eventNode, 'OnButtonClick', ['cancel']);
984						return BX.PreventDefault(e);
985					}, this));
986				}
987			},
988			this
989		)
990	);
991
992	this.inited = true;
993	BX.addCustomEvent(BX(this.formID), 'onAutoSavePrepare', function(ob) { ob.FORM.setAttribute("bx-lhe-autosave-prepared", "Y"); });
994
995	BX.onCustomEvent(this, "onInitialized", [this, formID, params, this.parsers]);
996	BX.onCustomEvent(this.eventNode, "onInitialized", [this, formID, params, this.parsers]);
997	if (this.oEditor && this.oEditor.inited && !this.oEditor['__lhe_flags'])
998	{
999		BX.onCustomEvent(this.oEditor, "OnEditorInitedBefore", [this.oEditor]);
1000		BX.onCustomEvent(this.oEditor, "OnEditorInitedAfter", [this.oEditor, true]);
1001	}
1002};
1003LHEPostForm.prototype = {
1004	editorIsLoaded : false,
1005	arFiles : {},
1006	parser : {},
1007	controllers : {},
1008	exec : function(func, args)
1009	{
1010		this.functionsToExec = (this.functionsToExec || []);
1011		if (typeof func == "function")
1012			this.functionsToExec.push([func, args]);
1013		if (this.editorIsLoaded === true)
1014		{
1015			var res;
1016			while((res = this.functionsToExec.shift()) && res)
1017				res[0].apply(this, res[1]);
1018		}
1019	},
1020	initParsers : function(params)
1021	{
1022		this.parser = {
1023			postimage : {
1024				exist : false,
1025				bxTag : 'postimage',
1026				tag : "IMG ID",
1027				tags : ["IMG ID"],
1028				regexp : /\[(IMG ID)=((?:\s|\S)*?)(?:\s*?WIDTH=(\d+)\s*?HEIGHT=(\d+))?\]/ig,
1029				code : '[IMG ID=#ID##ADDITIONAL#]',
1030				wysiwyg : '<img id="#ID#" src="' + '#SRC#" lowsrc="' + '#LOWSRC#" title=""#ADDITIONAL# />'
1031			}
1032		};
1033		var parsers = (params["parsers"] ? params["parsers"] : {});
1034		for (var ii in parsers)
1035		{
1036			if (parsers.hasOwnProperty(ii) && /[a-z]/gi.test(ii+''))
1037			{
1038				this.parser[ii] = new parserClass(ii, parsers[ii]);
1039			}
1040		}
1041		if (BX.util.object_search('UploadImage', parsers))
1042		{
1043			this.parser['postimage']['exist'] = true;
1044		}
1045		if (typeof params['arSize'] == "object")
1046		{
1047			var style = '';
1048			if (params['arSize']['width'])
1049				style += 'max-width:' + params['arSize']['width'] + 'px;';
1050			if (params['arSize']['height'])
1051				style += 'max-height:' + params['arSize']['height'] + 'px;';
1052			if (style !== '')
1053				this.parser['postimage']['wysiwyg'] = this.parser['postimage']['wysiwyg'].replace('#ADDITIONAL#', 'style="' + style + '" #ADDITIONAL#');
1054		}
1055	},
1056	initFiles : function(formID, params)
1057	{
1058		this.arFiles = {};
1059		this.controllers = {
1060			common : {
1061				postfix : "",
1062				storage : "bfile",
1063				parser : "postimage",
1064				node : window,
1065				obj : null,
1066				init : false
1067			}
1068		};
1069		if (!params["CID"] || typeof params["CID"] !== "object")
1070			return;
1071
1072		BX.addCustomEvent(this.eventNode, 'onFileIsAdded', BX.delegate(this.OnFileUploadSuccess, this));
1073		BX.addCustomEvent(this.eventNode, 'onFileIsFailed', BX.delegate(this.OnFileUploadFailed, this));
1074		BX.addCustomEvent(this.eventNode, 'onFileIsDeleted', BX.delegate(this.OnFileUploadRemove, this));
1075		BX.addCustomEvent(this.eventNode, 'onFileIsDetected', BX.delegate(this.setContent, this));
1076		BX.addCustomEvent(this.eventNode, 'onFileIsInserted', BX.delegate(this.insertFile, this));
1077
1078		var parser, cid, init;
1079
1080		for (cid in params["CID"])
1081		{
1082			if (params["CID"].hasOwnProperty(cid))
1083			{
1084				parser = params["CID"][cid]["parser"];
1085				if (parser == 'disk_file')
1086					this.controllers[cid] = new diskController(this, cid, params["CID"][cid]);
1087				else if (parser == 'webdav_element')
1088					this.controllers[cid] = new webdavController(this, cid, params["CID"][cid]);
1089				else if (parser == 'file')
1090					this.controllers[cid] = new fileController(this, cid, params["CID"][cid]);
1091				if (this.controllers[cid] && this.controllers[cid].init() && !init)
1092					init = true;
1093			}
1094		}
1095
1096		BX.ready(
1097			BX.delegate(
1098				function()
1099				{
1100					BX.bind(BX('bx-b-uploadfile-' + formID), 'click', BX.proxy(this.controllerInit, this));
1101					if (init)
1102						this.controllerInit('show');
1103				},
1104				this
1105			)
1106		);
1107	},
1108	controllerInit : function(status)
1109	{
1110		this.controllerInitStatus = (status == 'show' || status == 'hide' ? status : (this.controllerInitStatus == 'show' ? 'hide' : 'show'));
1111		BX.onCustomEvent(this.eventNode, "onShowControllers", [this.controllerInitStatus]);
1112	},
1113	initUrlPreview: function()
1114	{
1115		if(this.params['urlPreviewId'] && window['BXUrlPreview'] && BX(this.params['urlPreviewId']))
1116		{
1117			return new BXUrlPreview(BX(this.params['urlPreviewId']));
1118		}
1119		return null;
1120	},
1121	getContent : function()
1122	{
1123		return (this.oEditor ? this.oEditor.GetContent() : '');
1124	},
1125	setContent : function(text)
1126	{
1127		if (this.oEditor)
1128			this.oEditor.SetContent(text);
1129	},
1130	OnFileUploadSuccess : function(file, controller, uploaded, blob)
1131	{
1132		if (this.controllers[controller.id])
1133		{
1134			var id = controller.parser.bxTag + file.id;
1135			this.arFiles[id] = (this.arFiles[id] || []);
1136			this.arFiles[id].push(controller.id);
1137
1138			if (blob && blob["referrerToEditor"])
1139			{
1140				var regs = this.getFileToInsert(file, controller);
1141				BX.onCustomEvent(blob["referrerToEditor"], "OnImageDataUriCaughtUploaded", [regs]);
1142				BX.onCustomEvent(this.oEditor, "OnImageDataUriCaughtUploaded", [blob["referrerToEditor"], file, regs]);
1143			}
1144			else if (uploaded === true && file.isImage && this.insertImageAfterUpload)
1145			{
1146				if (!this._insertFile)
1147					this._insertFile = BX.delegate(this.insertFile, this);
1148				this.exec(this._insertFile, arguments);
1149			}
1150		}
1151	},
1152	OnFileUploadFailed : function(file, controller, blob)
1153	{
1154		if (blob && blob["referrerToEditor"])
1155		{
1156			BX.onCustomEvent(blob["referrerToEditor"], "OnImageDataUriCaughtFailed", []);
1157			BX.onCustomEvent(this.editor, "OnImageDataUriCaughtFailed", [blob["referrerToEditor"]]);
1158		}
1159	},
1160	OnFileUploadRemove : function(file, controller)
1161	{
1162		if (this.controllers[controller.id])
1163		{
1164			var id = controller.parser.bxTag + file.id;
1165			if (this.arFiles[id])
1166			{
1167				var key = BX.util.array_search(controller.id, this.arFiles[id]);
1168				this.arFiles[id] = BX.util.deleteFromArray(this.arFiles[id], key);
1169				if (!this.arFiles[id] || this.arFiles[id].length <= 0)
1170				{
1171					this.arFiles[id] = null;
1172					delete this.arFiles[id];
1173					if (!this._deleteFile)
1174						this._deleteFile = BX.delegate(this.deleteFile, this);
1175					this.exec(this._deleteFile, arguments);
1176				}
1177			}
1178		}
1179	},
1180
1181	showPanelEditor : function(show, save)
1182	{
1183		if (show == undefined)
1184			show = !this.oEditor.toolbar.IsShown();
1185        //force show toolbar
1186		show = true;
1187		this.params.showPanelEditor = show;
1188		var
1189			button = BX('lhe_button_editor_' + this.formID),
1190			panelClose = BX('panel-close' + this.__divId);
1191
1192		if (panelClose)
1193		{
1194			this.oEditor.dom.cont.appendChild(panelClose);
1195		}
1196
1197		if(show)
1198		{
1199			this.oEditor.dom.toolbarCont.style.opacity = 'inherit';
1200			this.oEditor.toolbar.Show();
1201
1202			if (button)
1203				BX.addClass(button, 'feed-add-post-form-btn-active');
1204
1205			if (panelClose)
1206				panelClose.style.display = '';
1207		}
1208		else
1209		{
1210			this.oEditor.toolbar.Hide();
1211
1212			if (button)
1213				BX.removeClass(button, 'feed-add-post-form-btn-active');
1214
1215			if (panelClose)
1216				panelClose.style.display = 'none';
1217		}
1218		if (save !== false)
1219			BX.userOptions.save('main.post.form', 'postEdit', 'showBBCode', show ? "Y" : "N");
1220	},
1221	monitoring : {
1222		interval : null,
1223		text : '',
1224		savedText : '',
1225		files : [],
1226		savedFiles : []
1227	},
1228	monitoringStart : function()
1229	{
1230		if (this.monitoring.interval === null)
1231		{
1232			if (!this._monitoringStart)
1233			{
1234				this._monitoringStart = BX.delegate(this.checkFilesInText, this);
1235				BX.addCustomEvent(this.oEditor, 'OnContentChanged', BX.proxy(function(text) {
1236					this.monitoring.text = text;
1237				}, this));
1238			}
1239			this.monitoring.interval = setInterval(this._monitoringStart, 1000);
1240		}
1241	},
1242	monitoringStop : function()
1243	{
1244		var ret = (this.monitoring.interval !== null);
1245		if (this.monitoring.interval !== null)
1246			clearInterval(this.monitoring.interval);
1247		this.monitoring.interval = null;
1248		return ret;
1249	},
1250	monitoringSetStatus: function(parser, file, inText)
1251	{
1252		if (this.arFiles[parser + file])
1253		{
1254			var cid;
1255			for (var ii = 0; ii < this.arFiles[parser + file].length; ii++)
1256			{
1257				this.monitoring.files.push(parser + file);
1258				cid = this.arFiles[parser + file][ii];
1259				BX.onCustomEvent(this.controllers[cid], "onFileIsInText", [file, inText]);
1260			}
1261		}
1262	},
1263	checkFilesInText: function()
1264	{
1265		if (this.monitoring.text !== this.monitoring.savedText)
1266		{
1267			this.monitoring.savedText = this.monitoring.text;
1268			this.monitoring.files = [];
1269			var text = this.monitoring.savedText,
1270				ii, closure = function(a,parser) {return function(str, tagName, id) { a.monitoring.files.push([parser, id].join('/')); } };
1271			for (ii in this.parser)
1272			{
1273				if (this.parser.hasOwnProperty(ii))
1274				{
1275					if (!this.parser[ii]["checkFilesInText"])
1276					{
1277						this.parser[ii]["checkFilesInText"] = closure(this, ii);
1278					}
1279					text.replace(
1280						this.parser[ii]["regexp"],
1281						this.parser[ii]["checkFilesInText"]
1282					);
1283				}
1284			}
1285			if (this.monitoring.savedFiles.join(',') != this.monitoring.files.join(','))
1286			{
1287
1288				var files = this.monitoring.files.join('|') + '|', id;
1289				for (ii = 0; ii < this.monitoring.savedFiles.length; ii++)
1290				{
1291					id = this.monitoring.savedFiles[ii];
1292					if (files.indexOf(id + '|') >= 0)
1293						files = files.replace(id + '|', '');
1294					else
1295					{
1296						id = id.split('/');
1297						this.monitoringSetStatus(id[0], id[1], false);
1298					}
1299				}
1300				files = files.substring(0, files.length - 1).split('|');
1301				for (ii = 0; ii < files.length; ii++)
1302				{
1303					id = id = files[ii].split('/');
1304					this.monitoringSetStatus(id[0], id[1], true);
1305				}
1306			}
1307			this.monitoring.savedFiles = this.monitoring.files;
1308			if (this.monitoring.savedFiles.length <= 0)
1309				this.monitoringStop();
1310		}
1311	},
1312	checkFile : function(file, controller) // or fileId and controller or fileId and parser
1313	{
1314		var r = false;
1315		if (typeof file == 'string')
1316		{
1317			var parser = (typeof controller == 'string' ? controller : controller.parser);
1318
1319			if (!!this.arFiles[parser + file])
1320			{
1321				var fileController = this.arFiles[parser + file][0];
1322				controller = this.controllers[fileController];
1323				r = {
1324					file : controller.values[file],
1325					controller : controller
1326				};
1327			}
1328		}
1329		else if (this.controllers[controller.id])
1330		{
1331			r = {
1332				file : file,
1333				controller : controller
1334			};
1335		}
1336		return r
1337	},
1338	insertFile : function(file, controller)
1339	{
1340		var editor = this.oEditor;
1341		if (editor && file)
1342		{
1343			var editorMode = editor.GetViewMode(),
1344				res = this.getFileToInsert(file, controller);
1345
1346			if(editorMode == 'wysiwyg') // WYSIWYG
1347			{
1348				editor.InsertHtml(res.replacement);
1349				setTimeout(BX.delegate(editor.AutoResizeSceleton, editor), 500);
1350				setTimeout(BX.delegate(editor.AutoResizeSceleton, editor), 1000);
1351			}
1352			else if (editorMode == 'code' && editor.bbCode) // BB Codes
1353			{
1354				editor.textareaView.Focus();
1355				editor.textareaView.WrapWith(false, false, res.replacement);
1356			}
1357			res["callback"]();
1358		}
1359	},
1360	getFileToInsert : function(file, controller)
1361	{
1362		var editor = this.oEditor;
1363		if (editor && file)
1364		{
1365			var
1366				fileID = file['id'],
1367				params = '',
1368				parser = controller.parser,
1369				editorMode = editor.GetViewMode(),
1370				pattern = this.parser[parser.bxTag][editorMode];
1371
1372			if (file['isImage'])
1373			{
1374				pattern = (editorMode == "wysiwyg" ? this.parser["postimage"][editorMode] : pattern);
1375				if (file.width > 0 && file.height > 0 && editor.sEditorMode == "html" )
1376				{
1377					params = ' style="width:' + file.width + 'px;height:' + file.height + 'px;" onload="this.style=\' \'"';
1378				}
1379			}
1380
1381			if(editorMode == 'wysiwyg') // WYSIWYG
1382			{
1383				pattern = pattern.
1384					replace("#ID#", editor.SetBxTag(false, {'tag': parser.bxTag, params: {'value' : fileID}})).
1385					replace("#SRC#", file.src).replace("#URL#", file.url).
1386					replace("#LOWSRC#", (file.lowsrc || '')).
1387					replace("#NAME#", file.name).replace("#ADDITIONAL#", params) + '<span>&nbsp;</span>';
1388			}
1389			else if (editorMode == 'code' && editor.bbCode) // BB Codes
1390			{
1391				pattern = pattern.replace("#ID#", fileID).replace("#ADDITIONAL#", "");
1392			}
1393			return {
1394				replacement : pattern,
1395				"callback" : BX.proxy(function(){
1396					this.monitoringSetStatus(controller.parser.bxTag, file.id, true);
1397					this.monitoringStart();
1398				}, this)
1399			}
1400		}
1401		return {
1402			replacement : "",
1403			"callback" : BX.DoNothing
1404		};
1405	},
1406
1407	deleteFile: function(file, controller)
1408	{
1409		var
1410			editor = this.oEditor,
1411			parser = controller.parser,
1412			id = file.id,
1413			content = editor.GetContent();
1414
1415		if (parser && content.indexOf('=' + id) >= 0)
1416		{
1417			if(editor.GetViewMode() == 'wysiwyg') // WYSIWYG
1418			{
1419				var doc = editor.GetIframeDoc(), ii, n;
1420				for (ii in editor["bxTags"])
1421				{
1422					if (editor["bxTags"].hasOwnProperty(ii))
1423					{
1424						if (typeof editor.bxTags[ii] == "object" &&
1425							editor.bxTags[ii]["params"] &&
1426							editor.bxTags[ii]["params"]["value"] == file.id)
1427						{
1428							n = doc.getElementById(ii);
1429							if (n)
1430								n.parentNode.removeChild(n);
1431						}
1432					}
1433				}
1434				editor.SaveContent();
1435			}
1436			else
1437			{
1438				content = content.replace(parser.regexp, function(str, tagName, id) { return (id == file.id ? '' : str); } );
1439				editor.SetContent(content);
1440				editor.Focus();
1441			}
1442			this.monitoringSetStatus(parser.bxTag, file.id, false);
1443		}
1444	},
1445	reinit : function(text, data)
1446	{
1447		BX.onCustomEvent(this.eventNode, "onReinitializeBefore", [this, text, data]);
1448		this.arFiles = {};
1449
1450		delete this.monitoringWakeUp;
1451		this.monitoringStop();
1452		this.oEditor.CheckAndReInit(text || '');
1453
1454		BX.onCustomEvent(this.eventNode, "onReinitialize", [this, text, data]);
1455		var cid, needsToInit = false;
1456		for (cid in this.controllers)
1457		{
1458			if (this.controllers.hasOwnProperty(cid))
1459			{
1460				if (this.controllers[cid]['init'] && this.controllers[cid].reinit(text, data))
1461					needsToInit = true;
1462			}
1463		}
1464
1465		this.controllerInit((needsToInit ? 'show' : 'hide'));
1466
1467		if (this.params["~height"])
1468		{
1469			this.oEditor.SetConfigHeight(this.params["~height"]);
1470			this.oEditor.ResizeSceleton();
1471		}
1472
1473		if (this.urlPreview)
1474		{
1475			this.urlPreview.detachUrlPreview();
1476			var urlPreviewId;
1477			for(var uf in data)
1478			{
1479				if(data.hasOwnProperty(uf) && data[uf].hasOwnProperty('USER_TYPE_ID') && data[uf]['USER_TYPE_ID'] === 'url_preview')
1480				{
1481					urlPreviewId = data[uf]['VALUE'];
1482				}
1483			}
1484			if(urlPreviewId)
1485				this.urlPreview.attachUrlPreview({id: urlPreviewId});
1486		}
1487	},
1488	Parse : function(parser, content, editor)
1489	{
1490		var
1491			arParser = this.parser[parser],
1492			obj = this;
1493
1494		if (arParser)
1495		{
1496			content = content.replace(
1497				arParser.regexp,
1498				function(str, tagName, id, width, height)
1499				{
1500					var file = obj.checkFile(id, parser);
1501					if (file && (file = file.file) && file)
1502					{
1503						var
1504							strAdditional = "",
1505							template = (file.isImage ? obj.parser.postimage.wysiwyg : arParser.wysiwyg);
1506						obj.monitoringStart();
1507
1508						if (file.isImage)
1509						{
1510							width = parseInt(width);
1511							height = parseInt(height);
1512
1513							strAdditional = ((width && height) ?
1514								(" width=\"" + width + "\" height=\"" + height + "\"") : "");
1515
1516							if (strAdditional === "" && file["width"] > 0 && file["height"] > 0)
1517							{
1518								strAdditional = ' style="width:' + file["width"] + 'px;height:' + file["height"] + 'px;" onload="this.style=\' \'"';
1519							}
1520						}
1521
1522						return template.
1523							replace("#ID#", editor.SetBxTag(false, {tag: parser, params: {value : id}})).
1524							replace("#NAME#", file.name).
1525							replace("#SRC#", file.src).
1526							replace("#LOWSRC#", file.lowsrc).
1527							replace("#ADDITIONAL#", strAdditional).
1528							replace("#WIDTH#", parseInt(width)).
1529							replace("#HEIGHT#", parseInt(height));
1530					}
1531					return str;
1532				}
1533			)
1534		}
1535		return content;
1536	},
1537
1538	/**
1539	 * @return {string}
1540	 */
1541	Unparse: function(bxTag, oNode/*, editor*/)
1542	{
1543		var res = "", parser = bxTag.tag;
1544		if (this.parser[parser])
1545		{
1546			var
1547				width = parseInt(oNode.node.hasAttribute("width") ? oNode.node.getAttribute("width") : 0),
1548				height = parseInt(oNode.node.hasAttribute("height") ? oNode.node.getAttribute("height") : 0),
1549				strSize = "";
1550
1551			if (width > 0 && height > 0)
1552			{
1553				strSize = ' WIDTH=' + width + ' HEIGHT=' + height;
1554			}
1555
1556			res = this.parser[parser]["code"].
1557				replace("#ID#", bxTag.params.value).
1558				replace("#ADDITIONAL#", strSize).
1559				replace("#WIDTH#", width).
1560				replace("#HEIGHT#", height);
1561		}
1562
1563		return res;
1564	},
1565
1566	OnShowLHE : function(show, editor, setFocus)
1567	{
1568		var lheName = this.__divId;
1569		show = (show === false ? false : (show === 'hide' ? 'hide' : (show === 'justShow' ? 'justShow' : true)));
1570
1571		this.oEditor = (this.oEditor || LHEPostForm.getEditor(this.oEditorId));
1572		if (!this.oEditor)
1573			return;
1574		this.oEditor.Init();
1575
1576		var
1577			micro = BX('micro' + lheName),
1578			div = this.eventNode;
1579
1580		if (micro)
1581		{
1582			micro.style.display = ((show === true || show === 'justShow') ? "none" : "block");
1583		}
1584
1585		if (show == 'hide')
1586		{
1587			BX.onCustomEvent(this.eventNode, 'OnBeforeHideLHE', [show, this]);
1588			if (this.eventNode.style.display == "none")
1589			{
1590				BX.onCustomEvent(this.eventNode, 'OnAfterHideLHE', [show, this]);
1591			}
1592			else
1593			{
1594				(new BX["easing"]({
1595					duration : 200,
1596					start : { opacity: 100, height : this.eventNode.scrollHeight},
1597					finish : { opacity : 0, height : 20},
1598					transition : BX.easing.makeEaseOut(BX.easing.transitions.quad),
1599					step : function(state)
1600					{
1601						div.style.height = state.height + "px";
1602						div.style.opacity = state.opacity / 100;
1603					},
1604					complete : BX.proxy(function()
1605					{
1606						this.eventNode.style.cssText = "";
1607						this.eventNode.style.display = "none";
1608						BX.onCustomEvent(div, 'OnAfterHideLHE', [show, this]);
1609					}, this)
1610				})).animate();
1611			}
1612		}
1613		else if (show)
1614		{
1615			BX.onCustomEvent(this.eventNode, 'OnBeforeShowLHE', [show, this]);
1616			if (show == "justShow")
1617			{
1618				this.eventNode.style.display = "block";
1619				BX.onCustomEvent(this.eventNode, 'OnAfterShowLHE', [show, this]);
1620				if (setFocus !== false)
1621					this.oEditor.Focus();
1622			}
1623			else if (this.eventNode.style.display == "block")
1624			{
1625				BX.onCustomEvent(this.eventNode, 'OnAfterShowLHE', [show, this]);
1626				if (setFocus !== false)
1627					this.oEditor.Focus();
1628			}
1629			else
1630			{
1631				BX.adjust(this.eventNode, {style:{display:"block", overflow:"hidden", height:"20px", opacity:0.1}});
1632				(new BX["easing"]({
1633					duration : 200,
1634					start : { opacity : 10, height : 20 },
1635					finish : { opacity: 100, height : div.scrollHeight},
1636					transition : BX["easing"].makeEaseOut(BX.easing.transitions.quad),
1637					step : function(state)
1638					{
1639						div.style.height = state.height + "px";
1640						div.style.opacity = state.opacity / 100;
1641					},
1642					complete : BX.proxy(function()
1643					{
1644						BX.onCustomEvent(div, 'OnAfterShowLHE', [show, this]);
1645						this.oEditor.Focus();
1646						this.eventNode.style.cssText = "";
1647					}, this)
1648				})).animate();
1649			}
1650		}
1651		else
1652		{
1653			BX.onCustomEvent(this.eventNode, 'OnBeforeHideLHE', [show, this]);
1654			this.eventNode.style.display = "none";
1655			BX.onCustomEvent(this.eventNode, 'OnAfterHideLHE', [show, this]);
1656		}
1657	},
1658
1659	OnButtonClick : function(type)
1660	{
1661		if(type != 'cancel')
1662		{
1663			BX.onCustomEvent(this.eventNode, 'OnClickSubmit', [this]);
1664		}
1665		else
1666		{
1667			BX.onCustomEvent(this.eventNode, 'OnClickCancel', [this]);
1668			BX.onCustomEvent(this.eventNode, 'OnShowLHE', ['hide']);
1669		}
1670	},
1671	OnEditorInitedBefore : function(editor)
1672	{
1673		var _this = this;
1674		this.oEditor = editor;
1675		editor.formID = this.formID;
1676		if (this.params)
1677			this.params["~height"] = editor.config["height"];
1678
1679		BX.addCustomEvent(editor, 'OnCtrlEnter', function() {
1680			editor.SaveContent();
1681			if (_this.params && _this.params['ctrlEnterHandler'] && typeof window[_this.params['ctrlEnterHandler']] == 'function')
1682				window[_this.params['ctrlEnterHandler']]();
1683			else
1684				BX.submit(BX(_this.formID));
1685		});
1686
1687		var parsers = (this.params.parsers ? this.params.parsers : []);
1688
1689		if (BX.util.object_search('Spoiler', parsers))
1690		{
1691			editor.AddButton({
1692				id : 'spoiler',
1693				name : BX.message('spoilerText'),
1694				iconClassName : 'spoiler',
1695				disabledForTextarea : false,
1696				src : BX.message('MPF_TEMPLATE_FOLDER') + '/images/lhespoiler.png',
1697				toolbarSort : 205,
1698				handler : function()
1699				{
1700					var
1701						_this = this,
1702						res = false;
1703
1704					// Iframe
1705					if (!_this.editor.bbCode || !_this.editor.synchro.IsFocusedOnTextarea())
1706					{
1707						res = _this.editor.action.actions.formatBlock.exec('formatBlock', 'blockquote', 'bx-spoiler', false, {bxTagParams : {tag: "spoiler"}});
1708					}
1709					else // bbcode + textarea
1710					{
1711						res = _this.editor.action.actions.formatBbCode.exec('quote', {tag: 'SPOILER'});
1712					}
1713					return res;
1714				}
1715			});
1716			editor.AddParser({
1717				name : 'spoiler',
1718				obj : {
1719					Parse: function(sName, content, pLEditor)
1720					{
1721						if (/\[(cut|spoiler)(([^\]])*)\]/gi.test(content))
1722						{
1723							content = content.
1724								replace(/[\001-\006]/gi, '').
1725								replace(/\[cut(((?:=)[^\]]*)|)\]/gi, '\001$1\001').
1726								replace(/\[\/cut]/gi, '\002').
1727								replace(/\[spoiler([^\]]*)\]/gi, '\003$1\003').
1728								replace(/\[\/spoiler]/gi, '\004');
1729							var
1730								reg1 = /(?:\001([^\001]*)\001)([^\001-\004]+)\002/gi,
1731								reg2 = /(?:\003([^\003]*)\003)([^\001-\004]+)\004/gi,
1732								__replace_reg = function(title, body){
1733									title = title.replace(/^(="|='|=)/gi, '').replace(/("|')?$/gi, '');
1734									return '<blockquote class="bx-spoiler" id="' + pLEditor.SetBxTag(false, {tag: "spoiler"}) + '" title="' + title + '">' + body + '</blockquote>';
1735								},
1736								func = function(str, title, body){return __replace_reg(title, body);};
1737							while (content.match(reg1) || content.match(reg2))
1738							{
1739								content = content.
1740									replace(reg1, func).
1741									replace(reg2, func);
1742							}
1743						}
1744						content = content.
1745							replace(/\001([^\001]*)\001/gi, '[cut$1]').
1746							replace(/\003([^\003]*)\003/gi, '[spoiler$1]').
1747							replace(/\002/gi, '[/cut]').
1748							replace(/\004/gi, '[/spoiler]');
1749						return content;
1750					},
1751					/**
1752					 * @return {string}
1753					 */
1754					UnParse: function(bxTag, oNode)
1755					{
1756						if (bxTag.tag == 'spoiler')
1757						{
1758							var name = '', i;
1759							// Handle childs
1760							for (i = 0; i < oNode.node.childNodes.length; i++)
1761							{
1762								name += editor.bbParser.GetNodeHtml(oNode.node.childNodes[i]);
1763							}
1764							name = BX.util.trim(name);
1765							if (name != '')
1766								return "[SPOILER" + (oNode.node.hasAttribute("title") ? '=' + oNode.node.getAttribute("title") : '')+ "]" + name +"[/SPOILER]";
1767						}
1768						return "";
1769					}
1770				}
1771			});
1772		}
1773		if (BX.util.object_search('MentionUser', parsers))
1774		{
1775			editor.AddParser(
1776				{
1777					name: 'postuser',
1778					obj: {
1779						Parse: function(parserName, content)
1780						{
1781							content = content.replace(/\[USER\s*=\s*(\d+)\]((?:\s|\S)*?)\[\/USER\]/ig,
1782								function(str, id, name)
1783								{
1784									name = BX.util.trim(name);
1785									if (name == '')
1786										return '';
1787									return '<span id="' + editor.SetBxTag(false, {tag: "postuser", params: {value : parseInt(id)}}) + '" class="bxhtmled-metion">' + name + '</span>';
1788								});
1789							return content;
1790						},
1791						/**
1792						 * @return {string}
1793						 */
1794						UnParse: function(bxTag, oNode)
1795						{
1796							if (bxTag.tag == 'postuser')
1797							{
1798								var name = '', i;
1799								// Handle childs
1800								for (i = 0; i < oNode.node.childNodes.length; i++)
1801								{
1802									name += editor.bbParser.GetNodeHtml(oNode.node.childNodes[i]);
1803								}
1804								name = BX.util.trim(name);
1805								if (name != '')
1806									return "[USER=" + bxTag.params.value + "]" + name +"[/USER]";
1807							}
1808							return "";
1809						}
1810					}
1811				}
1812			);
1813		}
1814		var funcParse = function(parserName, content) {
1815				return _this.Parse(parserName, content, editor);
1816			},
1817			funcUnparse = function(bxTag, oNode) {
1818				return _this.Unparse(bxTag, oNode/*, editor*/);
1819			};
1820		for (var parser in this.parser)
1821		{
1822			if (this.parser.hasOwnProperty(parser))
1823			{
1824				editor.AddParser({
1825					name: parser,
1826					obj: {
1827						Parse: funcParse,
1828						UnParse: funcUnparse
1829					}
1830				});
1831			}
1832		}
1833
1834		if (this.showPinButton)
1835		{
1836			this.pinEditorPanel = this.params && this.params.pinEditorPanel === true;
1837			var pinId = 'toolbar_pin';
1838			var but = function (editor, wrap)
1839			{
1840				// Call parrent constructor
1841				but.superclass.constructor.apply(this, arguments);
1842				this.id = pinId;
1843				this.title = BX.message('MPF_PIN_EDITOR_PANNEL');
1844				this.className += ' ' + (_this.pinEditorPanel ? 'bxhtmled-button-toolbar-pined' : 'bxhtmled-button-toolbar-pin');
1845				this.Create();
1846				if (wrap)
1847					wrap.appendChild(this.GetCont());
1848			};
1849
1850			BX.extend(but, window.BXHtmlEditor.Button);
1851			but.prototype.OnClick = function ()
1852			{
1853				BX.removeClass(this.pCont, 'bxhtmled-button-toolbar-pined');
1854				BX.removeClass(this.pCont, 'bxhtmled-button-toolbar-pin');
1855				if (_this.pinEditorPanel)
1856				{
1857					_this.pinEditorPanel = false;
1858					BX.addClass(this.pCont, 'bxhtmled-button-toolbar-pin');
1859				}
1860				else
1861				{
1862					_this.pinEditorPanel = true;
1863					BX.addClass(this.pCont, 'bxhtmled-button-toolbar-pined');
1864				}
1865				BX.userOptions.save('main.post.form', 'postEdit', 'pinEditorPanel', _this.pinEditorPanel ? "Y" : "N");
1866			};
1867
1868			window.BXHtmlEditor.Controls[pinId] = but;
1869			BX.addCustomEvent(editor, "GetControlsMap", function (controlsMap)
1870			{
1871				controlsMap.push({
1872					id: pinId, compact: true, hidden: false, sort: 500, checkWidth: true, offsetWidth: 32, wrap: 'right'
1873				});
1874			});
1875		}
1876	},
1877	OnEditorInitedAfter : function(editor)
1878	{
1879		BX.addCustomEvent(editor, "OnIframeDrop", BX.proxy(function(){BX.onCustomEvent(this.eventNode, "OnIframeDrop", arguments);}, this));
1880		BX.addCustomEvent(editor, "OnIframeDragOver", BX.proxy(function(){BX.onCustomEvent(this.eventNode, "OnIframeDragOver", arguments);}, this));
1881		BX.addCustomEvent(editor, "OnIframeDragLeave", BX.proxy(function(){BX.onCustomEvent(this.eventNode, "OnIframeDragLeave", arguments);}, this));
1882		BX.addCustomEvent(editor, "OnImageDataUriHandle", BX.proxy(function(){BX.onCustomEvent(this.eventNode, "OnImageDataUriHandle", arguments);}, this));
1883
1884		BX.addCustomEvent(editor, "OnAfterUrlConvert", this.OnAfterUrlConvert.bind(this));
1885		BX.addCustomEvent(editor, "OnBeforeCommandExec", this.OnBeforeCommandExec.bind(this));
1886		// Contextmenu changing for images/files
1887		editor.contextMenu.items['postimage'] =
1888			editor.contextMenu.items['postdocument'] =
1889				editor.contextMenu.items['postfile'] =
1890					[
1891						{
1892							TEXT: BX.message('BXEdDelFromText'),
1893							bbMode: true,
1894							ACTION: function()
1895							{
1896								var node = editor.contextMenu.GetTargetItem('postimage');
1897								if (!node)
1898									node = editor.contextMenu.GetTargetItem('postdocument');
1899								if (!node)
1900									node = editor.contextMenu.GetTargetItem('postfile');
1901
1902								if (node && node.element)
1903								{
1904									editor.selection.RemoveNode(node.element);
1905								}
1906								editor.contextMenu.Hide();
1907							}
1908						}
1909					];
1910		if (!this.params["lazyLoad"])
1911		{
1912			BX.onCustomEvent(this.eventNode, 'OnShowLHE', ["justShow", editor, false])
1913		}
1914
1915		if (editor.toolbar.controls && editor.toolbar.controls.FontSelector)
1916		{
1917			editor.toolbar.controls.FontSelector.SetWidth(45);
1918		}
1919
1920		BX.addCustomEvent(BX(this.formID), 'onAutoSavePrepare', function (ob) {
1921			var _ob=ob;
1922			setTimeout(function() {
1923				BX.addCustomEvent(editor, 'OnContentChanged', BX.proxy(function(text) {
1924					this["mpfTextContent"] = text;
1925					this.Init();
1926				}, _ob));
1927			},1500);
1928		});
1929		BX.addCustomEvent(BX(this.formID), 'onAutoSave', BX.proxy(function(ob, form_data)
1930		{
1931			if (BX.type.isNotEmptyString(ob['mpfTextContent']))
1932				form_data['text' + this.formID] = ob['mpfTextContent'];
1933		}, this));
1934		BX.addCustomEvent(BX(this.formID), 'onAutoSaveRestore', BX.proxy(function(ob, form_data)
1935		{
1936			if (form_data['text' + this.formID] && /[^\s]+/gi.test(form_data['text' + this.formID]))
1937			{
1938				editor.CheckAndReInit(form_data['text' + this.formID]);
1939			}
1940		}, this));
1941
1942		if (BX(this.formID) && BX(this.formID).hasAttribute("bx-lhe-autosave-prepared") && BX(this.formID).BXAUTOSAVE)
1943		{
1944			BX(this.formID).removeAttribute("bx-lhe-autosave-prepared");
1945			setTimeout(BX.proxy(function(){ BX(this.formID).BXAUTOSAVE.Prepare(); }, this), 100);
1946		}
1947		var
1948			formID = this.formID,
1949			settings = this.params;
1950
1951		this.showPanelEditor(settings.showPanelEditor, false);
1952
1953		if (!editor.mainPostFormCustomized)
1954		{
1955			editor.mainPostFormCustomized = true;
1956
1957			BX.addCustomEvent(
1958				editor,
1959				'OnIframeKeydown',
1960				function(e)
1961				{
1962					if (window.onKeyDownHandler)
1963					{
1964						window.onKeyDownHandler(e, editor, formID);
1965					}
1966				}
1967			);
1968
1969			BX.addCustomEvent(
1970				editor,
1971				'OnIframeKeyup',
1972				function(e)
1973				{
1974					if (window.onKeyUpHandler)
1975					{
1976						window.onKeyUpHandler(e, editor, formID);
1977					}
1978				}
1979			);
1980
1981			if (window['BXfpdStopMent' + formID])
1982			{
1983				BX.addCustomEvent(
1984					editor,
1985					'OnIframeClick',
1986					function()
1987					{
1988						window['BXfpdStopMent' + formID]();
1989					}
1990				);
1991			}
1992
1993			// Just to avoid version dependence from fileman
1994			if (editor && editor.textareaView.GetCursorPosition)
1995			{
1996				BX.addCustomEvent(
1997					editor,
1998					'OnTextareaKeyup',
1999					function(e)
2000					{
2001						if (window.onTextareaKeyUpHandler)
2002						{
2003							window.onTextareaKeyUpHandler(e, editor, formID);
2004						}
2005					}
2006				);
2007
2008				BX.addCustomEvent(
2009					editor,
2010					'OnTextareaKeydown',
2011					function(e)
2012					{
2013						if (window.onTextareaKeyDownHandler)
2014						{
2015							window.onTextareaKeyDownHandler(e, editor, formID);
2016						}
2017					}
2018				);
2019			}
2020		}
2021	},
2022	OnAfterUrlConvert: function(url)
2023	{
2024		if(this.urlPreview)
2025		{
2026			this.urlPreview.attachUrlPreview({url: url});
2027		}
2028	},
2029	OnBeforeCommandExec: function(isContentAction, action, oAction, value)
2030	{
2031		if(this.urlPreview && action == 'createLink' && BX.type.isPlainObject(value) && value.hasOwnProperty('href'))
2032		{
2033			this.urlPreview.attachUrlPreview({url: value.href});
2034		}
2035	}
2036};
2037LHEPostForm.getEditor = function(editor)
2038{
2039	return (window["BXHtmlEditor"] ? window["BXHtmlEditor"].Get((typeof editor == "object" ? editor.id : editor)) : null);
2040};
2041LHEPostForm.getHandler = function(editor)
2042{
2043	return repo.handler[(typeof editor == "object" ? editor.id : editor)];
2044};
2045LHEPostForm.unsetHandler = function(editor)
2046{
2047	var editorId = (typeof editor == "object" ? editor.id : editor);
2048
2049	if (!repo.handler[editorId])
2050		return;
2051
2052	if (repo.handler[editorId].oEditor)
2053		repo.handler[editorId].oEditor.Destroy();
2054
2055	// TODO: unregister event handlers here
2056
2057	repo.handler[editorId] = null;
2058};
2059LHEPostForm.reinitData = function(editorID, text, data)
2060{
2061	var handler = LHEPostForm.getHandler(editorID);
2062	if (handler)
2063		handler.exec(handler.reinit, [text, data]);
2064	return false;
2065};
2066LHEPostForm.reinitDataBefore = function(editorID)
2067{
2068	var handler = LHEPostForm.getHandler(editorID);
2069	if (handler && handler["eventNode"])
2070		BX.onCustomEvent(handler.eventNode, "onReinitializeBefore", [handler]);
2071};
2072window.LHEPostForm = LHEPostForm;
2073window.BXPostFormTags = function(formID, buttonID)
2074{
2075	this.popup = null;
2076	this.formID = formID;
2077	this.buttonID = buttonID;
2078	this.sharpButton = null;
2079	this.addNewLink = null;
2080	this.tagsArea = null;
2081	this.hiddenField = null;
2082	this.popupContent = null;
2083
2084	BX.ready(BX.proxy(this.init, this));
2085};
2086
2087window.BXPostFormTags.prototype.init = function()
2088{
2089	this.sharpButton = BX(this.buttonID);
2090	this.addNewLink = BX("post-tags-add-new-" + this.formID);
2091	this.tagsArea = BX("post-tags-block-" + this.formID);
2092	this.tagsContainer = BX("post-tags-container-" + this.formID);
2093	this.hiddenField = BX("post-tags-hidden-" + this.formID);
2094	this.popupContent = BX("post-tags-popup-content-" + this.formID);
2095	this.popupInput = BX.findChild(this.popupContent, { tag : "input" });
2096
2097	var tags = BX.findChildren(this.tagsContainer, { className : "feed-add-post-del-but" }, true);
2098	for (var i = 0, cnt = tags.length; i < cnt; i++ )
2099	{
2100		BX.bind(tags[i], "click", BX.proxy(this.onTagDelete, {
2101			obj : this,
2102			tagBox : tags[i].parentNode,
2103			tagValue : tags[i].parentNode.getAttribute("data-tag")
2104		}));
2105	}
2106
2107	BX.bind(this.sharpButton, "click", BX.proxy(this.onButtonClick, this));
2108	BX.bind(this.addNewLink, "click", BX.proxy(this.onAddNewClick, this));
2109};
2110
2111window.BXPostFormTags.prototype.onTagDelete = function()
2112{
2113	BX.remove(this.tagBox);
2114	this.obj.hiddenField.value = this.obj.hiddenField.value.replace(this.tagValue + ',', '').replace('  ', ' ');
2115};
2116
2117window.BXPostFormTags.prototype.show = function()
2118{
2119	if (this.popup === null)
2120	{
2121		this.popup = new BX.PopupWindow("bx-post-tag-popup", this.addNewLink, {
2122			content : this.popupContent,
2123			lightShadow : false,
2124			offsetTop: 8,
2125			offsetLeft: 10,
2126			autoHide: true,
2127			angle : true,
2128			closeByEsc: true,
2129			zIndex: -840,
2130			buttons: [
2131				new BX.PopupWindowButton({
2132					text : BX.message("TAG_ADD"),
2133					events : {
2134						click : BX.proxy(this.onTagAdd, this)
2135					}
2136				})
2137			]
2138		});
2139
2140		BX.bind(this.popupInput, "keydown", BX.proxy(this.onKeyPress, this));
2141		BX.bind(this.popupInput, "keyup", BX.proxy(this.onKeyPress, this));
2142	}
2143
2144	this.popup.show();
2145	BX.focus(this.popupInput);
2146};
2147
2148window.BXPostFormTags.prototype.addTag = function(tagStr)
2149{
2150	var tags = BX.type.isNotEmptyString(tagStr) ? tagStr.split(",") : this.popupInput.value.split(",");
2151	var result = [];
2152	for (var i = 0; i < tags.length; i++ )
2153	{
2154		var tag = BX.util.trim(tags[i]);
2155		if(tag.length > 0)
2156		{
2157			var allTags = this.hiddenField.value.split(",");
2158			if(!BX.util.in_array(tag, allTags))
2159			{
2160				var newTagDelete;
2161				var newTag = BX.create("span", {
2162					children : [
2163						(newTagDelete = BX.create("span", { attrs : { "class": "feed-add-post-del-but" }}))
2164					],
2165					attrs : { "class": "feed-add-post-tags" }
2166				});
2167
2168				newTag.insertBefore(document.createTextNode(tag), newTagDelete);
2169				this.tagsContainer.insertBefore(newTag, this.addNewLink);
2170
2171				BX.bind(newTagDelete, "click", BX.proxy(this.onTagDelete, {
2172					obj : this,
2173					tagBox : newTag,
2174					tagValue : tag
2175				}));
2176
2177				this.hiddenField.value += tag + ',';
2178
2179				result.push(tag);
2180			}
2181		}
2182	}
2183
2184	return result;
2185};
2186
2187window.BXPostFormTags.prototype.onTagAdd = function()
2188{
2189	this.addTag();
2190	this.popupInput.value = "";
2191	this.popup.close();
2192};
2193
2194window.BXPostFormTags.prototype.onAddNewClick = function(event)
2195{
2196	event = event || window.event;
2197	this.show();
2198	BX.PreventDefault(event);
2199};
2200
2201window.BXPostFormTags.prototype.onButtonClick = function(event)
2202{
2203	event = event || window.event;
2204	BX.show(this.tagsArea);
2205	this.show();
2206	BX.PreventDefault(event);
2207};
2208
2209window.BXPostFormTags.prototype.onKeyPress = function(event)
2210{
2211	event = event || window.event;
2212	var key = (event.keyCode ? event.keyCode : (event.which ? event.which : null));
2213	if (key == 13)
2214	{
2215		setTimeout(BX.proxy(this.onTagAdd, this), 0);
2216	}
2217};
2218
2219window.BXPostFormImportant = function(formID, buttonID, inputName)
2220{
2221	if (inputName)
2222	{
2223		this.formID = formID;
2224		this.buttonID = buttonID;
2225		this.inputName = inputName;
2226
2227		this.fireButton = null;
2228		this.activeBlock = null;
2229		this.hiddenField = null;
2230
2231		BX.ready(BX.proxy(this.init, this));
2232	}
2233
2234	return false;
2235};
2236window.BXPostFormImportant.prototype.init = function()
2237{
2238	this.fireButton = BX(this.buttonID);
2239	this.activeBlock = BX(this.buttonID + '-active');
2240
2241	var form = BX(this.formID);
2242	if (form)
2243	{
2244		this.hiddenField = form[this.inputName];
2245		if (
2246			this.hiddenField
2247			&& this.hiddenField.value == 1
2248		)
2249		{
2250			this.showActive();
2251		}
2252	}
2253
2254	BX.bind(this.fireButton, "click", BX.proxy(function(event) {
2255		event = event || window.event;
2256		this.showActive();
2257		BX.PreventDefault(event);
2258	}, this));
2259
2260	BX.bind(this.activeBlock, "click", BX.proxy(function(event) {
2261		event = event || window.event;
2262		this.hideActive();
2263		BX.PreventDefault(event);
2264	}, this));
2265};
2266window.BXPostFormImportant.prototype.showActive = function(event)
2267{
2268	BX.hide(this.fireButton);
2269	BX.show(this.activeBlock, 'inline-block');
2270
2271	if (this.hiddenField)
2272	{
2273		this.hiddenField.value = 1;
2274	}
2275
2276	return false;
2277};
2278window.BXPostFormImportant.prototype.hideActive = function(event)
2279{
2280	BX.hide(this.activeBlock);
2281	BX.show(this.fireButton, 'inline-block');
2282
2283	if (this.hiddenField)
2284	{
2285		this.hiddenField.value = 0;
2286	}
2287
2288	return false;
2289};
2290
2291var lastWaitElement = null;
2292window.MPFbuttonShowWait = function(el)
2293{
2294	if (el && !BX.type.isElementNode(el))
2295		el = null;
2296	el = el || this;
2297	el = (el ? (el.tagName == "A" ? el : el.parentNode) : el);
2298	if (el)
2299	{
2300		BX.addClass(el, "feed-add-button-load");
2301		lastWaitElement = el;
2302		BX.defer(function(){el.disabled = true})();
2303	}
2304};
2305
2306window.MPFbuttonCloseWait = function(el)
2307{
2308	if (el && !BX.type.isElementNode(el))
2309		el = null;
2310	el = el || lastWaitElement || this;
2311	if (el)
2312	{
2313		el.disabled = false ;
2314		BX.removeClass(el, 'feed-add-button-load');
2315		lastWaitElement = null;
2316	}
2317};
2318
2319window.__mpf_wd_getinfofromnode = function(result, obj)
2320{
2321	var preview = BX.findChild(BX((result["prefixNode"] || 'wd-doc') + result.element_id), {'className': 'files-preview', 'tagName' : 'IMG'}, true, false);
2322	if (preview)
2323	{
2324		result.lowsrc = preview.src;
2325		result.element_url = preview.src.replace(/\Wwidth=(\d+)/, '').replace(/\Wheight\=(\d+)/, '');
2326		result.width = parseInt(preview.getAttribute("data-bx-full-width"));
2327		result.height = parseInt(preview.getAttribute("data-bx-full-height"));
2328	}
2329	else if (obj.urlGet)
2330	{
2331		result.element_url = obj.urlGet.
2332			replace("#element_id#", result.element_id).
2333			replace("#ELEMENT_ID#", result.element_id).
2334			replace("#element_name#", result.element_name).
2335			replace("#ELEMENT_NAME#", result.element_name);
2336	}
2337};
2338
2339var MPFMention = {
2340	listen: false,
2341	plus : false,
2342	text : '',
2343	bSearch: false
2344};
2345
2346window.BXfpdSelectCallback = function(item, type, search, bUndeleted)
2347{
2348	BX.SocNetLogDestination.BXfpSelectCallback({
2349		item: item,
2350		type: type,
2351		bUndeleted: bUndeleted,
2352		containerInput: BX('feed-add-post-destination-item'),
2353		valueInput: BX('feed-add-post-destination-input'),
2354		formName: window.BXSocNetLogDestinationFormName,
2355		tagInputName: 'bx-destination-tag',
2356		tagLink1: BX.message('BX_FPD_LINK_1'),
2357		tagLink2: BX.message('BX_FPD_LINK_2')
2358	});
2359};
2360
2361window.onKeyDownHandler = function(e, editor, formID)
2362{
2363	var keyCode = e.keyCode;
2364
2365	if (!window['BXfpdStopMent' + formID])
2366		return true;
2367
2368	if (keyCode == 107 ||
2369		((e.shiftKey || e.modifiers > 3) && BX.util.in_array(keyCode, [187, 50, 107, 43, 61])) ||
2370		(e.altKey && BX.util.in_array(keyCode, [76])) /* German @ == Alt + L*/)
2371	{
2372		setTimeout(function()
2373		{
2374			var
2375				range = editor.selection.GetRange(),
2376				doc = editor.GetIframeDoc(),
2377				txt = (range ? range.endContainer.textContent : ''),
2378				determiner = (txt ? txt.slice(range.endOffset - 1, range.endOffset) : ''),
2379				prevS = (txt ? txt.slice(range.endOffset - 2, range.endOffset-1) : '');
2380
2381			if ((determiner == "@" || determiner == "+")
2382				&&
2383				(!prevS || BX.util.in_array(prevS, ["+", "@", ",", "("]) || (prevS.length == 1 && BX.util.trim(prevS) === "")))
2384			{
2385				MPFMention.listen = true;
2386				MPFMention.listenFlag = true;
2387				MPFMention.text = '';
2388				MPFMention.leaveContent = true;
2389
2390				range.setStart(range.endContainer, range.endOffset - 1);
2391				range.setEnd(range.endContainer, range.endOffset);
2392				editor.selection.SetSelection(range);
2393				var mentNode = BX.create("SPAN", {props: {id: "bx-mention-node"}}, doc);
2394				editor.selection.Surround(mentNode, range);
2395				range.setStart(mentNode, 1);
2396				range.setEnd(mentNode, 1);
2397				editor.selection.SetSelection(range);
2398
2399				if(!BX.SocNetLogDestination.isOpenDialog())
2400				{
2401					BX.SocNetLogDestination.openDialog(window['BXSocNetLogDestinationFormNameMent' + formID],
2402						{
2403							bindNode: getMentionNodePosition(mentNode, editor)
2404						}
2405					);
2406				}
2407			}
2408		}, 10);
2409	}
2410
2411	if(MPFMention.listen)
2412	{
2413		var type = (
2414			MPFMention.bSearch
2415				? 'search'
2416				: BX.SocNetLogDestination.obTabSelected[window['BXSocNetLogDestinationFormNameMent' + formID]]
2417		);
2418
2419		if (keyCode == editor.KEY_CODES["enter"])
2420		{
2421			BX.SocNetLogDestination.selectCurrentItem(type, window['BXSocNetLogDestinationFormNameMent' + formID]);
2422			editor.iframeKeyDownPreventDefault = true;
2423			BX.PreventDefault(e);
2424		}
2425		else if (keyCode == editor.KEY_CODES["left"])
2426		{
2427			BX.SocNetLogDestination.moveCurrentItem(type, window['BXSocNetLogDestinationFormNameMent' + formID], 'left');
2428			editor.iframeKeyDownPreventDefault = true;
2429			BX.PreventDefault(e);
2430		}
2431		else if (keyCode == editor.KEY_CODES["right"])
2432		{
2433			BX.SocNetLogDestination.moveCurrentItem(type, window['BXSocNetLogDestinationFormNameMent' + formID], 'right');
2434			editor.iframeKeyDownPreventDefault = true;
2435			BX.PreventDefault(e);
2436		}
2437		else if (keyCode == editor.KEY_CODES["up"])
2438		{
2439			BX.SocNetLogDestination.moveCurrentItem(type, window['BXSocNetLogDestinationFormNameMent' + formID], 'up');
2440			editor.iframeKeyDownPreventDefault = true;
2441			BX.PreventDefault(e);
2442		}
2443		else if (keyCode == editor.KEY_CODES["down"])
2444		{
2445			BX.SocNetLogDestination.moveCurrentItem(type, window['BXSocNetLogDestinationFormNameMent' + formID], 'down');
2446			editor.iframeKeyDownPreventDefault = true;
2447			BX.PreventDefault(e);
2448		}
2449	}
2450
2451	if (!MPFMention.listen && MPFMention.listenFlag && keyCode === editor.KEY_CODES["enter"])
2452	{
2453		var range = editor.selection.GetRange();
2454		if (range.collapsed)
2455		{
2456			var
2457				node = range.endContainer,
2458				doc = editor.GetIframeDoc();
2459
2460			if (node)
2461			{
2462				if (node.className !== 'bxhtmled-metion')
2463				{
2464					node = BX.findParent(node, function(n)
2465					{
2466						return n.className == 'bxhtmled-metion';
2467					}, doc.body);
2468				}
2469
2470				if (node && node.className == 'bxhtmled-metion')
2471				{
2472					editor.selection.SetAfter(node);
2473				}
2474			}
2475		}
2476	}
2477};
2478
2479window.onKeyUpHandler = function(e, editor, formID)
2480{
2481	var
2482		keyCode = e.keyCode,
2483		doc, range, mentText;
2484
2485	if (!window['BXfpdStopMent' + formID])
2486		return true;
2487
2488	if(MPFMention.listen === true)
2489	{
2490		if(keyCode == editor.KEY_CODES["escape"]) //ESC
2491		{
2492			window['BXfpdStopMent' + formID]();
2493		}
2494		else if(
2495			keyCode !== editor.KEY_CODES["enter"]
2496			&& keyCode !== editor.KEY_CODES["left"]
2497			&& keyCode !== editor.KEY_CODES["right"]
2498			&& keyCode !== editor.KEY_CODES["up"]
2499			&& keyCode !== editor.KEY_CODES["down"]
2500		)
2501		{
2502			doc = editor.GetIframeDoc();
2503			var mentNode = doc.getElementById('bx-mention-node');
2504
2505			if (mentNode)
2506			{
2507				mentText = BX.util.trim(editor.util.GetTextContent(mentNode))
2508				var mentTextOrig = mentText;
2509
2510				mentText = mentText.replace(/^[\+@]*/, '');
2511				MPFMention.bSearch = (mentText.length > 0);
2512				BX.SocNetLogDestination.search(mentText, true, window['BXSocNetLogDestinationFormNameMent' + formID], BX.message("MPF_NAME_TEMPLATE"), {bindNode: getMentionNodePosition(mentNode, editor)});
2513
2514				if (MPFMention.leaveContent && MPFMention._lastText && mentTextOrig === '')
2515				{
2516					window['BXfpdStopMent' + formID]();
2517				}
2518				else if (MPFMention.leaveContent && MPFMention.lastText && mentTextOrig !== '' && mentText === '')
2519				{
2520					MPFMention.bSearch = false;
2521					window['BXfpdStopMent' + formID]();
2522					BX.SocNetLogDestination.openDialog(window['BXSocNetLogDestinationFormNameMent' + formID],
2523						{
2524							bindNode: getMentionNodePosition(mentNode, editor)
2525						}
2526					);
2527				}
2528
2529				MPFMention.lastText = mentText;
2530				MPFMention._lastText = mentTextOrig;
2531			}
2532			else
2533			{
2534				window['BXfpdStopMent' + formID]();
2535			}
2536		}
2537	}
2538	else
2539	{
2540		if (
2541			!e.shiftKey &&
2542			(keyCode === editor.KEY_CODES["space"] ||
2543			keyCode === editor.KEY_CODES["escape"] ||
2544			keyCode === 188 ||
2545			keyCode === 190
2546			))
2547		{
2548			range = editor.selection.GetRange();
2549			if (range.collapsed)
2550			{
2551				var node = range.endContainer;
2552				doc = editor.GetIframeDoc();
2553
2554				if (node)
2555				{
2556					if (node.className !== 'bxhtmled-metion')
2557					{
2558						node = BX.findParent(node, function(n)
2559						{
2560							return n.className == 'bxhtmled-metion';
2561						}, doc.body);
2562					}
2563
2564					if (node && node.className == 'bxhtmled-metion')
2565					{
2566						mentText = editor.util.GetTextContent(node);
2567						var matchSep = mentText.match(/[\s\.\,]$/);
2568						if (matchSep || keyCode === editor.KEY_CODES["escape"])
2569						{
2570							node.innerHTML = mentText.replace(/[\s\.\,]$/, '');
2571							var sepNode = BX.create('SPAN', {html: matchSep || editor.INVISIBLE_SPACE}, doc);
2572							editor.util.InsertAfter(sepNode, node);
2573							editor.selection.SetAfter(sepNode);
2574						}
2575					}
2576				}
2577			}
2578		}
2579	}
2580};
2581
2582window.onTextareaKeyDownHandler = function(e, editor, formID)
2583{
2584	var keyCode = e.keyCode;
2585
2586	if(MPFMention.listen && keyCode == editor.KEY_CODES["enter"])
2587	{
2588		BX.SocNetLogDestination.selectFirstSearchItem(window['BXSocNetLogDestinationFormNameMent' + formID]);
2589		editor.textareaKeyDownPreventDefault = true;
2590		BX.PreventDefault(e);
2591	}
2592};
2593
2594window.onTextareaKeyUpHandler = function(e, editor, formID)
2595{
2596	var
2597		cursor, value,
2598		keyCode = e.keyCode;
2599
2600	if(MPFMention.listen === true)
2601	{
2602		if(keyCode == 27) //ESC
2603		{
2604			window['BXfpdStopMent' + formID]();
2605		}
2606		else if(keyCode !== 13)
2607		{
2608			value = editor.textareaView.GetValue(false);
2609			cursor = editor.textareaView.GetCursorPosition();
2610
2611			if (value.indexOf('+') !== -1 || value.indexOf('@') !== -1)
2612			{
2613				var
2614					valueBefore = value.substr(0, cursor),
2615					charPos = Math.max(valueBefore.lastIndexOf('+'), valueBefore.lastIndexOf('@'));
2616
2617				if (charPos >= 0)
2618				{
2619					var
2620						mentText = valueBefore.substr(charPos),
2621						mentTextOrig = mentText;
2622
2623					mentText = mentText.replace(/^[\+@]*/, '');
2624
2625					if(BX.SocNetLogDestination && !BX.SocNetLogDestination.isOpenDialog())
2626					{
2627						BX.SocNetLogDestination.openDialog(window['BXSocNetLogDestinationFormNameMent' + formID]);
2628					}
2629
2630					MPFMention.bSearch = (mentText.length > 0);
2631					if (BX.SocNetLogDestination)
2632						BX.SocNetLogDestination.search(mentText, true, window['BXSocNetLogDestinationFormNameMent' + formID], BX.message("MPF_NAME_TEMPLATE"));
2633
2634					if (MPFMention.leaveContent && MPFMention._lastText && mentTextOrig === '')
2635					{
2636						window['BXfpdStopMent' + formID]();
2637					}
2638					else if (MPFMention.leaveContent && MPFMention.lastText && mentTextOrig !== '' && mentText === '')
2639					{
2640						window['BXfpdStopMent' + formID]();
2641						if (BX.SocNetLogDestination)
2642							BX.SocNetLogDestination.openDialog(window['BXSocNetLogDestinationFormNameMent' + formID]);
2643					}
2644
2645					MPFMention.lastText = mentText;
2646					MPFMention._lastText = mentTextOrig;
2647				}
2648			}
2649		}
2650	}
2651	else
2652	{
2653		if (keyCode == 16)
2654		{
2655			var _this = this;
2656			this.shiftPressed = true;
2657			if (this.shiftTimeout)
2658				this.shiftTimeout = clearTimeout(this.shiftTimeout);
2659
2660			this.shiftTimeout = setTimeout(function()
2661			{
2662				_this.shiftPressed = false;
2663			}, 100);
2664		}
2665
2666		if (keyCode == 107 || (e.shiftKey || e.modifiers > 3 || this.shiftPressed) &&
2667			BX.util.in_array(keyCode, [187, 50, 107, 43, 61]))
2668		{
2669			cursor = editor.textareaView.element.selectionStart;
2670			if (cursor > 0)
2671			{
2672				value = editor.textareaView.element.value;
2673				var
2674					lastChar = value.substr(cursor - 1, 1);
2675
2676				if (lastChar && (lastChar === '+' || lastChar === '@'))
2677				{
2678					MPFMention.listen = true;
2679					MPFMention.listenFlag = true;
2680					MPFMention.text = '';
2681					MPFMention.textarea = true;
2682
2683					if(!BX.SocNetLogDestination.isOpenDialog())
2684					{
2685						BX.SocNetLogDestination.openDialog(window['BXSocNetLogDestinationFormNameMent' + formID]);
2686					}
2687				}
2688			}
2689		}
2690	}
2691};
2692
2693
2694var getMentionNodePosition = function(mention, editor)
2695{
2696	var
2697		mentPos = BX.pos(mention),
2698		editorPos = BX.pos(editor.dom.areaCont),
2699		editorDocScroll = BX.GetWindowScrollPos(editor.GetIframeDoc()),
2700		top = editorPos.top + mentPos.bottom - editorDocScroll.scrollTop + 2,
2701		left = editorPos.left + mentPos.right - editorDocScroll.scrollLeft;
2702
2703	return {top: top, left: left};
2704};
2705
2706window.BxInsertMention = function (params)
2707{
2708	var
2709		item = params.item,
2710		type = params.type,
2711		formID = params.formID,
2712		editorId = params.editorId,
2713		bNeedComa = params.bNeedComa,
2714		editor = LHEPostForm.getEditor(editorId),
2715		spaceNode;
2716
2717	if(type == 'users' && item && item.entityId > 0 && editor)
2718	{
2719		if(editor.GetViewMode() == 'wysiwyg') // WYSIWYG
2720		{
2721			var
2722				doc = editor.GetIframeDoc(),
2723				range = editor.selection.GetRange(),
2724				mentNode = doc.getElementById('bx-mention-node'),
2725				mention = BX.create('SPAN',
2726					{
2727						props: {className: 'bxhtmled-metion'},
2728						text: BX.util.htmlspecialcharsback(item.name)
2729					}, doc);
2730				// &nbsp; - for chrome
2731			spaceNode = BX.create('SPAN', {html: (bNeedComa ? ',&nbsp;' : '&nbsp;')}, doc);
2732
2733			editor.SetBxTag(mention, {tag: "postuser", params: {value : item.entityId}});
2734
2735			if (mentNode)
2736			{
2737				editor.util.ReplaceNode(mentNode, mention);
2738			}
2739			else
2740			{
2741				editor.selection.InsertNode(mention, range);
2742			}
2743
2744			if (mention && mention.parentNode)
2745			{
2746				var parentMention = BX.findParent(mention, {className: 'bxhtmled-metion'}, doc.body);
2747				if (parentMention)
2748				{
2749					editor.util.InsertAfter(mention, parentMention);
2750				}
2751			}
2752
2753			if (mention && mention.parentNode)
2754			{
2755				editor.util.InsertAfter(spaceNode, mention);
2756				editor.selection.SetAfter(spaceNode);
2757			}
2758		}
2759		else if (editor.GetViewMode() == 'code' && editor.bbCode) // BB Codes
2760		{
2761			editor.textareaView.Focus();
2762
2763			var
2764				value = editor.textareaView.GetValue(false),
2765				cursor = editor.textareaView.GetCursorPosition(),
2766				valueBefore = value.substr(0, cursor),
2767				charPos = Math.max(valueBefore.lastIndexOf('+'), valueBefore.lastIndexOf('@'));
2768
2769			if (charPos >= 0 && cursor > charPos)
2770			{
2771				editor.textareaView.SetValue(value.substr(0, charPos) + value.substr(cursor));
2772				editor.textareaView.element.setSelectionRange(charPos, charPos);
2773			}
2774
2775			editor.textareaView.WrapWith(false, false, "[USER=" + item.entityId + "]" + item.name + "[/USER]" + (bNeedComa ? ', ' : ' '));
2776		}
2777
2778		delete BX.SocNetLogDestination.obItemsSelected[window['BXSocNetLogDestinationFormNameMent' + formID]][item.id];
2779		window['BXfpdStopMent' + formID]();
2780		MPFMention["text"] = '';
2781
2782		if(editor.GetViewMode() == 'wysiwyg') // WYSIWYG
2783		{
2784			editor.Focus();
2785			editor.selection.SetAfter(spaceNode);
2786		}
2787	}
2788};
2789
2790window.MPFMentionInit = function(formId, params)
2791{
2792	if (!params["items"]["departmentRelation"])
2793	{
2794		params["items"]["departmentRelation"] = BX.SocNetLogDestination.buildDepartmentRelation(params["items"]["department"]);
2795	}
2796
2797	window["departmentRelation"] = params["items"]["departmentRelation"]; // for calendar - do not remove
2798
2799	if (params["initDestination"] === true)
2800	{
2801		window.BXSocNetLogDestinationFormName = 'destination' + ('' + new Date().getTime()).substr(6);
2802		window.BXSocNetLogDestinationDisableBackspace = null;
2803		BX.SocNetLogDestination.init({
2804			name : window.BXSocNetLogDestinationFormName,
2805			searchInput : BX('feed-add-post-destination-input'),
2806			extranetUser :  params["extranetUser"],
2807			bindMainPopup : {
2808				node: BX('feed-add-post-destination-container'),
2809				offsetTop: '5px',
2810				offsetLeft: '15px'
2811			},
2812			bindSearchPopup : {
2813				node : BX('feed-add-post-destination-container'),
2814				offsetTop : '5px',
2815				offsetLeft: '15px'
2816			},
2817			callback : {
2818				select : window["BXfpdSelectCallback"],
2819				unSelect : BX.delegate(BX.SocNetLogDestination.BXfpUnSelectCallback, {
2820					formName: window.BXSocNetLogDestinationFormName,
2821					inputContainerName: 'feed-add-post-destination-item',
2822					inputName: 'feed-add-post-destination-input',
2823					tagInputName: 'bx-destination-tag',
2824					tagLink1: BX.message('BX_FPD_LINK_1'),
2825					tagLink2: BX.message('BX_FPD_LINK_2')
2826				}),
2827				openDialog : BX.delegate(BX.SocNetLogDestination.BXfpOpenDialogCallback, {
2828					inputBoxName: 'feed-add-post-destination-input-box',
2829					inputName: 'feed-add-post-destination-input',
2830					tagInputName: 'bx-destination-tag'
2831				}),
2832				closeDialog : BX.delegate(BX.SocNetLogDestination.BXfpCloseDialogCallback, {
2833					inputBoxName: 'feed-add-post-destination-input-box',
2834					inputName: 'feed-add-post-destination-input',
2835					tagInputName: 'bx-destination-tag'
2836				}),
2837				openSearch : BX.delegate(BX.SocNetLogDestination.BXfpOpenDialogCallback, {
2838					inputBoxName: 'feed-add-post-destination-input-box',
2839					inputName: 'feed-add-post-destination-input',
2840					tagInputName: 'bx-destination-tag'
2841				}),
2842				closeSearch : BX.delegate(BX.SocNetLogDestination.BXfpCloseSearchCallback, {
2843					inputBoxName: 'feed-add-post-destination-input-box',
2844					inputName: 'feed-add-post-destination-input',
2845					tagInputName: 'bx-destination-tag'
2846				})
2847			},
2848			items : params["items"],
2849			itemsLast : params["itemsLast"],
2850			itemsSelected : params["itemsSelected"],
2851			isCrmFeed : params["isCrmFeed"],
2852			useClientDatabase: (!!params["useClientDatabase"]),
2853			destSort: params["destSort"],
2854			allowAddUser: params["allowAddUser"],
2855			allowAddCrmContact: params["allowAddCrmContact"],
2856			allowSearchCrmEmailUsers: (typeof params["allowSearchCrmEmailUsers"] != 'undefined' ? !!params["allowSearchCrmEmailUsers"] : false),
2857			userNameTemplate: (typeof params["userNameTemplate"] != 'undefined' ? params["userNameTemplate"] : '')
2858		});
2859		BX.bind(BX('feed-add-post-destination-input'), 'keyup', BX.delegate(BX.SocNetLogDestination.BXfpSearch, {
2860			formName: window.BXSocNetLogDestinationFormName,
2861			inputName: 'feed-add-post-destination-input',
2862			tagInputName: 'bx-destination-tag'
2863		}));
2864		BX.bind(BX('feed-add-post-destination-input'), 'keydown', BX.delegate(BX.SocNetLogDestination.BXfpSearchBefore, {
2865			formName: window.BXSocNetLogDestinationFormName,
2866			inputName: 'feed-add-post-destination-input'
2867		}));
2868		BX.bind(BX('bx-destination-tag'), 'focus', function(e) {
2869			BX.SocNetLogDestination.openDialog(
2870				window.BXSocNetLogDestinationFormName,
2871				{
2872					bByFocusEvent: true
2873				}
2874			);
2875			return BX.PreventDefault(e);
2876		});
2877		BX.bind(BX('feed-add-post-destination-container'), 'click', function(e) {
2878			BX.SocNetLogDestination.openDialog(window.BXSocNetLogDestinationFormName);
2879			return BX.PreventDefault(e);
2880		});
2881		if (params["itemsHidden"])
2882		{
2883			for (var ii in params["itemsHidden"])
2884			{
2885				if (params["itemsHidden"].hasOwnProperty(ii))
2886				{
2887					window.BXfpdSelectCallback({id:('SG'+params["itemsHidden"][ii]["ID"]), name:params["itemsHidden"][ii]["
2887NAME"]}, 'sonetgroups', '', true);
2888				}
2889			}
2890		}
2891
2892		BX.SocNetLogDestination.BXfpSetLinkName({
2893			formName: window.BXSocNetLogDestinationFormName,
2894			tagInputName: 'bx-destination-tag',
2895			tagLink1: BX.message('BX_FPD_LINK_1'),
2896			tagLink2: BX.message('BX_FPD_LINK_2')
2897		});
2898	}
2899	window["BXfpdSelectCallbackMent" + formId] = function(item, type, search)
2900	{
2901		window.BxInsertMention({item: item, type: type, formID: formId, editorId: params["editorId"]});
2902	};
2903
2904	window["BXfpdStopMent" + formId] = function ()
2905	{
2906		BX.SocNetLogDestination.closeDialog();
2907		BX.SocNetLogDestination.closeSearch();
2908		clearTimeout(BX.SocNetLogDestination.searchTimeout);
2909		BX.SocNetLogDestination.searchOnSuccessHandle = false;
2910	};
2911
2912	window["BXfpdOnDialogOpen" + formId] = function ()
2913	{
2914		MPFMention.listen = true;
2915		MPFMention.listenFlag = true;
2916	};
2917
2918	window["BXfpdOnDialogClose" + formId] = function ()
2919	{
2920		MPFMention.listen = false;
2921		setTimeout(function()
2922		{
2923			MPFMention.listenFlag = false;
2924			if (!MPFMention.listen)
2925			{
2926				var editor = LHEPostForm.getEditor(params.editorId);
2927				if(editor)
2928				{
2929					var
2930						doc = editor.GetIframeDoc(),
2931						mentNode = doc.getElementById('bx-mention-node');
2932
2933					if (mentNode)
2934					{
2935						editor.selection.SetAfter(mentNode);
2936						if (MPFMention.leaveContent)
2937						{
2938							editor.util.ReplaceWithOwnChildren(mentNode);
2939						}
2940						else
2941						{
2942							BX.remove(mentNode);
2943						}
2944					}
2945					editor.Focus();
2946				}
2947			}
2948		}, 100);
2949	};
2950
2951	window["BXSocNetLogDestinationFormNameMent" + formId] = 'mention' + ('' + new Date().getTime()).substr(5);
2952	window["BXSocNetLogDestinationDisableBackspace"] = null;
2953	var bxBMent = BX('bx-b-mention-' + formId);
2954
2955	if (typeof params["items"]["extranetRoot"] != 'undefined')
2956	{
2957		params["items"]["departmentExtranet"] = BX.clone(params["items"]["department"]);
2958		for(var key in params["items"]["extranetRoot"])
2959		{
2960			if (params["items"]["extranetRoot"].hasOwnProperty(key))
2961			{
2962				params["items"]["departmentExtranet"][key] = params["items"]["extranetRoot"][key];
2963			}
2964		}
2965		params["items"]["departmentRelationExtranet"] = BX.SocNetLogDestination.buildDepartmentRelation(params["items"]["departmentExtranet"]);
2966	}
2967
2968	BX.SocNetLogDestination.init({
2969		name : window["BXSocNetLogDestinationFormNameMent" + formId],
2970		searchInput : bxBMent,
2971		extranetUser : params["extranetUser"],
2972		bindMainPopup :  {
2973			node : bxBMent,
2974			offsetTop : '1px',
2975			offsetLeft: '12px'
2976		},
2977		bindSearchPopup : {
2978			node : bxBMent,
2979			offsetTop : '1px',
2980			offsetLeft: '12px'
2981		},
2982		callback : {
2983			select : window["BXfpdSelectCallbackMent" + formId],
2984			openDialog : window["BXfpdOnDialogOpen" + formId],
2985			closeDialog : window["BXfpdOnDialogClose" + formId],
2986			openSearch : window["BXfpdOnDialogOpen" + formId],
2987			closeSearch : window["BXfpdOnDialogClose" + formId]
2988		},
2989		items : {
2990			users : params["items"]["mentionUsers"],
2991			groups : {},
2992			sonetgroups : {},
2993			department : (typeof params["items"]["departmentExtranet"] != 'undefined' ? params["items"]["departmentExtranet"] : params["items"]["department"]),
2994			departmentRelation : (typeof params["items"]["departmentRelationExtranet"] != 'undefined' ? params["items"]["departmentRelationExtranet"] : params["items"]["departmentRelation"])
2995		},
2996		itemsLast : {
2997			users : params["itemsLast"]["mentionUsers"],
2998			sonetgroups : {},
2999			department : {},
3000			groups : {}
3001		},
3002		itemsSelected : params["itemsSelected"],
3003		destSort: (typeof params["mentionDestSort"] != 'undefined' && params["mentionDestSort"] ? params["mentionDestSort"] : {}),
3004		departmentSelectDisable : true,
3005		obWindowClass : 'bx-lm-mention',
3006		obWindowCloseIcon : false,
3007		useClientDatabase: (!!params["useClientDatabase"]),
3008		userNameTemplate: (typeof params["userNameTemplate"] != 'undefined' ? params["userNameTemplate"] : '')
3009	});
3010
3011	BX.ready(function() {
3012			var ment = BX('bx-b-mention-' + formId);
3013			if(BX.browser.IsIE() && !BX.browser.IsIE9())
3014			{
3015				ment.style.width = '1px';
3016				ment.style.marginRight = '0';
3017			}
3018
3019			BX.bind(
3020				ment,
3021				"mousedown",
3022				function(e)
3023				{
3024					if(MPFMention.listen !== true)
3025					{
3026						var
3027							editor = LHEPostForm.getEditor(params.editorId),
3028							doc = editor.GetIframeDoc();
3029
3030						if(editor.GetViewMode() == 'wysiwyg' && doc)
3031						{
3032							MPFMention.listen = true;
3033							MPFMention.listenFlag = true;
3034							MPFMention.text = '';
3035							MPFMention.leaveContent = false;
3036
3037							var
3038								range = editor.selection.GetRange(),
3039								mentNode = doc.getElementById('bx-mention-node');
3040
3041							if (mentNode)
3042							{
3043								BX.remove(mentNode);
3044							}
3045							editor.InsertHtml('<span id="bx-mention-node">' + editor.INVISIBLE_SPACE + '</span>', range);
3046
3047							setTimeout(function()
3048							{
3049								if(!BX.SocNetLogDestination.isOpenDialog())
3050								{
3051									BX.SocNetLogDestination.openDialog(window["BXSocNetLogDestinationFormNameMent" + formId], {bindNode: ment});
3052								}
3053
3054								var mentionNode = doc.getElementById('bx-mention-node');
3055								if (mentionNode)
3056								{
3057									range.setStart(mentionNode, 0);
3058									if (mentionNode.firstChild && mentionNode.firstChild.nodeType == 3 && mentionNode.firstChild.nodeValue.length > 0)
3059									{
3060										range.setEnd(mentionNode, 1);
3061									}
3062									else
3063									{
3064										range.setEnd(mentionNode, 0);
3065									}
3066									editor.selection.SetSelection(range);
3067								}
3068								editor.Focus();
3069							}, 100);
3070						}
3071						else if (editor.GetViewMode() == 'code')
3072						{
3073							MPFMention.listen = true;
3074							MPFMention.listenFlag = true;
3075							MPFMention.text = '';
3076							MPFMention.leaveContent = false;
3077
3078							// TODO: get current cusrsor position
3079
3080							setTimeout(function()
3081							{
3082								if(!BX.SocNetLogDestination.isOpenDialog())
3083								{
3084									BX.SocNetLogDestination.openDialog(window["BXSocNetLogDestinationFormNameMent" + formId], {bindNode: ment});
3085								}
3086							}, 100);
3087						}
3088
3089						BX.onCustomEvent(ment, 'mentionClick');
3090					}
3091				}
3092			);
3093		}
3094	);
3095}
3096})();

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.