/**
 * @class eli18n
 * Javascript applications localization 
 *
 * @param Object o - class options. Object. {textdomain : 'имя_группы_сообщений', messages : {textdomain1 : {}[, textdomain2 : {}]...}}
 *
 * Usage:
 * 
 * var msgs = { Hello : 'Превэд', 'Hello %user' : 'Превед %user' };
 * //load messages and set default textdomain
 * var translator = new eli18n( {textdomain : 'test', messages : {test : msgs}} )
 * window.console.log(translator.translate('Hello'));
 * window.console.log(translator.format('Hello %user', {user : 'David Blain'}))
 * // create new textdomain
 * translator.load({test2 : {'Goodbye' : 'Ja, deva mata!'} })
 * // and use it, without changing default one
 * window.console.log(translator.translate('Goodbye', 'test2'));
 *
 * @author:    Dmitry (dio) Levashov dio@std42.ru
 * license:   BSD license
 **/
function eli18n(o) {
	
	/**
	 * Get/set default textdomain
	 *
	 * @param String d new textdomain name
	 * @return String  default textdomain
	 **/
	this.textdomain = function(d) {
		return this.messages[d] ? this._domain = d : this._domain;
	}
	
	o && o.messages   && this.load(o.messages);
	o && o.textdomain && this.textdomain(o.textdomain);
}

eli18n.prototype = new function() {
	
	/**
	 * @var Object messages (key - messages in English or message handler, value - message in selected language)
	 **/
	this.messages = {};
	/**
	 * @var String default textdomain
	 **/
	this._domain   = '';
	
	/**
	 * Load new messages
	 *
	 * @param Object msgs - messages (key - textdomain name, value - messages Object)
	 * @return Object this
	 **/
	this.load = function(msgs) {
		if (typeof(msgs) == 'object') {
			for (var d in msgs) {
				var _msgs = msgs[d];
				if (typeof(_msgs) == 'object') {
					if (!this.messages[d]) {
						this.messages[d] = {}; 
					}
					for (var k in _msgs) {
						if (typeof(_msgs[k]) == 'string') {
							this.messages[d][k] = _msgs[k];
						}
					}
				}
			}
		}
		return this;
	}

	/**
	 * Return translated message, if message exists in required or default textdomain, otherwise returns original message
	 *
	 * @param  String msg - message
	 * @param  String d - textdomain. If empty, default textdomain will be used
	 * @return String translated message
	 **/
	this.translate = function(msg, d) {
		var d = d && this.messages[d] ? d : this._domain;
		return this.messages[d] && this.messages[d][msg] ? this.messages[d][msg] : msg;
		
	}
	
	/**
	 * Translate message and replace placeholders (%placeholder)
	 *
	 * @param  String  msg - message
	 * @param  Object  replacement for placeholders (keys - placeholders name without leading %, values - replacements)
	 * @param  String  d - textdomain. If empty, default textdomain will be used
	 * @return String  translated message
	 **/
	this.format = function(msg, data, d) {
		msg = this.translate(msg, d);
		if (typeof(data) == 'object') {
			for (var i in data) {
				msg = msg.replace('%'+i, this.translate(data[i], d));
			}
		}
		return msg;
	}
}
/**
 * @class elDialogForm
 * Wraper for jquery.ui.dialog and jquery.ui.tabs
 *  Create form in dialog. You can decorate it as you wish - with tabs or/and tables
 *
 * Usage:
 *   var d = new elDialogForm(opts)
 *   d.append(['Field name: ', $('<input type="text" name="f1" />')])
 *		.separator()
 *		.append(['Another field name: ', $('<input type="text" name="f2" />')])
 *      .open()
 * will create dialog with pair text field separated by horizontal rule
 * Calling append() with 2 additional arguments ( d.append([..], null, true)) 
 *  - will create table in dialog and put text inputs and labels in table cells
 *
 * Dialog with tabs:
 *   var d = new elDialogForm(opts)
 *   d.tab('first', 'First tab label)
 * 	  .tab('second', 'Second tab label)
 *    .append(['Field name: ', $('<input type="text" name="f1" />')], 'first', true)  - add label and input to first tab in table (table will create automagicaly)
 *    .append(['Field name 2: ', $('<input type="text" name="f2" />')], 'second', true)  - same in secon tab
 *
 * Options:
 *   class     - css class for dialog
 *   submit    - form submit event callback. Accept 2 args - event and this object
 *   ajaxForm  - arguments for ajaxForm, if needed (dont forget include jquery.form.js)
 *   tabs      - arguments for ui.tabs
 *   dialog    - arguments for ui.dialog
 *   name      - hidden text field in wich selected value will saved
 *
 * Notice!
 * When close dialog, it will destroing insead of dialog('close'). Reason - strange bug with tabs in dialog on secondary opening. 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 *
 **/

function elDialogForm(o) {
	var self = this;
	
	var defaults = {
		'class'   : 'el-dialogform',
		submit    : function(e, d) { d.close(); },
		form      : { action : window.location.href,	method : 'post'	},
		ajaxForm  : null,
		validate  : null,
		spinner   : 'Loading',
		tabs      : { active: 0, selected : 0 },
		tabPrefix : 'el-df-tab-',
		dialog    : {
			title     : 'dialog',
			autoOpen  : false,
			modal     : true,
			resizable : false,
			closeOnEscape : true,
			buttons  : {
				Cancel : function() { self.close(); },
				Ok     : function() { self.form.trigger('submit'); }
			}
		}
	};

	this.opts = jQuery.extend(true, {}, defaults, o);
	
	this.opts.dialog.close = function() { 
		self.close(); 
	}

	// this.opts.dialog.autoOpen = true;
	if (this.opts.rtl) {
		this.opts['class'] += ' el-dialogform-rtl';
	}
	
	if (o && o.dialog && o.dialog.buttons && typeof(o.dialog.buttons) == 'object') {
		this.opts.dialog.buttons = o.dialog.buttons;
	}

	this.ul     = null;
	this.tabs   = {};
	this._table = null;

	this.dialog = jQuery('<div />').addClass(this.opts['class']).dialog(this.opts.dialog);

	this.message = jQuery('<div class="el-dialogform-message rounded-5" />').hide().appendTo(this.dialog);
	this.error   = jQuery('<div class="el-dialogform-error rounded-5" />').hide().appendTo(this.dialog);
	this.spinner = jQuery('<div class="spinner" />').hide().appendTo(this.dialog);
	this.content = jQuery('<div class="el-dialogform-content" />').appendTo(this.dialog)
	this.form   = jQuery('<form />').attr(this.opts.form).appendTo(this.content);

	if (this.opts.submit) {
		this.form.bind('submit', function(e) { self.opts.submit(e, self) })
	}
	if (this.opts.ajaxForm && jQuery.fn.ajaxForm) {
		this.form.ajaxForm(this.opts.ajaxForm);
	}
	if (this.opts.validate) {
		this.form.validate(this.opts.validate);
	}
	
	this.option = function(name, value) {
		return this.dialog.dialog('option', name, value)
	}
	
	this.showError = function(msg, hideContent) {
		this.hideMessage();
		this.hideSpinner();
		this.error.html(msg).show();
		hideContent && this.content.hide();
		return this;
	}
	
	this.hideError= function() {
		this.error.text('').hide();
		this.content.show();
		return this;		
	}
	
	this.showSpinner = function(txt) {
		this.error.hide();
		this.message.hide();
		this.content.hide();
		this.spinner.text(txt||this.opts.spinner).show();
		this.option('buttons', {});
		return this;		
	}
	
	this.hideSpinner = function() {
		this.content.show();
		this.spinner.hide();
		return this;		
	}
	
	this.showMessage = function(txt, hideContent) {
		this.hideError();
		this.hideSpinner();
		this.message.html(txt||'').show();
		hideContent && this.content.hide();
		return this;
	}
	
	this.hideMessage = function() {
		this.message.hide();
		this.content.show();
		return this;		
	}
	
	/**
	 * Create new tab
	 * @param string id    - tab id
	 * @param string title - tab name
	 * @return elDialogForm	
	**/
	this.tab = function(id, title) {
		id = this.opts.tabPrefix+id;
		
		if (!this.ul) {
			this.ul = jQuery('<ul />').prependTo(this.form);
		}
		jQuery('<li />').append(jQuery('<a />').attr('href', '#'+id).html(title)).appendTo(this.ul);
		this.tabs[id] = {tab : jQuery('<div />').attr('id', id).addClass('tab').appendTo(this.form), table : null};
		return this;
	}
	
	/**
	 * Create new table
	 * @param string id  tab id, if set - table will create in tab, otherwise - in dialog
	 * @return elDialogForm	
	**/
	this.table = function(id) {
		id = id && id.indexOf(this.opts.tabPrefix) == -1 ? this.opts.tabPrefix+id : id;
		if (id && this.tabs && this.tabs[id]) {
			this.tabs[id].table = jQuery('<table />').appendTo(this.tabs[id].tab);
		} else {
			this._table = jQuery('<table />').appendTo(this.form); 
		}
		return this;
	}
	
	/**
	 * Append html, dom nodes or jQuery objects to dialog or tab
	 * @param array|object|string  data object(s) to append to dialog
	 * @param string               tid  tab id, if adding to tab
	 * @param bool                 t    if true - data will added in table (creating automagicaly)
	 * @return elDialogForm	
	**/
	this.append = function(data, tid, t) {
		tid = tid ? 'el-df-tab-'+tid : '';

		if (!data) {
			return this;
		}
		
		if (tid && this.tabs[tid]) {
			if (t) {
				!this.tabs[tid].table && this.table(tid);
				var tr = jQuery('<tr />').appendTo(this.tabs[tid].table);
				if (!jQuery.isArray(data)) {
					tr.append(jQuery('<td />').append(data));
				} else {
					for (var i=0; i < data.length; i++) {
						tr.append(jQuery('<td />').append(data[i]));
					};
				}
			} else {
				if (!jQuery.isArray(data)) {
					this.tabs[tid].tab.append(data)
				} else {
					for (var i=0; i < data.length; i++) {
						this.tabs[tid].tab.append(data[i]);
					};
				}
			}
			
		} else {
			if (!t) {
				if (!jQuery.isArray(data)) {
					this.form.append(data);
				} else {
					for (var i=0; i < data.length; i++) {
						this.form.append(data[i]);
					};
				}
			} else {
				if (!this._table) {
					this.table();
				}
				var tr = jQuery('<tr />').appendTo(this._table);
				if (!jQuery.isArray(data)) {
					tr.append(jQuery('<td />').append(data));
				} else {
					for (var i=0; i < data.length; i++) {
						tr.append(jQuery('<td />').append(data[i]));
					};
				}
			}
		}
		return this;
	}
	
	/**
	 * Append separator (div class="separator") to dialog or tab
	 * @param  string tid  tab id, if adding to tab
	 * @return elDialogForm	
	**/
	this.separator = function(tid) {
		tid = 'el-df-tab-'+tid;
		if (this.tabs && this.tabs[tid]) {
			this.tabs[tid].tab.append(jQuery('<div />').addClass('separator'));
			this.tabs[tid].table && this.table(tid);
		} else {
			this.form.append(jQuery('<div />').addClass('separator'));
		}
		return this;
	}
	
	/**
	 * Open dialog window
	 * @return elDialogForm	
	**/
	this.open = function() {
		var self = this;
		
		this.ul && this.form.tabs(this.opts.tabs);

		setTimeout(function() {
			self.dialog.find(':text')
				.keydown(function(e) {
					if (e.keyCode == 13) {
						e.preventDefault()
						self.form.submit();
					}
				})
				.filter(':first')[0].focus()
		}, 200);

		this.dialog.dialog('open');

		return this;
	}
	
	/**
	 * Close dialog window and destroy content
	 * @return void	
	**/
	this.close = function() {
		if (typeof(this.opts.close) == 'function') {
			this.opts.close();
		}
		this.dialog.dialog('destroy')//.remove();
	}
	
}

/**
 * elColorPicker. JQuery plugin
 * Create drop-down colors palette.
 *
 * Usage:
 * $(selector).elColorPicker(opts)
 *
 * set color after init:
 * var c = $(selector).elColorPicker(opts)
 * c.val('#ffff99)
 *
 * Get selected color:
 * var color = c.val();
 *
 * Notice!
 *   Palette created only after first click on element (lazzy loading)
 *
 * Options:
 *   colors - colors array (by default display 256 web safe colors)
 *   color  - current (selected) color
 *   class - css class for display "button" (element on wich plugin was called)
 *   paletteClass - css class for colors palette
 *   palettePosition - string indicate where palette will created:
 *      'inner' - palette will attach to element (acceptable in most cases)
 *      'outer' - palette will attach to document.body. 
 *                Use, when create color picker inside element with overflow == 'hidden', for example in ui.dialog
 *   update - function wich update button view on select color (by default set selected color as background)
 *   change - callback, called when color was selected (by default write color to console.log)
 *   name   - hidden text field in wich selected color value will saved
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 *
 **/
(function($) {

	$.fn.elColorPicker = function(o) {
		var self     = this;
		var opts     = $.extend({}, $.fn.elColorPicker.defaults, o);
		this.hidden  = $('<input type="hidden" />').attr('name', opts.name).val(opts.color||'').appendTo(this);
		this.palette = null;
		this.preview = null;
		this.input   = null;

		function setColor(c) {
			self.val(c);
			opts.change && opts.change(self.val());
			self.palette.slideUp();
		}

		function init() {
			self.palette  = $('<div />').addClass(opts.paletteClass+' rounded-3');
			for (var i=0; i < opts.colors.length; i++) {
				$('<div />')
					.addClass('color')
					.css('background-color', opts.colors[i])
					.attr({title : opts.colors[i], unselectable : 'on'})
					.appendTo(self.palette)
					.mouseenter(function() {
						var v = $(this).attr('title');
						self.input.val(v);
						self.preview.css('background-color', v);
					})
					.click(function(e) {
						e.stopPropagation(); 
						setColor($(this).attr('title'));
					});
			};
			self.input = $('<input type="text" />')
				.addClass('rounded-3').addClass('span1')
				.attr('size', 8)
				.click(function(e) {
					e.stopPropagation();
					$(this).focus();
				})
				.keydown(function(e) {
					if (e.ctrlKey || e.metaKey) {
						return true;
					}
					var k = e.keyCode;
					// on esc - close palette
					if (k == 27) {
						return self.mouseleave();
					}
					// allow input only hex color value
					if (k!=8 && k != 13 && k!=46 && k!=37 && k != 39 && (k<48 || k>57) && (k<65 || k > 70)) {
						return false;
					}
					var c = $(this).val();
					if (c.length == 7 || c.length == 0) {
						if (k == 13) {
							e.stopPropagation();
							e.preventDefault();
							setColor(c);
							self.palette.slideUp();
						}
						if (e.keyCode != 8 && e.keyCode != 46 && k!=37 && k != 39) {
							return false;
						}
					}
				})
				.keyup(function(e) {
					var c = $(this).val(); 
					c.length == 7 && /^#[0-9abcdef]{6}$/i.test(c) && self.val(c);
				});
				
			self.preview = $('<div />')
				.addClass('preview rounded-3')
				.click(function(e) {
					e.stopPropagation();
					setColor(self.input.val());
				});
			
			self.palette
				.append($('<div />').addClass('clearfix'))
				.append($('<div />').addClass('panel').append(self.input).append(self.preview));
			
			if (opts.palettePosition == 'outer') {
				self.palette.hide()
					.appendTo(self.parents('body').eq(0))
					.mouseleave(function() {
						if (!self.palette.is(':animated')) {
						$(this).slideUp();
						self.val(self.val());
					}
					});
				self.mouseleave(function(e) {
					if (e.relatedTarget != self.palette.get(0)) {
						if (!self.palette.is(':animated')) {
							self.palette.slideUp();
							self.val(self.val());
						}
						
					}
				})
			} else {
				self.append(self.palette.hide())
					.mouseleave(function(e) {
						self.palette.slideUp();
						self.val(self.val());
					});
			}
			self.val(self.val());
		}
		
		this.empty().addClass(opts['class']+' rounded-3')
			.css({'position' : 'relative', 'background-color' : opts.color||''})
		.click(function(e) { 
			if (!self.hasClass('disabled')) {
				!self.palette && init();
				if (opts.palettePosition == 'outer' && self.palette.css('display') == 'none') {
					var o = $(this).offset();
					var w = self.palette.width();
					var l = self.parents('body').width() - o.left >= w ? o.left : o.left + $(this).outerWidth() - w;
					self.palette.css({left : l+'px', top : o.top+$(this).height()+1+'px'});
				}
				self.palette.slideToggle();
			}
		});
		
		this.val = function(v) {
			if (!v && v!=='') {
				return this.hidden.val();
			} else {
				this.hidden.val(v);
				if (opts.update) {
					opts.update(this.hidden.val());
				} else {
					this.css('background-color', v);
				}
				
				if (self.palette) {
					self.preview.css('background-color', v);
					self.input.val(v);
				}
			}
			return this;
		}
		
		return this;
	}

	$.fn.elColorPicker.defaults = {
		'class'         : 'el-colorpicker',
		paletteClass    : 'el-palette',
		palettePosition : 'inner',
		name            : 'color',
		color           : '',
		update          : null,
		change          : function(c) {  },
		colors          : [
			'#ffffff', '#cccccc', '#999999', '#666666', '#333333', '#000000', 
			'#ffcccc', '#cc9999', '#996666', '#663333', '#330000', 
			'#ff9999', '#cc6666', '#cc3333', '#993333', '#660000', 
			'#ff6666', '#ff3333', '#ff0000', '#cc0000', '#990000',
			'#ff9966', '#ff6633', '#ff3300', '#cc3300', '#993300',
			'#ffcc99', '#cc9966', '#cc6633', '#996633', '#663300',
			'#ff9933', '#ff6600', '#ff9900', '#cc6600', '#cc9933',
			'#ffcc66', '#ffcc33', '#ffcc00', '#cc9900', '#996600',
			'#ffffcc', '#cccc99', '#999966', '#666633', '#333300',
			'#ffff99', '#cccc66', '#cccc33', '#999933', '#666600',
			'#ffff66', '#ffff33', '#ffff00', '#cccc00', '#999900',
			'#ccff66', '#ccff33', '#ccff00', '#99cc00', '#669900',
			'#ccff99', '#99cc66', '#99cc33', '#669933', '#336600',
			'#99ff33', '#99ff00', '#66ff00', '#66cc00', '#66cc33',
			'#99ff66', '#66ff33', '#33ff00', '#33cc00', '#339900',
			'#ccffcc', '#99cc99', '#669966', '#336633', '#003300',
			'#99ff99', '#66cc66', '#33cc33', '#339933', '#006600',
			'#66ff66', '#33ff33', '#00ff00', '#00cc00', '#009900',
			'#66ff99', '#33ff66', '#00ff33', '#00cc33', '#009933',			
			'#99ffcc', '#66cc99', '#33cc66', '#339966', '#006633',						
			'#33ff99', '#00ff66', '#00ff99', '#00cc66', '#33cc99',						
			'#66ffcc', '#33ffcc', '#00ffcc', '#00cc99', '#009966',						
			'#ccffff', '#99cccc', '#669999', '#336666', '#003333',						
			'#99ffff', '#66cccc', '#33cccc', '#339999', '#006666',						
			'#66cccc', '#33ffff', '#00ffff', '#00cccc', '#009999',						
			'#66ccff', '#33ccff', '#00ccff', '#0099cc', '#006699',																		
			'#99ccff', '#6699cc', '#3399cc', '#336699', '#003366',						
			'#3399ff', '#0099ff', '#0066ff', '#066ccc', '#3366cc',																		
			'#6699ff', '#3366ff', '#0033ff', '#0033cc', '#003399',						
			'#ccccff', '#9999cc', '#666699', '#333366', '#000033',																		
			'#9999ff', '#6666cc', '#3333cc', '#333399', '#000066',																		
			'#6666ff', '#3333ff', '#0000ff', '#0000cc', '#009999',																		
			'#9966ff', '#6633ff', '#3300ff', '#3300cc', '#330099',																		
			'#cc99ff', '#9966cc', '#6633cc', '#663399', '#330066',
			'#9933ff', '#6600ff', '#9900ff', '#6600cc', '#9933cc',			
			'#cc66ff', '#cc33ff', '#cc00ff', '#9900cc', '#660099',
			'#ffccff', '#cc99cc', '#996699', '#663366', '#330033',			
			'#ff99ff', '#cc66cc', '#cc33cc', '#993399', '#660066',
			'#ff66ff', '#ff33ff', '#ff00ff', '#cc00cc', '#990099',			
			'#ff66cc', '#ff33cc', '#ff00cc', '#cc0099', '#990066',
			'#ff99cc', '#cc6699', '#cc3399', '#993366', '#660033',			
			'#ff3399', '#ff0099', '#ff0066', '#cc0066', '#cc3366',
			'#ff6699', '#ff3366', '#ff0033', '#cc0033', '#990033'		
			]
	};

})(jQuery);
/**
 * jQuery plugin. Create group of text input, elSelect and elColorPicker. 
 * Allow input border-width, border-style and border-color. Used in elRTE
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
	
	$.fn.elBorderSelect = function(o) {
		
		var $self = this;
		var self  = this.eq(0);
		var opts  = $.extend({}, $.fn.elBorderSelect.defaults, o);
		var width = $('<input type="text" />').css('width','20px')
			.attr({'name' : opts.name+'[width]', size : 3}).css('text-align', 'right')
			.change(function() { $self.change(); });
		
		var color = $('<div />').css('position', 'relative')
			.elColorPicker({
				'class'         : 'el-colorpicker ui-icon ui-icon-pencil',
				name            : opts.name+'[color]', 
				palettePosition : 'outer',
				change          : function() { $self.change(); }
			});
		
		
		var style = $('<div />').elSelect({
			tpl       : '<div style="border-bottom:4px %val #000;width:100%;margin:7px 0"> </div>',
			tpls      : { '' : '%label'},
			maxHeight : opts.styleHeight || null,
			select    : function() { $self.change(); },
			src       : {
				''       : 'none',
				solid    : 'solid',
				dashed   : 'dashed',
				dotted   : 'dotted',
				'double' : 'double',
				groove   : 'groove',
				ridge    : 'ridge',
				inset    : 'inset',
				outset   : 'outset'
			}
		});
		
		self.empty()
			.addClass(opts['class'])
			.attr('name', opts.name||'')
			.append(
				$('<table />').attr('cellspacing', 0).append(
					$('<tr />')
						.append($('<td />').append(width).append(' px'))
						.append($('<td />').append(style))
						.append($('<td />').append(color))
				)
			);
		
		function rgb2hex(str) {
		    function hex(x)  {
		    	hexDigits = ["0", "1", "2", "3", "4", "5", "6", "7", "8","9", "a", "b", "c", "d", "e", "f"];
		        return !x  ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x% 16];
		    }
			var rgb = (str||'').match(/\(([0-9]{1,3}),\s*([0-9]{1,3}),\s*([0-9]{1,3})\)/); 
			return rgb ? "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]) : '';
		}
		
		function toPixels(num) {
			if (!num) {
				return num;
			}
			var m = num.match(/([0-9]+\.?[0-9]*)\s*(px|pt|em|%)/);
			if (m) {
				num  = m[1];
				unit = m[2];
			} 
			if (num[0] == '.') {
				num = '0'+num;
			}
			num = parseFloat(num);

			if (isNaN(num)) {
				return '';
			}
			var base = parseInt($(document.body).css('font-size')) || 16;
			switch (unit) {
				case 'em': return parseInt(num*base);
				case 'pt': return parseInt(num*base/12);
				case '%' : return parseInt(num*base/100);
			}
			return num;
		}
		
		this.change = function() {
			opts.change && opts.change(this.val());
		}
		
		this.val = function(v) {
			var w, s, c, b, m;
			
			if (!v && v !== '') {
				w = parseInt(width.val());
				w = !isNaN(w) ? w+'px' : '';
				s = style.val();
				c = color.val();
				return {
					width : w,
					style : s,
					color : c,
					css   : $.trim(w+' '+s+' '+c)
				}
			} else {
				b = '';
				if (v.nodeName || v.css) {
					if (!v.css) {
						v = $(v);					
					}
					b = v.css('border');
					if ((b = v.css('border'))) {
						w = s = c = b;
					} else {
						w = v.css('border-width');
						s = v.css('border-style');
						c = v.css('border-color');
					}

				} else {
					w = v.width||'';
					s = v.style||'';
					c = v.color||'';
				}

				width.val(toPixels(w));
				m = s ? s.match(/(solid|dashed|dotted|double|groove|ridge|inset|outset)/i) :'';
				style.val(m ? m[1] : '');
				color.val(c.indexOf('#') === 0 ? c : rgb2hex(c));
				return this;
			}
		}
		
		this.val(opts.value);
		return this;
	}
	
	$.fn.elBorderSelect.defaults = {
		name      : 'el-borderselect',
		'class'   : 'el-borderselect',
		value     : {},
		change    : null
	}
	
})(jQuery);
/**
 * jQuery plugin. Create group of text input fields and selects for setting padding/margin. Used in elRTE
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
	
	$.fn.elPaddingInput = function(o) {
		var self = this;
		var opts = $.extend({}, $.fn.elPaddingInput.defaults, {name : this.attr('name')}, o);
		this.regexps = {
			main   : new RegExp(opts.type == 'padding' ? 'padding\s*:\s*([^;"]+)'        : 'margin\s*:\s*([^;"]+)',       'im'),
			left   : new RegExp(opts.type == 'padding' ? 'padding-left\s*:\s*([^;"]+)'   : 'margin-left\s*:\s*([^;"]+)',  'im'),
			top    : new RegExp(opts.type == 'padding' ? 'padding-top\s*:\s*([^;"]+)'    : 'margin-top\s*:\s*([^;"]+)',    'im'),
			right  : new RegExp(opts.type == 'padding' ? 'padding-right\s*:\s*([^;"]+)'  : 'margin-right\s*:\s*([^;"]+)',  'im'),
			bottom : new RegExp(opts.type == 'padding' ? 'padding-bottom\s*:\s*([^;"]+)' : 'margin-bottom\s*:\s*([^;"]+)', 'im')
		};
			
		$.each(['left', 'top', 'right', 'bottom'], function() {
			
			self[this] = $('<input type="text" />').css('width','20px')
				.attr('size', 3)
				.css('text-align', 'right')
				.css('border-'+this, '2px solid red')
				.bind('change', function() { $(this).val(parseNum($(this).val())); change(); })
				.attr('name', opts.name+'['+this+']');
		});
		$.each(['uleft', 'utop', 'uright', 'ubottom'], function() {
			self[this] = $('<select />').css('width','40px')
				.append('<option value="px">px</option>')
				.append('<option value="em">em</option>')
				.append('<option value="pt">pt</option>')
				.bind('change', function() { change(); })
				.attr('name', opts.name+'['+this+']');
			if (opts.percents) {
				self[this].append('<option value="%">%</option>');
			}
		});
		
		this.empty().addClass(opts['class'])
			.append(this.left).append(this.uleft).append(' x ')
			.append(this.top).append(this.utop).append(' x ')
			.append(this.right).append(this.uright).append(' x ')
			.append(this.bottom).append(this.ubottom);
			
		this.val = function(v) {
			if (!v && v!=='') {
				var l = parseNum(this.left.val());
				var t = parseNum(this.top.val());
				var r = parseNum(this.right.val());
				var b = parseNum(this.bottom.val());
				var ret = {
					left   : l=='auto' || l==0 ? l : (l!=='' ? l+this.uleft.val()   : ''), 
					top    : t=='auto' || t==0 ? t : (t!=='' ? t+this.utop.val()    : ''),
					right  : r=='auto' || r==0 ? r : (r!=='' ? r+this.uright.val()  : ''),
					bottom : b=='auto' || b==0 ? b : (b!=='' ? b+this.ubottom.val() : ''),
					css    : ''
				};
				if (ret.left!=='' && ret.right!=='' && ret.top!=='' && ret.bottom!=='') {
					if (ret.left == ret.right && ret.top == ret.bottom) {
						ret.css = ret.top+' '+ret.left;
					} else{
						ret.css = ret.top+' '+ret.right+' '+ret.bottom+' '+ret.left;
					}
				}
				
				return ret;
			} else {
				
				if (v.nodeName || v.css) {
					if (!v.css) {
						v = $(v);
					}
					var val   = {left : '', top : '', right: '', bottom : ''};
					var style = (v.attr('style')||'').toLowerCase();

					if (style) {
						style   = $.trim(style);
						var m = style.match(this.regexps.main);
						if (m) {
							var tmp    = $.trim(m[1]).replace(/\s+/g, ' ').split(' ', 4);
							val.top    = tmp[0];
							val.right  = tmp[1] && tmp[1]!=='' ? tmp[1] : val.top;
							val.bottom = tmp[2] && tmp[2]!=='' ? tmp[2] : val.top;
							val.left   = tmp[3] && tmp[3]!=='' ? tmp[3] : val.right;
						} else {
							$.each(['left', 'top', 'right', 'bottom'], function() {
								var name = this.toString();
								m = style.match(self.regexps[name]);
								if (m) {
									val[name] = m[1];
								}
							});
						}
					}
					var v = val;
				} 

				$.each(['left', 'top', 'right', 'bottom'], function() {
					var name = this.toString();
					self[name].val('');
					self['u'+name].val();
					if (typeof(v[name]) != 'undefined' && v[name] !== null) {
						v[name] = v[name].toString();
						var _v = parseNum(v[name]);
						self[name].val(_v);
						var m = v[name].match(/(px|em|pt|%)/i);
						self['u'+name].val(m ? m[1] : 'px');
					}
				});
				return this;
			}
		}
			
		function parseNum(num) {
			num = $.trim(num.toString());
			if (num[0] == '.') { 
				num = '0'+num;
			}
			n = parseFloat(num);
			return !isNaN(n) ? n : (num == 'auto' ? num : '');
		}
			
		function change() {
			opts.change && opts.change(self);
		}
		
		this.val(opts.value);
		
		return this;
	}
	
	$.fn.elPaddingInput.defaults = {
		name     : 'el-paddinginput',
		'class'  : 'el-paddinginput',
		type     : 'padding',
		value    : {},
		percents : true,
		change   : null
	}
	
})(jQuery);
/**
 * elSelect JQuery plugin
 * Replacement for select input
 * Allow to put any html and css decoration in drop-down list
 *
 * Usage:
 *   $(selector).elSelect(opts)
 *
 * set value after init:
 *   var c = $(selector).elSelect(opts)
 *   c.val('some value')
 *
 * Get selected value:
 *   var val = c.val();
 *
 * Notice!
 *   1. When called on multiply elements, elSelect create drop-down list only for fist element
 *   2. Elements list created only after first click on element (lazzy loading)
 *
 * Options:
 *   src       - object with pairs value:label to create drop-down list 
 *   value     - current (selected) value
 *   class     - css class for display "button" (element on wich plugin was called)
 *   listClass - css class for drop down elements list
 *   select    - callback, called when value was selected (by default write value to console.log)
 *   name      - hidden text field in wich selected value will saved
 *   maxHeight - elements list max height (if height greater - scroll will appear)
 *   tpl       - template for element in list (contains 2 vars: %var - for src key, %label - for src[val] )
 *   labelTpl  - template for label (current selected element) (contains 2 placeholders: %var - for src key, %label - for src[val] )
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
	
	$.fn.elSelect = function(o) {
		var $self    = this;
		var self     = this.eq(0);
		var opts     = $.extend({}, $.fn.elSelect.defaults, o);
		var hidden   = $('<input type="hidden" />').attr('name', opts.name);
		var label    = $('<label />').attr({unselectable : 'on'}).addClass('rounded-left-3');
		var list     = null;
		var ieWidth  = null;

		if (self.get(0).nodeName == 'SELECT') {
			opts.src = {};
			self.children('option').each(function() {
				opts.src[$(this).val()] = $(this).text();
			});
			opts.value = self.val();
			opts.name  = self.attr('name');
			self.replaceWith((self = $('<div />')));
		}
		
		if (!opts.value || !opts.src[opts.val]) {
			opts.value = null;
			var i = 0;
			for (var v in opts.src) {
				if (i++ == 0) {
					opts.value = v;
				}
			}
		}

		this.val = function(v) {
			if (!v && v!=='') {
				return hidden.val();
			} else {
				if (opts.src[v]) {
					hidden.val(v);
					updateLabel(v);
					if (list) {
						list.children().each(function() {
							if ($(this).attr('name') == v) {
								$(this).addClass('active');
							} else {
								$(this).removeClass('active');
							}
						});
					}
				}
				return this;
			}
		}
	
		// update label content
		function updateLabel(v) {
			var tpl = opts.labelTpl || opts.tpls[v] || opts.tpl;
			label.html(tpl.replace(/%val/g, v).replace(/%label/, opts.src[v])).children().attr({unselectable : 'on'});
		}
		
		// init "select"
		self.empty()
			.addClass(opts['class']+' rounded-3')
			.attr({unselectable : 'on'})
			.append(hidden)
			.append(label)
			.hover(
				function() { $(this).addClass('hover') },
				function() { $(this).removeClass('hover') }
			)
			.click(function(e) {
				!list && init();
				list.slideToggle();
				// stupid ie inherit width from parent
				if ($.browser.msie && !ieWidth) { 
					list.children().each(function() {
						ieWidth = Math.max(ieWidth, $(this).width());
					});
					if (ieWidth > list.width()) {
						list.width(ieWidth+40);
					}
				}
			});
			
		this.val(opts.value);
	
		// create drop-down list
		function init() {
			// not ul because of ie is stupid with mouseleave in it :(
			list = $('<div />')
				.addClass(opts.listClass+' rounded-3')
				.hide()
				.appendTo(self.mouseleave(function(e) { list.slideUp(); }));

			for (var v in opts.src) {
				var tpl = opts.tpls[v] || opts.tpl; 
				$('<div />')
					.attr('name', v)
					.append( $(tpl.replace(/%val/g, v).replace(/%label/g, opts.src[v])).attr({unselectable : 'on'}) )
					.appendTo(list)
					.hover(
						function() { $(this).addClass('hover') },
						function() { $(this).removeClass('hover') }
					)
					.click(function(e) {
						e.stopPropagation();
						e.preventDefault();
						
						var v = $(this).attr('name');
						$self.val(v);
						opts.select(v);
						list.slideUp();
					});
			};
			
			var w = self.outerWidth();
			if (list.width() < w) {
				list.width(w);
			}
			
			var h = list.height();
			if (opts.maxHeight>0 && h>opts.maxHeight) {
				list.height(opts.maxHeight);
			}
			
			$self.val(hidden.val());
		}
		
		return this;
	}
	
	$.fn.elSelect.defaults = {
		name      : 'el-select',
		'class'   : 'el-select',
		listClass : 'list',
		labelTpl  : null,
		tpl       : '<%val>%label</%val>',
		tpls      : {},
		value     : null,
		src       : {},
		select    : function(v) {  window.console &&  window.console.log && window.console.log('selected: '+v); },
		maxHeight : 410
	}
	
})(jQuery);
/*
 * elRTE - WSWING editor for web
 *
 * Usage:
 * var opts = {
 *	.... // see elRTE.options.js
 * }
 * var editor = new elRTE($('#my-id').get(0), opts)
 * or
 * $('#my-id').elrte(opts)
 *
 * $('#my-id) may be textarea or any DOM Element with text
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 */
(function($) {

elRTE = function(target, opts) {
	if (!target || !target.nodeName) {
		return alert('elRTE: argument "target" is not DOM Element');
	}
	var self       = this, html;
	this.version   = '1.3';
	this.build     = '2011-06-23';
	this.options   = $.extend(true, {}, this.options, opts);
	this.browser   = $.browser;
	this.target    = $(target);
	
	this.lang      = (''+this.options.lang);
	this._i18n     = new eli18n({textdomain : 'rte', messages : { rte : this.i18Messages[this.lang] || {}} });
	this.rtl       = !!(/^(ar|fa|he)$/.test(this.lang) && this.i18Messages[this.lang]);
	
	if (this.rtl) {
		this.options.cssClass += ' el-rte-rtl';
	}
	this.toolbar   = $('<div class="toolbar"/>');
	this.iframe    = document.createElement('iframe');
	this.iframe.setAttribute('frameborder', 0); // fixes IE border

	// this.source    = $('<textarea />').hide();
	this.workzone  = $('<div class="workzone"/>').append(this.iframe).append(this.source);
	this.statusbar = $('<div class="statusbar"/>');
	this.tabsbar   = $('<div class="tabsbar"/>');
	this.editor    = $('<div class="'+this.options.cssClass+'" />').append(this.toolbar).append(this.workzone).append(this.statusbar).append(this.tabsbar);
	
	this.doc       = null;
	this.$doc      = null;
	this.window    = null;
	
	this.utils     = new this.utils(this);
	this.dom       = new this.dom(this);
	this.filter    = new this.filter(this)
	
	/**
	 * Sync iframes/textareas height with workzone height 
	 *
	 * @return void
	 */
	this.updateHeight = function() {
		self.workzone.add(self.iframe).add(self.source).height(self.workzone.height());
	}
	
	/**
	 * Turn editor resizable on/off if allowed
	 *
	 * @param  Boolean 
	 * @return void
	 **/
	this.resizable = function(r) {
		var self = this;
		if (this.options.resizable && $.fn.resizable) {
			if (r) {
				this.editor.resizable({handles : 'se', alsoResize : this.workzone, minWidth :300, minHeight : 200 }).bind('resize', self.updateHeight);
			} else {
				this.editor.resizable('destroy').unbind('resize', self.updateHeight);
			}
		}
	}
	
	/* attach editor to document */
	this.editor.insertAfter(target);
	/* init editor textarea */
	var content = '';
	if (target.nodeName == 'TEXTAREA') {
		this.source = this.target;
		this.source.insertAfter(this.iframe).hide();
		content = this.target.val();
	} else {
		this.source = $('<textarea />').insertAfter(this.iframe).hide();
		content = this.target.hide().html();
	}
	this.source.attr('name', this.target.attr('name')||this.target.attr('id'));
	content = $.trim(content);
	if (!content) {
		content = ' ';
	}

	/* add tabs */
	if (this.options.allowSource) {
		this.tabsbar.append('<div class="tab editor rounded-bottom-7 active">'+self.i18n('Editor')+'</div><div class="tab source rounded-bottom-7">'+self.i18n('Source')+'</div><div class="clearfix" style="clear:both"/>')
			.children('.tab').click(function(e) {
				if (!$(this).hasClass('active')) {
					self.tabsbar.children('.tab').toggleClass('active');
					self.workzone.children().toggle();

					if ($(this).hasClass('editor')) {
						self.updateEditor();
						self.window.focus();
						self.ui.update(true);
					} else {
						self.updateSource();
						self.source.focus();
						if ($.browser.msie) {
							// @todo
						} else {
							self.source[0].setSelectionRange(0, 0);
						}
						self.ui.disable();
						self.statusbar.empty();
						
					}
				}
				
			});
	}
	
	this.window = this.iframe.contentWindow;
	this.doc    = this.iframe.contentWindow.document;
	this.$doc   = $(this.doc);
	
	/* put content into iframe */
	html = '<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
	$.each(self.options.cssfiles, function() {
		html += '<link rel="stylesheet" type="text/css" href="'+this+'" />';
	});
	this.doc.open();
	var s = this.filter.wysiwyg(content),
		cl = this.rtl ? ' class="el-rte-rtl"' : '';
	this.doc.write(self.options.doctype+html+'</head><body'+cl+'>'+(s)+'</body></html>');
	this.doc.close();
	
	/* make iframe editable */
	if ($.browser.msie) {
		this.doc.body.contentEditable = true;
	} else {
		try { this.doc.designMode = "on"; } 
		catch(e) { }
		this.doc.execCommand('styleWithCSS', false, this.options.styleWithCSS);
	}
	
	if (this.options.height>0) {
		this.workzone.height(this.options.height);
	}
	if (this.options.width>0) {
		this.editor.width(this.options.width);
	}
	
	this.updateHeight();
	this.resizable(true);
	this.window.focus();
	
	this.history = new this.history(this);
	
	/* init selection object */
	this.selection = new this.selection(this);
	/* init buttons */
	this.ui = new this.ui(this);
	
	
	/* bind updateSource to parent form submit */
	this.target.parents('form').bind('submit.elfinder', function(e) {
		self.source.parents('form').find('[name="el-select"]').remove()
		self.beforeSave();
	});
	
	// on tab press - insert \t and prevent move focus
	this.source.bind('keydown', function(e) {
		if (e.keyCode == 9) {
			e.preventDefault();
				
			if ($.browser.msie) {
				var r = document.selection.createRange();
				r.text = "\t"+r.text;
				this.focus();
			} else {
				var before = this.value.substr(0, this.selectionStart),
					after = this.value.substr(this.selectionEnd);
				this.value = before+"\t"+after;
				this.setSelectionRange(before.length+1, before.length+1);
			}
		}
	});
	
	$(this.doc.body).bind('dragend', function(e) {
		setTimeout(function() {
			try {
				self.window.focus();
				var bm = self.selection.getBookmark();
				self.selection.moveToBookmark(bm);
				self.ui.update();
			} catch(e) { }
			
			
		}, 200);
		
	});
	
	this.typing = false;
	this.lastKey = null;
	/* update buttons on click and keyup */
	this.$doc.bind('mouseup', function() {
		self.typing = false;
		self.lastKey = null;
		self.ui.update();
	})
	.bind('keyup', function(e) {
		if ((e.keyCode >= 8 && e.keyCode <= 13) || (e.keyCode>=32 && e.keyCode<= 40) || e.keyCode == 46 || (e.keyCode >=96 && e.keyCode <= 111)) {
			self.ui.update();
		}
	})
	.bind('keydown', function(e) {
		if ((e.metaKey || e.ctrlKey) && e.keyCode == 65) {
			self.ui.update();
		} else if (e.keyCode == 13) {
			var n = self.selection.getNode();
			// self.log(n)
			if (self.dom.selfOrParent(n, /^PRE$/)) {
				self.selection.insertNode(self.doc.createTextNode("\r\n"));
				return false;
			} else if ($.browser.safari && e.shiftKey) {
				self.selection.insertNode(self.doc.createElement('br'))
				return false;
			}
		}

		if ((e.keyCode>=48 && e.keyCode <=57) || e.keyCode==61 || e.keyCode == 109 || (e.keyCode>=65 && e.keyCode<=90) || e.keyCode==188 ||e.keyCode==190 || e.keyCode==191 || (e.keyCode>=219 && e.keyCode<=222)) {
			if (!self.typing) {
				self.history.add(true);
			}
			self.typing = true;
			self.lastKey = null;
		} else if (e.keyCode == 8 || e.keyCode == 46 || e.keyCode == 32 || e.keyCode == 13) {
			if (e.keyCode != self.lastKey) {
				self.history.add(true);
			}
			self.lastKey = e.keyCode;
			self.typing = false;
		}
		
		if (e.keyCode == 32 && $.browser.opera) {
			self.selection.insertNode(self.doc.createTextNode(" "));
			return false
		}
	})
	.bind('paste', function(e) {
		if (!self.options.allowPaste) {
			// paste denied 
			e.stopPropagation();
			e.preventDefault();
		} else {
			var n = $(self.dom.create('div'))[0],
				r = self.doc.createTextNode('_');
			self.history.add(true);
			self.typing = true;
			self.lastKey = null;
			n.appendChild(r);
			self.selection.deleteContents().insertNode(n);
			self.selection.select(r);
			setTimeout(function() {
				if (n.parentNode) {
					// clean sandbox content
					$(n).html(self.filter.proccess('paste', $(n).html()));
					r = n.lastChild;
					self.dom.unwrap(n);
					if (r) {
						self.selection.select(r);
						self.selection.collapse(false);
					}
				} else {
					// smth wrong - clean all doc
					n.parentNode && n.parentNode.removeChild(n);
					self.val(self.filter.proccess('paste', self.filter.wysiwyg2wysiwyg($(self.doc.body).html())));
					self.selection.select(self.doc.body.firstChild);
					self.selection.collapse(true);
				}
				$(self.doc.body).mouseup(); // to activate history buutons
			}, 15);
		}
	});
	
	if ($.browser.msie) {
		this.$doc.bind('keyup', function(e) {
			if (e.keyCode == 86 && (e.metaKey||e.ctrlKey)) {
				self.history.add(true);
				self.typing = true;
				self.lastKey = null;
				self.selection.saveIERange();
				self.val(self.filter.proccess('paste', self.filter.wysiwyg2wysiwyg($(self.doc.body).html())));
				self.selection.restoreIERange();
				$(self.doc.body).mouseup();
				this.ui.update();
			}
		});
	}
	
	if ($.browser.safari) {
		this.$doc.bind('click', function(e) {
			$(self.doc.body).find('.elrte-webkit-hl').removeClass('elrte-webkit-hl');
			if (e.target.nodeName == 'IMG') {
				$(e.target).addClass('elrte-webkit-hl');
			}
		}).bind('keyup', function(e) {
			$(self.doc.body).find('.elrte-webkit-hl').removeClass('elrte-webkit-hl');
		})
	}
	
	this.window.focus();
	
	this.destroy = function() {
		this.updateSource();
		this.target.is('textarea')
			? this.target.val($.trim(this.source.val()))
			: this.target.html($.trim(this.source.val()));
		this.editor.remove();
		this.target.show().parents('form').unbind('submit.elfinder');
	}
	
}

/**
 * Return message translated to selected language
 *
 * @param  string  msg  message text in english
 * @return string
 **/
elRTE.prototype.i18n = function(msg) {
	return this._i18n.translate(msg);
}



/**
 * Display editor
 *
 * @return void
 **/
elRTE.prototype.open = function() {
	this.editor.show();
}

/**
 * Hide editor and display elements on wich editor was created
 *
 * @return void
 **/
elRTE.prototype.close = function() {
	this.editor.hide();
}

elRTE.prototype.updateEditor = function() {
	this.val(this.source.val());
}

elRTE.prototype.updateSource = function() {
	this.source.val(this.filter.source($(this.doc.body).html()));
}

/**
 * Return edited text
 *
 * @return String
 **/
elRTE.prototype.val = function(v) {
	if (typeof(v) == 'string') {
		v = ''+v;
		if (this.source.is(':visible')) {
			this.source.val(this.filter.source2source(v));
		} else {
			if ($.browser.msie) {
				this.doc.body.innerHTML = '<br />'+this.filter.wysiwyg(v);
				this.doc.body.removeChild(this.doc.body.firstChild);
			} else {
				this.doc.body.innerHTML = this.filter.wysiwyg(v);
			}
			
		}
	} else {
		if (this.source.is(':visible')) {
			return this.filter.source2source(this.source.val()).trim();
		} else {
			return this.filter.source($(this.doc.body).html()).trim();
		}
	}
}

elRTE.prototype.beforeSave = function() {
	this.source.val($.trim(this.val())||'');
}

/**
 * Submit form
 *
 * @return void
 **/
elRTE.prototype.save = function() {
	this.beforeSave();
	this.editor.parents('form').submit();
}

elRTE.prototype.log = function(msg) {
	if (window.console && window.console.log) {
		window.console.log(msg);
	}
        
}

elRTE.prototype.i18Messages = {};

$.fn.elrte = function(o, v) { 
	var cmd = typeof(o) == 'string' ? o : '', ret;
	
	this.each(function() {
		if (!this.elrte) {
			this.elrte = new elRTE(this, typeof(o) == 'object' ? o : {});
		}
		switch (cmd) {
			case 'open':
			case 'show':
				this.elrte.open();
				break;
			case 'close':
			case 'hide':
				this.elrte.close();
				break;
			case 'updateSource':
				this.elrte.updateSource();
				break;
			case 'destroy':
				this.elrte.destroy();
		}
	});
	
	if (cmd == 'val') {
		if (!this.length) {
			return '';
		} else if (this.length == 1) {
			return v ? this[0].elrte.val(v) : this[0].elrte.val();
		} else {
			ret = {}
			this.each(function() {
				ret[this.elrte.source.attr('name')] = this.elrte.val();
			});
			return ret;
		}
	}
	return this;
}

})(jQuery);
/*
 * DOM utilites for elRTE 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 */
(function($) {
elRTE.prototype.dom = function(rte) {
	this.rte = rte;
	var self = this;
	this.regExp = {
		textNodes         : /^(A|ABBR|ACRONYM|ADDRESS|B|BDO|BIG|BLOCKQUOTE|CAPTION|CENTER|CITE|CODE|DD|DEL|DFN|DIV|DT|EM|FIELDSET|FONT|H[1-6]|I|INS|KBD|LABEL|LEGEND|LI|MARQUEE|NOBR|NOEMBED|P|PRE|Q|SAMP|SMALL|SPAN|STRIKE|STRONG|SUB|SUP|TD|TH|TT|VAR)$/,
		textContainsNodes : /^(A|ABBR|ACRONYM|ADDRESS|B|BDO|BIG|BLOCKQUOTE|CAPTION|CENTER|CITE|CODE|DD|DEL|DFN|DIV|DL|DT|EM|FIELDSET|FONT|H[1-6]|I|INS|KBD|LABEL|LEGEND|LI|MARQUEE|NOBR|NOEMBED|OL|P|PRE|Q|SAMP|SMALL|SPAN|STRIKE|STRONG|SUB|SUP|TABLE|THEAD|TBODY|TFOOT|TD|TH|TR|TT|UL|VAR)$/,
		block             : /^(APPLET|BLOCKQUOTE|BR|CAPTION|CENTER|COL|COLGROUP|DD|DIV|DL|DT|H[1-6]|EMBED|FIELDSET|LI|MARQUEE|NOBR|OBJECT|OL|P|PRE|TABLE|THEAD|TBODY|TFOOT|TD|TH|TR|UL)$/,
		selectionBlock    : /^(APPLET|BLOCKQUOTE|BR|CAPTION|CENTER|COL|COLGROUP|DD|DIV|DL|DT|H[1-6]|EMBED|FIELDSET|LI|MARQUEE|NOBR|OBJECT|OL|P|PRE|TD|TH|TR|UL)$/,		
		header            : /^H[1-6]$/,
		formElement       : /^(FORM|INPUT|HIDDEN|TEXTAREA|SELECT|BUTTON)$/
	};
	
	/********************************************************/
	/*                      Утилиты                         */
	/********************************************************/	
	
	/**
	 * Возвращает body редактируемого документа
	 *
	 * @return Element
	 **/
	this.root = function() {
		return this.rte.body;
	}

	this.create = function(t) {
		return this.rte.doc.createElement(t);
	}

	/**
	 * Return node for bookmark with unique ID
	 *
	 * @return DOMElement
	 **/
	this.createBookmark = function() {
		var b = this.rte.doc.createElement('span');
		b.id = 'elrte-bm-'+Math.random().toString().substr(2);
		$(b).addClass('elrtebm elrte-protected');
		return b;
	}

	/**
	 * Вовращает индекс элемента внутри родителя
	 *
	 * @param  Element n  нода
	 * @return integer
	 **/
	this.indexOf = function(n) {
		var ndx = 0;
		n = $(n);
		while ((n = n.prev()) && n.length) {
			ndx++;
		}
		return ndx;
	}
	
	/**
	 * Вовращает значение аттрибута в нижнем регистре (ох уж этот IE)
	 *
	 * @param  Element n  нода
	 * @param  String  attr имя аттрибута
	 * @return string
	 **/
	this.attr = function(n, attr) {
		var v = '';
		if (n.nodeType == 1) {
			v = $(n).attr(attr);
			if (v && attr != 'src' && attr != 'href' && attr != 'title' && attr != 'alt') {
				v = v.toString().toLowerCase();
			}
		} 
		return v||'';
	}
	
	/**
	 * Вовращает ближайший общий контейнер для 2-х эл-тов
	 *
	 * @param  Element n  нода1
	 * @param  Element n  нода2
	 * @return Element
	 **/
	this.findCommonAncestor = function(n1, n2) {
		if (!n1 || !n2) {
			return this.rte.log('dom.findCommonAncestor invalid arguments');
		}
		if (n1 == n2) {
			return n1;
		} else if (n1.nodeName == 'BODY' || n2.nodeName == 'BODY') {
			return this.rte.doc.body;
		}
		var p1 = $(n1).parents(), p2 = $(n2).parents(), l  = p2.length-1, c  = p2[l];
		for (var i = p1.length - 1; i >= 0; i--, l--){
			if (p1[i] == p2[l]) {
				c = p1[i];
			} else {
				break;
			}
		};
		return c;
	}
	/**
	 * Вовращает TRUE, если нода пустая
	 * пустой считаем ноды:
	 *  - текстовые эл-ты, содержащие пустую строку или тег br
	 *  - текстовые ноды с пустой строкой
	 *
	 * @param  DOMElement n  нода
	 * @return bool
	 **/
	this.isEmpty = function(n) {
		if (n.nodeType == 1) {
			return this.regExp.textNodes.test(n.nodeName) ? $.trim($(n).text()).length == 0 : false;
		} else if (n.nodeType == 3) {
			return /^(TABLE|THEAD|TFOOT|TBODY|TR|UL|OL|DL)$/.test(n.parentNode.nodeName)
				|| n.nodeValue == ''
				|| ($.trim(n.nodeValue).length== 0 && !(n.nextSibling && n.previousSibling && n.nextSibling.nodeType==1 && n.previousSibling.nodeType==1 && !this.regExp.block.test(n.nextSibling.nodeName) && !this.regExp.block.test(n.previousSibling.nodeName) ));
		}
		return true;
	}

	/********************************************************/
	/*                  Перемещение по DOM                  */
	/********************************************************/

	/**
	 * Вовращает следующую соседнюю ноду (не включаются текстовые ноды не создающие значимые пробелы между инлайн элементами)
	 *
	 * @param  DOMElement n  нода
	 * @return DOMElement
	 **/
	this.next = function(n) {
		while (n.nextSibling && (n = n.nextSibling)) {
			if (n.nodeType == 1 || (n.nodeType == 3 && !this.isEmpty(n))) {
				return n;
			}
		}
		return null;
	}

	/**
	 * Вовращает предыдующую соседнюю ноду (не включаются текстовые ноды не создающие значимые пробелы между инлайн элементами)
	 *
	 * @param  DOMElement n  нода
	 * @return DOMElement
	 **/
	this.prev = function(n) {
		while (n.previousSibling && (n = n.previousSibling)) {
			if (n.nodeType == 1 || (n.nodeType ==3 && !this.isEmpty(n))) {
				return n;
			}
		}
		return null;
	}

	this.isPrev = function(n, prev) {
		while ((n = this.prev(n))) {
			if (n == prev) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Вовращает все следующие соседнии ноды (не включаются текстовые ноды не создающие значимые пробелы между инлайн элементами)
	 *
	 * @param  DOMElement n  нода
	 * @return Array
	 **/
	this.nextAll = function(n) {
		var ret = [];
		while ((n = this.next(n))) {
			ret.push(n);
		}
		return ret;
	}
	
	/**
	 * Вовращает все предыдующие соседнии ноды (не включаются текстовые ноды не создающие значимые пробелы между инлайн элементами)
	 *
	 * @param  DOMElement n  нода
	 * @return Array
	 **/
	this.prevAll = function(n) {
		var ret = [];
		while ((n = this.prev(n))) {
			ret.push(n);
		}
		return ret;
	}
	
	/**
	 * Вовращает все следующие соседнии inline ноды (не включаются текстовые ноды не создающие значимые пробелы между инлайн элементами)
	 *
	 * @param  DOMElement n  нода
	 * @return Array
	 **/
	this.toLineEnd = function(n) {
		var ret = [];
		while ((n = this.next(n)) && n.nodeName != 'BR' && n.nodeName != 'HR' && this.isInline(n)) {
			ret.push(n);
		}
		return ret;
	}
	
	/**
	 * Вовращает все предыдующие соседнии inline ноды (не включаются текстовые ноды не создающие значимые пробелы между инлайн элементами)
	 *
	 * @param  DOMElement n  нода
	 * @return Array
	 **/
	this.toLineStart = function(n) {
		var ret = [];
		while ((n = this.prev(n)) && n.nodeName != 'BR' && n.nodeName != 'HR' && this.isInline(n) ) {
			ret.unshift(n);
		}
		return ret;
	}
	
	/**
	 * Вовращает TRUE, если нода - первый непустой эл-т внутри родителя
	 *
	 * @param  Element n  нода
	 * @return bool
	 **/
	this.isFirstNotEmpty = function(n) {
		while ((n = this.prev(n))) {
			if (n.nodeType == 1 || (n.nodeType == 3 && $.trim(n.nodeValue)!='' ) ) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Вовращает TRUE, если нода - последний непустой эл-т внутри родителя
	 *
	 * @param  Element n  нода
	 * @return bool
	 **/
	this.isLastNotEmpty = function(n) {
		while ((n = this.next(n))) {
			if (!this.isEmpty(n)) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Вовращает TRUE, если нода - единственный непустой эл-т внутри родителя
	 *
	 * @param  DOMElement n  нода
	 * @return bool
	 **/
	this.isOnlyNotEmpty = function(n) {
		return this.isFirstNotEmpty(n) && this.isLastNotEmpty(n);
	}
	
	/**
	 * Вовращает последний непустой дочерний эл-т ноды или FALSE
	 *
	 * @param  Element n  нода
	 * @return Element
	 **/
	this.findLastNotEmpty = function(n) {
		this.rte.log('findLastNotEmpty Who is here 0_o');
		if (n.nodeType == 1 && (l = n.lastChild)) {
			if (!this.isEmpty(l)) {
				return l;
			}
			while (l.previousSibling && (l = l.previousSibling)) {
				if (!this.isEmpty(l)) {
					return l;
				}
			}
		}
		return false;
	}
	
	/**
	 * Возвращает TRUE, если нода "inline" 
	 *
	 * @param  DOMElement n  нода
	 * @return bool
	 **/
	this.isInline = function(n) {
		if (n.nodeType == 3) {
			return true;
		} else if (n.nodeType == 1) {
			n = $(n);
			var d = n.css('display');
			var f = n.css('float');
			return d == 'inline' || d == 'inline-block' || f == 'left' || f == 'right';
		}
		return true;
	}
	
	
	/********************************************************/
	/*                  Поиск элементов                     */
	/********************************************************/
	
	this.is = function(n, f) {
		if (n && n.nodeName) {

			if (typeof(f) == 'string') {
				f = this.regExp[f]||/.?/;
			}

			if (f instanceof RegExp && n.nodeName) {
				return f.test(n.nodeName);
			} else if (typeof(f) == 'function') {
				return f(n);
			}
		}
		
		return false;
	}
	
	/**
	 * Вовращает элемент(ы) отвечающие условиям поиска
	 *
	 * @param  DOMElement||Array  n       нода
	 * @param  RegExp||String     filter  фильтр условия поиска (RegExp или имя ключа this.regExp или *)
	 * @return DOMElement||Array
	 **/
	this.filter = function(n, filter) {
		var ret = [], i;
		if (!n.push) {
			return this.is(n, filter) ? n : null;
		}
		for (i=0; i < n.length; i++) {
			if (this.is(n[i], filter)) {
				ret.push(n[i]);
			}
		};
		return ret;
	}
	
	
	/**
	 * Вовращает массив родительских элементов, отвечающих условиям поиска
	 *
	 * @param  DOMElement      n  нода, родителей, которой ищем
	 * @param  RegExp||String  filter   фильтр условия поиска (RegExp или имя ключа this.regExp или *)
	 * @return Array
	 **/
	this.parents = function(n, filter) {
		var ret = [];

		while (n && (n = n.parentNode) && n.nodeName != 'BODY' && n.nodeName != 'HTML') {
			if (this.is(n, filter)) {
				ret.push(n);
			}
		}
		return ret;
	}
	
	/**
	 * Вовращает ближайший родительский эл-т, отвечающий условиям поиска
	 *
	 * @param  DOMElement     n  нода, родителя, которой ищем
	 * @param  RegExp||String f   фильтр условия поиска (RegExp или имя ключа this.regExp или *)
	 * @return DOMElement
	 **/
	this.parent = function(n, f) { 
		return this.parents(n, f)[0] || null; 
	}
	
	/**
	 * Вовращает или саму ноду или ее ближайшего родителя, если выполняются условия sf для самой ноды или pf для родителя
	 *
	 * @param  DOMElement     n  нода, родителя, которой ищем
	 * @param  RegExp||String sf   фильтр условия для самой ноды
	* @param  RegExp||String  pf   фильтр условия для родителя
	 * @return DOMElement
	 **/
	this.selfOrParent = function(n, sf, pf) {
		return this.is(n, sf) ? n : this.parent(n, pf||sf);
	}
	
	/**
	 * Вовращает родительскую ноду - ссылку
	 *
	 * @param  Element n  нода
	 * @return Element
	 **/
	this.selfOrParentLink = function(n) {
		n = this.selfOrParent(n, /^A$/);
		return n && n.href ? n : null;
	}

	/**
	 * Вовращает TRUE, если нода -  anchor
	 *
	 * @param  Element n  нода
	 * @return bool
	 **/
	this.selfOrParentAnchor = function(n) {
		n = this.selfOrParent(n, /^A$/);
		return n && !n.href && n.name ? n : null;
	}

	/**
	 * Вовращает массив дочерних ссылок
	 *
	 * @param  DOMElement n  нода
	 * @return Array
	 **/
	this.childLinks = function(n) {
		var res = [];
		$('a[href]', n).each(function() { res.push(this); });
		return res;
	}
	
	this.selectionHas = function(f) {
		var n = this.rte.selection.cloneContents(), i;
		if (n && n.childNodes && n.childNodes.length) {
			for (i=0; i < n.childNodes.length; i++) {
				if (typeof(f) == 'function') {
					if (f(n.childNodes[i])) {
						return true;
					}
				} else if (n instanceof RegExp) {
					if (f.test(n.childNodes[i].nodeName)) {
						return true;
					}
				}
			};
		}
		
		return false;
	}
	/********************************************************/
	/*                    Изменения DOM                     */
	/********************************************************/
	
	/**
	 * Оборачивает одну ноду другой
	 *
	 * @param  DOMElement n  оборачиваемая нода
	 * @param  DOMElement w  нода обертка или имя тега
	 * @return DOMElement
	 **/
	this.wrap = function(n, w) {
		n = $.isArray(n) ? n : [n];
		w = w.nodeName ? w : this.create(w);
		
		if (n[0] && n[0].nodeType && n[0].parentNode) {
			w = n[0].parentNode.insertBefore(w, n[0]);
			$(n).each(function() {
				if (this!=w) {
					w.appendChild(this);
				}
			});
		}
		
		return w;
	}
	
	/**
	 * Replace node with its contents
	 *
	 * @param  DOMElement n node
	 * @return void
	 **/
	this.unwrap = function(n) {
		if (n && n.parentNode) {
			while (n.firstChild) {
				n.parentNode.insertBefore(n.firstChild, n);
			}
			n.parentNode.removeChild(n);
			
		}
	}
	
	/**
	 * Оборачивает все содержимое ноды
	 *
	 * @param  DOMElement n  оборачиваемая нода
	 * @param  DOMElement w  нода обертка или имя тега
	 * @return DOMElement
	 **/
	this.wrapContents = function(n, w) {
		w = w.nodeName ? w : this.create(w);
		for (var i=0; i < n.childNodes.length; i++) {
			w.appendChild(n.childNodes[i]);
		};
		n.appendChild(w);
		return w;
	}
	
	this.cleanNode = function(n) {

		if (n.nodeType != 1) {
			return;
		}
		if (/^(P|LI)$/.test(n.nodeName) && (l = this.findLastNotEmpty(n)) && l.nodeName == 'BR') {
			$(l).remove();
		}
		$n = $(n);
		$n.children().each(function() {
			this.cleanNode(this);
		});
		if (n.nodeName != 'BODY' && !/^(TABLE|TR|TD)$/.test(n) && this.isEmpty(n)) {
			return $n.remove();
		}
		if ($n.attr('style') === '') {
			$n.removeAttr('style');
		}
		if (this.rte.browser.safari && $n.hasClass('Apple-span')) {
			$n.removeClass('Apple-span');
		}
		if (n.nodeName == 'SPAN' && !$n.attr('style') && !$n.attr('class') && !$n.attr('id')) {
			$n.replaceWith($n.html());
		}
	}
	
	this.cleanChildNodes = function(n) {
		var cmd = this.cleanNode;
		$(n).children().each(function() { cmd(this); });
	}
	
	/********************************************************/
	/*                       Таблицы                        */
	/********************************************************/
	
	this.tableMatrix = function(n) {
		var mx = [];
		if (n && n.nodeName == 'TABLE') {
			var max = 0;
			function _pos(r) {
				for (var i=0; i<=max; i++) {
					if (!mx[r][i]) {
						return i;
					}
				};
			}
			
			$(n).find('tr').each(function(r) {
				if (!$.isArray(mx[r])) {
					mx[r] = [];
				}
				
				$(this).children('td,th').each(function() {
					var w = parseInt($(this).attr('colspan')||1);
					var h = parseInt($(this).attr('rowspan')||1);
					var i = _pos(r);
					for (var y=0; y<h; y++) {
						for (var x=0; x<w; x++) {
							var _y = r+y;
							if (!$.isArray(mx[_y])) {
								mx[_y] = [];
							}
							var d = x==0 && y==0 ? this : (y==0 ? x : "-");
							mx[_y][i+x] = d;
						}
					};
					max= Math.max(max, mx[r].length);
				});
			});
		}
		return mx;
	}
	
	this.indexesOfCell = function(n, tbm) {
		for (var rnum=0; rnum < tbm.length; rnum++) {
			for (var cnum=0; cnum < tbm[rnum].length; cnum++) {
				if (tbm[rnum][cnum] == n) {
					return [rnum, cnum];
				}
				
			};
		};
	}
	
	this.fixTable = function(n) {
		if (n && n.nodeName == 'TABLE') {
			var tb = $(n);
			//tb.find('tr:empty').remove();
			var mx = this.tableMatrix(n);
			var x  = 0;
			$.each(mx, function() {
				x = Math.max(x, this.length);
			});
			if (x==0) {
				return tb.remove();
			}
			// for (var i=0; i<mx.length; i++) {
			// 	this.rte.log(mx[i]);
			// }
			
			for (var r=0; r<mx.length; r++) {
				var l = mx[r].length;
				//this.rte.log(r+' : '+l)
				
				if (l==0) {
					//this.rte.log('remove: '+tb.find('tr').eq(r))
					tb.find('tr').eq(r).remove();
//					tb.find('tr').eq(r).append('<td>remove</td>')
				} else if (l<x) {
					var cnt = x-l;
					var row = tb.find('tr').eq(r);
					for (i=0; i<cnt; i++) {
						row.append('<td>&nbsp;</td>');
					}
				}
			}
			
		}
	}
	
	this.tableColumn = function(n, ext, fix) {
		n      = this.selfOrParent(n, /^TD|TH$/);
		var tb = this.selfOrParent(n, /^TABLE$/);
		ret    = [];
		info   = {offset : [], delta : []};
		if (n && tb) {
			fix && this.fixTable(tb);
			var mx = this.tableMatrix(tb);
			var _s = false;
			var x;
			for (var r=0; r<mx.length; r++) {
				for (var _x=0; _x<mx[r].length; _x++) {
					if (mx[r][_x] == n) {
						x = _x;
						_s = true;
						break;
					}
				}
				if (_s) {
					break;
				}
			}
			
			// this.rte.log('matrix');
			// for (var i=0; i<mx.length; i++) {
			// 	this.rte.log(mx[i]);
			// }
			if (x>=0) {
				for(var r=0; r<mx.length; r++) {
					var tmp = mx[r][x]||null;
					if (tmp) {
						if (tmp.nodeName) {
							ret.push(tmp);
							if (ext) {
								info.delta.push(0);
								info.offset.push(x);
							}
						} else {
							var d = parseInt(tmp);
							if (!isNaN(d) && mx[r][x-d] && mx[r][x-d].nodeName) {
								ret.push(mx[r][x-d]);
								if (ext) {
									info.delta.push(d);
									info.offset.push(x);
								}
							}
						}
					}
				}
			}
		}
		return !ext ? ret : {column : ret, info : info};
	}
}

})(jQuery);
(function($) {
	/**
	 * @class Filter - clean editor content
	 * @param elRTE editor instance
	 * @author Dmitry (dio) Levashov, dio@std42.ru
	 */
	elRTE.prototype.filter = function(rte) {
		var self = this,
			n = $('<span/>').addClass('elrtetesturl').appendTo(document.body)[0];
		// media replacement image base url
		this.url = (typeof(n.currentStyle )!= "undefined" ? n.currentStyle['backgroundImage'] : document.defaultView.getComputedStyle(n, null)['backgroundImage']).replace(/^url\((['"]?)([\s\S]+\/)[\s\S]+\1\)$/i, "$2");
		$(n).remove();

		this.rte = rte;
		// flag - return xhtml tags?
		this.xhtml = /xhtml/i.test(rte.options.doctype);
		// boolean attributes
		this.boolAttrs = rte.utils.makeObject('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'.split(','));
		// tag regexp
		this.tagRegExp = /<(\/?)([\w:]+)((?:\s+[a-z\-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*\/?>/g;
		// this.tagRegExp = /<(\/?)([\w:]+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*\/?>/g;		
		// opened tag regexp
		this.openTagRegExp = /<([\w:]+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*\/?>/g;
		// attributes regexp
		this.attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^\s]+))?/g;
		// script tag regexp
		this.scriptRegExp = /<script([^>]*)>([\s\S]*?)<\/script>/gi;
		// style tag regexp
		this.styleRegExp = /(<style([^>]*)>[\s\S]*?<\/style>)/gi;
		// link tag regexp
		this.linkRegExp = /(<link([^>]+)>)/gi;
		// cdata regexp
		this.cdataRegExp = /<!\[CDATA\[([\s\S]+)\]\]>/g;
		// object tag regexp
		this.objRegExp = /<object([^>]*)>([\s\S]*?)<\/object>/gi;
		// embed tag regexp
		this.embRegExp = /<(embed)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*>/gi;
		// param tag regexp
		this.paramRegExp = /<(param)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*>/gi;
		// iframe tag regexp
		this.iframeRegExp = /<iframe([^>]*)>([\s\S]*?)<\/iframe>/gi;

		// yandex maps regexp
		this.yMapsRegExp = /<div\s+([^>]*id\s*=\s*('|")?YMapsID[^>]*)>/gi;
		// google maps regexp
		this.gMapsRegExp = /<iframe\s+([^>]*src\s*=\s*"http:\/\/maps\.google\.\w+[^>]*)>([\s\S]*?)<\/iframe>/gi;
		// video hostings url regexp
		this.videoHostRegExp = /^(http:\/\/[\w\.]*)?(youtube|vimeo|rutube).*/i;
		// elrte services classes regexp
		this.serviceClassRegExp = /<(\w+)([^>]*class\s*=\s*"[^>]*elrte-[^>]*)>\s*(<\/\1>)?/gi;
		this.pagebreakRegExp = /<(\w+)([^>]*style\s*=\s*"[^>]*page-break[^>]*)>\s*(<\/\1>)?/gi;
		
		this.pbRegExp = new RegExp('<!-- pagebreak -->', 'gi');
		// allowed tags
		this.allowTags = rte.options.allowTags.length ? rte.utils.makeObject(rte.options.allowTags) : null;
		// denied tags
		this.denyTags = rte.options.denyTags.length ? rte.utils.makeObject(rte.options.denyTags) : null;
		// deny attributes
		this.denyAttr = rte.options.denyAttr ? rte.utils.makeObject(rte.options.denyAttr) : null;
		// deny attributes for pasted html
		this.pasteDenyAttr = rte.options.pasteDenyAttr ? rte.utils.makeObject(rte.options.pasteDenyAttr) : null;
		// font sizes to convert size attr into css property
		this.fontSize = ['medium', 'xx-small', 'small', 'medium','large','x-large','xx-large' ];
		// font families regexp to detect family by font name
		this.fontFamily = {
			'sans-serif' : /^(arial|tahoma|verdana)$/i,
			'serif'      : /^(times|times new roman)$/i,
			'monospace'  : /^courier$/i
		}
		// scripts storage
		this.scripts = {};
		// cached chains of rules
		this._chains = {};
		
		// cache chains
		$.each(this.chains, function(n) {
			self._chains[n] = [];
			$.each(this, function(i, r) {
				typeof(self.rules[r]) == 'function' && self._chains[n].push(self.rules[r]);
			});
		});

		/**
		 * filtering through required chain
		 *
		 * @param  String  chain name
		 * @param  String  html-code
		 * @return String
		 **/
		this.proccess = function(chain, html) {
			// remove whitespace at the begin and end
			html = $.trim(html).replace(/^\s*(&nbsp;)+/gi, '').replace(/(&nbsp;|<br[^>]*>)+\s*$/gi, '');
			// pass html through chain
			$.each(this._chains[chain]||[], function() {
				html = this.call(self, html);
			});
			html = html.replace(/\t/g, '  ').replace(/\r/g, '').replace(/\s*\n\s*\n+/g, "\n")+'  ';
			return $.trim(html) ? html : ' ';
		}
		
		/**
		 * wrapper for "wysiwyg" chain filtering
		 *
		 * @param  String  
		 * @return String
		 **/
		this.wysiwyg = function(html) {
			return this.proccess('wysiwyg', html);
		}
		
		/**
		 * wrapper for "source" chain filtering
		 *
		 * @param  String  
		 * @return String
		 **/
		this.source = function(html) {
			return this.proccess('source', html);
		}
		
		/**
		 * wrapper for "source2source" chain filtering
		 *
		 * @param  String  
		 * @return String
		 **/
		this.source2source = function(html) {
			return this.proccess('source2source', html);
		}
		
		/**
		 * wrapper for "wysiwyg2wysiwyg" chain filtering
		 *
		 * @param  String  
		 * @return String
		 **/
		this.wysiwyg2wysiwyg = function(html) {
			return this.proccess('wysiwyg2wysiwyg', html);
		}
		
		/**
		 * Parse attributes from string into object
		 *
		 * @param  String  string of attributes  
		 * @return Object
		 **/
		this.parseAttrs = function(s) {
			var a = {},
				b = this.boolAttrs,
				m = s.match(this.attrRegExp),
				t, n, v;
				// this.rte.log(s)
			// this.rte.log(m)
			m && $.each(m, function(i, s) {
				t = s.split('=');
				n = $.trim(t[0]).toLowerCase();

				if (t.length>2) {
					t.shift();
					v = t.join('=');
				} else {
					v = b[n] ||t[1]||'';
				}
				a[n] = $.trim(v).replace(/^('|")(.*)(\1)$/, "$2");
			});

			a.style = this.rte.utils.parseStyle(a.style);
			// rte.log(a.style)
			a['class'] = this.rte.utils.parseClass(a['class']||'')
			return a;
		}
		
		/**
		 * Restore attributes string from hash
		 *
		 * @param  Object  attributes hash
		 * @return String
		 **/
		this.serializeAttrs = function(a, c) {
			var s = [], self = this;

			$.each(a, function(n, v) {
				if (n=='style') {
					v = self.rte.utils.serializeStyle(v, c);
				} else if (n=='class') {
					// self.rte.log(v)
					// self.rte.log(self.rte.utils.serializeClass(v))
					v = self.rte.utils.serializeClass(v);
				} 
				v && s.push(n+'="'+v+'"');
			});
			return s.join(' ');
		}
		
		/**
		 * Remove/replace denied attributes/style properties
		 *
		 * @param  Object  attributes hash
		 * @param  String  tag name to wich attrs belongs 
		 * @return Object
		 **/
		this.cleanAttrs = function(a, t) {
			var self = this, ra = this.replaceAttrs;

			// remove safari and mso classes
			$.each(a['class'], function(n) {
				/^(Apple-style-span|mso\w+)$/i.test(n) && delete a['class'][n];
			});

			function value(v) {
				return v+(/\d$/.test(v) ? 'px' : '');
			}

			$.each(a, function(n, v) {
				// replace required attrs with css
				ra[n] && ra[n].call(self, a, t);
				// remove/fix mso styles
				if (n == 'style') {
					$.each(v, function(sn, sv) {
						switch (sn) {
							case "mso-padding-alt":
							case "mso-padding-top-alt":
							case "mso-padding-right-alt":
							case "mso-padding-bottom-alt":
							case "mso-padding-left-alt":
							case "mso-margin-alt":
							case "mso-margin-top-alt":
							case "mso-margin-right-alt":
							case "mso-margin-bottom-alt":
							case "mso-margin-left-alt":
							case "mso-table-layout-alt":
							case "mso-height":
							case "mso-width":
							case "mso-vertical-align-alt":
								a.style[sn.replace(/^mso-|-alt$/g, '')] = value(sv);
								delete a.style[sn];
								break;

							case "horiz-align":
								a.style['text-align'] = sv;
								delete a.style[sn];
								break;

							case "vert-align":
								a.style['vertical-align'] = sv;
								delete a.style[sn];
								break;

							case "font-color":
							case "mso-foreground":
								a.style.color = sv;
								delete a.style[sn];
							break;

							case "mso-background":
							case "mso-highlight":
								a.style.background = sv;
								delete a.style[sn];
								break;

							case "mso-default-height":
								a.style['min-height'] = value(sv);
								delete a.style[sn];
								break;

							case "mso-default-width":
								a.style['min-width'] = value(sv);
								delete a.style[sn];
								break;

							case "mso-padding-between-alt":
								a.style['border-collapse'] = 'separate';
								a.style['border-spacing'] = value(sv);
								delete a.style[sn];
								break;

							case "text-line-through":
								if (sv.match(/(single|double)/i)) {
									a.style['text-decoration'] = 'line-through';
								}
								delete a.style[sn];
								break;

							case "mso-zero-height":
								if (sv == 'yes') {
									a.style.display = 'none';
								}
								delete a.style[sn];
								break;

							case 'font-weight':
								if (sv == 700) {
									a.style['font-weight'] = 'bold';
								}
								break;

							default:
								if (sn.match(/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/)) {
									delete a.style[sn]
								}
						}
					});
				}
			});
			return a;
		}
		
	}

	// rules to replace tags
	elRTE.prototype.filter.prototype.replaceTags = {
		b         : { tag : 'strong' },
		big       : { tag : 'span', style : {'font-size' : 'large'} },
		center    : { tag : 'div',  style : {'text-align' : 'center'} },
		i         : { tag : 'em' },
		font      : { tag : 'span' },
		nobr      : { tag : 'span', style : {'white-space' : 'nowrap'} },
		menu      : { tag : 'ul' },
		plaintext : { tag : 'pre' },
		s         : { tag : 'strike' },
		small     : { tag : 'span', style : {'font-size' : 'small'}},
		u         : { tag : 'span', style : {'text-decoration' : 'underline'} },
		xmp       : { tag : 'pre' }
	}
	
	// rules to replace attributes
	elRTE.prototype.filter.prototype.replaceAttrs = {
		align : function(a, n) {
			switch (n) {
				case 'img':
					a.style[a.align.match(/(left|right)/) ? 'float' : 'vertical-align'] = a.align;
					break;
				
				case 'table':
					if (a.align == 'center') {
						a.style['margin-left'] = a.style['margin-right'] = 'auto';
					} else {
						a.style['float'] = a.align;
					}
					break;
					
				default:
					a.style['text-align'] = a.align;
			}
			delete a.align;
		},
		border : function(a) {
			!a.style['border-width'] && (a.style['border-width'] = (parseInt(a.border)||1)+'px');
			!a.style['border-style'] && (a.style['border-style'] = 'solid');
			delete a.border;
		},
		bordercolor : function(a) {
			!a.style['border-color'] && (a.style['border-color'] = a.bordercolor);
			delete a.bordercolor;
		},
		background : function(a) {
			!a.style['background-image'] && (a.style['background-image'] = 'url('+a.background+')');
			delete a.background;
		},
		bgcolor : function(a) {
			!a.style['background-color'] && (a.style['background-color'] = a.bgcolor);
			delete a.bgcolor;
		},
		clear : function(a) {
			a.style.clear = a.clear == 'all' ? 'both' : a.clear;
			delete a.clear;
		},
		color : function(a) {
			!a.style.color && (a.style.color = a.color);
			delete a.color;
		},
		face : function(a) {
			var f = a.face.toLowerCase();
			$.each(this.fontFamily, function(n, r) {
				if (f.match(r)) {
					a.style['font-family'] = f+','+n;
				}
			});
			delete a.face;
		},
		hspace : function(a, n) {
			if (n == 'img') {
				var v = parseInt(a.hspace)||0;
				!a.style['margin-left'] && (a.style['margin-left'] = v+'px');
				!a.style['margin-right'] && (a.style['margin-right'] = v+'px')
				delete a.hspace;
			}
		},
		size : function(a, n) {
			if (n != 'input') {
				a.style['font-size'] = this.fontSize[parseInt(a.size)||0]||'medium';
				delete a.size;
			}
		},
		valign : function(a) {
			if (!a.style['vertical-align']) {
				a.style['vertical-align'] = a.valign;
			}
			delete a.valign;
		},
		vspace : function(a, n) {
			if (n == 'img') {
				var v = parseInt(a.vspace)||0;
				!a.style['margin-top'] && (a.style['margin-top'] = v+'px');
				!a.style['margin-bottom'] && (a.style['margin-bottom'] = v+'px')
				delete a.hspace;
			}
		}
	}
	
	// rules collection
	elRTE.prototype.filter.prototype.rules = {
		/**
		 * If this.rte.options.allowTags is set - remove all except this ones
		 *
		 * @param String  html code
		 * @return String
		 **/
		allowedTags : function(html) {
			var a = this.allowTags;
			
			return a ? html.replace(this.tagRegExp, function(t, c, n) { return a[n.toLowerCase()] ? t : ''; }) : html;
		},
		/**
		 * If this.rte.options.denyTags is set - remove all deny tags
		 *
		 * @param String  html code
		 * @return String
		 **/
		deniedTags : function(html) {
			var d = this.denyTags; 

			return d ? html.replace(this.tagRegExp, function(t, c, n) { return d[n.toLowerCase()] ? '' : t }) : html;
		},
		
		/**
		 * Replace not allowed tags/attributes
		 *
		 * @param String  html code
		 * @return String
		 **/
		clean : function(html) {
			var self = this, 
				rt   = this.replaceTags,
				ra   = this.replaceAttrs, 
				da   = this.denyAttr,
				n;
			
			
			html = html.replace(/<!DOCTYPE([\s\S]*)>/gi, '')
				.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>")
				.replace(/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s&nbsp;]*)<\/span>/gi, "$1")
				.replace(/(<p[^>]*>\s*<\/p>|<p[^>]*\/>)/gi, '<br>')
				.replace(/(<\/p>)(?:\s*<br\s*\/?>\s*|\s*&nbsp;\s*)+\s*(<p[^>]*>)/gi, function(t, b, e) {
					return b+"\n"+e;
				})
				.replace(this.tagRegExp, function(t, c, n, a) {
					n = n.toLowerCase();
					
					if (c) {
						return '</'+(rt[n] ? rt[n].tag : n)+'>';
					}
					// self.rte.log(t)
					// create attributes hash and clean it
					a = self.cleanAttrs(self.parseAttrs(a||''), n);
					// self.rte.log(a)
					if (rt[n]) {
						rt[n].style && $.extend(a.style, rt[n].style);
						n = rt[n].tag;
					}
					
					da && $.each(a, function(na) {
						if (da[na]) {
							delete a[na];
						}
					});
					a = self.serializeAttrs(a);
					// self.rte.log(a)
					return '<'+n+(a?' ':'')+a+'>';
				});
				
			
			n = $('<div>'+html+'</div>');
			
			// remove empty spans and merge nested spans
			n.find('span:not([id]):not([class])').each(function() {
				var t = $(this);
				
				if (!t.attr('style')) {
					
					$.trim(t.html()).length ? self.rte.dom.unwrap(this) : t.remove();
					// t.children().length ? self.rte.dom.unwrap(this) : t.remove();
				}
			}).end().find('span span:only-child').each(function() {
				var t   = $(this), 
					p   = t.parent().eq(0), 
					tid = t.attr('id'), 
					pid = p.attr('id'), id, s, c;

				if (self.rte.dom.isOnlyNotEmpty(this) && (!tid || !pid)) {
					c = $.trim(p.attr('class')+' '+t.attr('class'))
					c && p.attr('class', c);
					s = self.rte.utils.serializeStyle($.extend(self.rte.utils.parseStyle($(this).attr('style')||''), self.rte.utils.parseStyle($(p).attr('style')||'')));
					s && p.attr('style', s);
					id = tid||pid;
					id && p.attr('id', id);
					this.firstChild ? $(this.firstChild).unwrap() : t.remove();
				}
			})
			.end().find('a[name]').each(function() {
				$(this).addClass('elrte-protected elrte-anchor');
			});
			
			return n.html()	
		},
		
		/**
		 * Clean pasted html
		 *
		 * @param String  html code
		 * @return String
		 **/
		cleanPaste : function(html) {
			var self = this, d = this.pasteDenyAttr;

			html = html
				.replace(this.scriptRegExp, '')
				.replace(this.styleRegExp, '')
				.replace(this.linkRegExp, '')
				.replace(this.cdataRegExp, '')
				.replace(/\<\!--[\s\S]*?--\>/g, '');
			
			if (this.rte.options.pasteOnlyText) {
				html = html.replace(this.tagRegExp, function(t, c, n) {
					return /br/i.test(n) || (c && /h[1-6]|p|ol|ul|li|div|blockquote|tr/i) ? '<br>' : '';
				}).replace(/(&nbsp;|<br[^>]*>)+\s*$/gi, '');
			} else if (d) {
				html = html.replace(this.openTagRegExp, function(t, n, a) {
					a = self.parseAttrs(a);
					$.each(a, function(an) {
						if (d[an]) {
							delete a[an];
						}
					});
					a = self.serializeAttrs(a, true);
					return '<'+n+(a?' ':'')+a+'>';
				});
			}
			return html; 
		},
		
		/**
		 * Replace script/style/media etc with placeholders
		 *
		 * @param String  html code
		 * @return String
		 **/
		replace : function(html) {
			var self = this, r = this.rte.options.replace||[], n;

			// custom replaces if set
			if (r.length) {
				$.each(r, function(i, f) {
					if (typeof(f) == 'function') {
						html = f.call(self, html);
					}
				});
			}

			/**
			 * Return media replacement - img html code
			 *
			 * @param Object  object to store in rel attr
			 * @param String  media mime-type
			 * @return String
			 **/
			function img(o, t) {
				var s = src(),
					c = s && self.videoHostRegExp.test(s) ? s.replace(self.videoHostRegExp, "$2") : t.replace(/^\w+\/(.+)/, "$1"),
					w = parseInt((o.obj ? o.obj.width || o.obj.style.width : 0)||(o.embed ? o.embed.width || o.embed.style.width : 0))||150,
					h = parseInt((o.obj ? o.obj.height || o.obj.style.height : 0)||(o.embed ? o.embed.height || o.embed.style.height : 0))||100,
					id = 'media'+Math.random().toString().substring(2),
					style ='',
					l;

				// find media src
				function src() {
					if (o.embed && o.embed.src) {
						return o.embed.src;
					}
					if (o.params && o.params.length) {
						l = o.params.length;
						while (l--) {
							if (o.params[l].name == 'src' || o.params[l].name == 'movie') {
								return o.params[l].value;
							}
						}
					}
				}
				if (o.obj && o.obj.style && o.obj.style['float']) {
					style = ' style="float:'+o.obj.style['float']+'"';
				}
				self.scripts[id] = o;
				return '<img src="'+self.url+'pixel.gif" class="elrte-media elrte-media-'+c+' elrte-protected" title="'+(s ? self.rte.utils.encode(s) : '')+'" rel="'+id+'" width="'+w+'" height="'+h+'"'+style+'>';
			}
			
			html = html
				.replace(this.styleRegExp, "<!-- ELRTE_COMMENT$1 -->")
				.replace(this.linkRegExp,  "<!-- ELRTE_COMMENT$1-->")
				.replace(this.cdataRegExp, "<!--[CDATA[$1]]-->")
				.replace(this.scriptRegExp, function(t, a, s) {
					var id;
					if (self.denyTags.script) {
						return '';
					}
					id = 'script'+Math.random().toString().substring(2);
					a = self.parseAttrs(a);
					!a.type && (a.type = 'text/javascript');
					self.scripts[id] = '<script '+self.serializeAttrs(a)+">"+s+"</script>";
					return '<!-- ELRTE_SCRIPT:'+(id)+' -->';
				})
				.replace(this.yMapsRegExp, function(t, a) {
					a = self.parseAttrs(a);
					a['class']['elrte-yandex-maps'] = 'elrte-yandex-maps';
					a['class']['elrte-protected'] = 'elrte-protected';
					return '<div '+self.serializeAttrs(a)+'>';
				})
				.replace(this.gMapsRegExp, function(t, a) {
					var id = 'gmaps'+Math.random().toString().substring(2), w, h;
					a = self.parseAttrs(a);
					w = parseInt(a.width||a.style.width||100);
					h = parseInt(a.height||a.style.height||100);
					self.scripts[id] = t;
					return '<img src="'+self.url+'pixel.gif" class="elrte-google-maps elrte-protected" id="'+id+'" style="width:'+w+'px;height:'+h+'px">';
				})
				.replace(this.objRegExp, function(t, a, c) {
					var m = c.match(self.embRegExp),
						o = { obj : self.parseAttrs(a), embed : m && m.length ? self.parseAttrs(m[0].substring(7)) : null, params : [] },
						i = self.rte.utils.mediaInfo(o.embed ? o.embed.type||'' : '', o.obj.classid||'');
					
					if (i) {
						if ((m = c.match(self.paramRegExp))) {
							$.each(m, function(i, p) {
								o.params.push(self.parseAttrs(p.substring(6)));
							});
						}
						!o.obj.classid  && (o.obj.classid  = i.classid[0]);
						!o.obj.codebase && (o.obj.codebase = i.codebase);
						o.embed && !o.embed.type && (o.embed.type = i.type);
						// ie bug with empty attrs
						o.obj.width == '1' && delete o.obj.width;
						o.obj.height == '1' && delete o.obj.height;
						if (o.embed) {
							o.embed.width == '1' && delete o.embed.width;
							o.embed.height == '1' && delete o.embed.height;
						}
						return img(o, i.type);
					}
					return t;
				})
				.replace(this.embRegExp, function(t, n, a) {
					var a = self.parseAttrs(a),
						i = self.rte.utils.mediaInfo(a.type||'');
					// ie bug with empty attrs
					a.width == '1' && delete a.width;
					a.height == '1' && delete a.height;
					return i ? img({ embed : a }, i.type) : t;
				})
				.replace(this.iframeRegExp, function(t, a) {
					var a = self.parseAttrs(a);
					var w = a.style.width || (parseInt(a.width) > 1 ? parseInt(a.width)+'px' : '100px');
					var h = a.style.height || (parseInt(a.height) > 1 ? parseInt(a.height)+'px' : '100px');
					var id = 'iframe'+Math.random().toString().substring(2);
					self.scripts[id] = t;
					var img = '<img id="'+id+'" src="'+self.url+'pixel.gif" class="elrte-protected elrte-iframe" style="width:'+w+'; height:'+h+'">';
					return img;
				})
				.replace(this.vimeoRegExp, function(t, n, a) {
					a = self.parseAttrs(a);
					delete a.frameborder;
					a.width == '1' && delete a.width;
					a.height == '1' && delete a.height;
					a.type = 'application/x-shockwave-flash';
					return img({ embed : a }, 'application/x-shockwave-flash');
				})
				.replace(/<\/(embed|param)>/gi, '')
				.replace(this.pbRegExp, function() {
					return '<img src="'+self.url+'pixel.gif" class="elrte-protected elrte-pagebreak">';
				});


			n = $('<div>'+html+'</div>');
			
			// remove empty spans and merge nested spans
			// n.find('span:not([id]):not([class])').each(function() {
			// 	var t = $(this);
			// 	
			// 	if (!t.attr('style')) {
			// 		$.trim(t.html()).length ? self.rte.dom.unwrap(this) : t.remove();
			// 		// t.children().length ? self.rte.dom.unwrap(this) : t.remove();
			// 	}
			// }).end().find('span span:only-child').each(function() {
			// 	var t   = $(this), 
			// 		p   = t.parent().eq(0), 
			// 		tid = t.attr('id'), 
			// 		pid = p.attr('id'), id, s, c;
			// 
			// 	if (self.rte.dom.is(this, 'onlyChild') && (!tid || !pid)) {
			// 		c = $.trim(p.attr('class')+' '+t.attr('class'))
			// 		c && p.attr('class', c);
			// 		s = self.rte.utils.serializeStyle($.extend(self.rte.utils.parseStyle($(this).attr('style')||''), self.rte.utils.parseStyle($(p).attr('style')||'')));
			// 		s && p.attr('style', s);
			// 		id = tid||pid;
			// 		id && p.attr('id', id);
			// 		this.firstChild ? $(this.firstChild).unwrap() : t.remove();
			// 	}
			// })
			// .end().find('a[name]').each(function() {
			// 	$(this).addClass('elrte-anchor');
			// });


			if (!this.rte.options.allowTextNodes) {
				// wrap inline nodes with p
				var dom = this.rte.dom,
					nodes = [],
					w = [];
				
				if ($.browser.msie) {
					for (var i = 0; i<n[0].childNodes.length; i++) {
						nodes.push(n[0].childNodes[i])
					}
				} else {
					nodes = Array.prototype.slice.call(n[0].childNodes);
				}
				

				function wrap() {
					if (w.length && dom.filter(w, 'notEmpty').length) {
						dom.wrap(w, document.createElement('p'));
					}
					w = [];
				}	
				$.each(nodes, function(i, n) {
					if (dom.is(n, 'block')) {
						wrap();
					} else {
						if (w.length && n.previousSibling != w[w.length-1]) {
							wrap();
						}
						w.push(n);
					}
				});
				wrap();
			}
			
			return n.html();
		},
		/**
		 * Restore script/style/media etc from placeholders
		 *
		 * @param String  html code
		 * @return String
		 **/
		restore : function(html) {
			var self =this, r = this.rte.options.restore||[];

			// custom restore if set
			if (r.length) {
				$.each(r, function(i, f) {
					if (typeof(f) == 'function') {
						html = f.call(self, html);
					}
				});
			}
			
			html = html
				.replace(/\<\!--\[CDATA\[([\s\S]*?)\]\]--\>/gi, "<![CDATA[$1]]>")
				.replace(/\<\!--\s*ELRTE_SCRIPT\:\s*(script\d+)\s*--\>/gi, function(t, n) {
					if (self.scripts[n]) {
						t = self.scripts[n];
						delete self.scripts[n];
					}
					return t||'';
				})
				.replace(/\<\!-- ELRTE_COMMENT([\s\S]*?) --\>/gi, "$1")
				.replace(this.serviceClassRegExp, function(t, n, a, e) {

					var a = self.parseAttrs(a), j, o = '';
					// alert(t)
					if (a['class']['elrte-google-maps']) {
						var t = '';
						if (self.scripts[a.id]) {
							t = self.scripts[a.id];
							delete self.scripts[a.id]
						}
						return t;
					} else if (a['class']['elrte-iframe']) {
						return self.scripts[a.id] || '';
					} else if (a['class']['elrtebm']) {
						return '';
					} else if (a['class']['elrte-media']) {
						// alert(a.rel)
						// return ''
						// j = a.rel ? JSON.parse(self.rte.utils.decode(a.rel)) : {};
						j = self.scripts[a.rel]||{};
						j.params && $.each(j.params, function(i, p) {
							o += '<param '+self.serializeAttrs(p)+">\n";
						});
						j.embed && (o+='<embed '+self.serializeAttrs(j.embed)+">");
						j.obj && (o = '<object '+self.serializeAttrs(j.obj)+">\n"+o+"\n</object>\n");
						return o||t;
					} else if (a['class']['elrte-pagebreak']) {
						return '--summary--';
					}
					$.each(a['class'], function(n) {
						if (/^elrte-\w+/i.test(n)) {
							delete(a['class'][n]);
						}
						// /^elrte\w+/i.test(n) && delete(a['class'][n]); 
					});
					return '<'+n+' '+self.serializeAttrs(a)+'>'+(e||'');

				});
			
			return html;
		},
		/**
		 * compact styles and move tags and attributes names in lower case(for ie&opera)
		 *
		 * @param String  html code
		 * return String
		 **/
		compactStyles : function(html) {
			var self = this;

			return html.replace(this.tagRegExp, function(t, c, n, a) {
				a = !c && a ? self.serializeAttrs(self.parseAttrs(a), true) : '';
				return '<'+c+n.toLowerCase()+(a?' ':'')+a+'>';
			});
		},
		/**
		 * return xhtml tags
		 *
		 * @param String  html code
		 * return String
		 **/
		xhtmlTags : function(html) {
			return this.xhtml ? html.replace(/<(img|hr|br|embed|param|link|area)([^>]*\/*)>/gi, "<$1$2 />") : html;
		}
	}
	
	/**
	 * Chains configuration
	 * Default chains 
	 * wysiwyg - proccess html from source for wysiwyg editor mode
	 * source  - proccess html from wysiwyg for source editor mode
	 * paste   - clean pasted html
	 * wysiwyg2wysiwyg - ciclyc rule to clean html from wysiwyg for wysiwyg paste
	 * source2source - ciclyc rule to clean html from source for source paste
	 * deniedTags is in the end of chain to protect google maps iframe from removed
	 **/
	elRTE.prototype.filter.prototype.chains = {
		wysiwyg         : ['replace', 'clean', 'allowedTags', 'deniedTags', 'compactStyles'],
		source          : ['clean', 'allowedTags', 'restore', 'compactStyles', 'xhtmlTags'],
		paste           : ['clean', 'allowedTags', 'cleanPaste', 'replace', 'deniedTags', 'compactStyles'],
		wysiwyg2wysiwyg : ['clean', 'allowedTags', 'restore', 'replace', 'deniedTags', 'compactStyles'],
		source2source   : ['clean', 'allowedTags', 'replace', 'deniedTags', 'restore', 'compactStyles', 'xhtmlTags']
	}
	

	
})(jQuery);
(function($) {
elRTE.prototype.history = function(rte) {
	this.rte = rte;
	this._prev = []
	this._next = [];

	
	this.add = function() {
		if (this.rte.options.historyLength>0 && this._prev.length>= this.rte.options.historyLength) {
			this._prev.slice(this.rte.options.historyLength);
		}
		var b = this.rte.selection.getBookmark();
		this._prev.push([$(this.rte.doc.body).html(), b]);
		this.rte.selection.moveToBookmark(b);
		// this._prev.push($(this.rte.doc.body).html());
		this._next = [];
	}
	
	this.back = function() {
		
		if (this._prev.length) {
			var b = this.rte.selection.getBookmark(), 
				data = this._prev.pop();
			this._next.push([$(this.rte.doc.body).html(), b]);
			
			$(this.rte.doc.body).html(data[0]);
			this.rte.selection.moveToBookmark(data[1]);
		}
	}

	this.fwd = function() {
		if (this._next.length) {
			var b = this.rte.selection.getBookmark(), 
				data = this._next.pop();
			this._prev.push([$(this.rte.doc.body).html(), b]);
			
			$(this.rte.doc.body).html(data[0]);
			this.rte.selection.moveToBookmark(data[1]);
		}
	}
	
	this.canBack = function() {
		return this._prev.length;
	}
	
	this.canFwd = function() {
		return this._next.length;
	}

}
})(jQuery);/*
 * elRTE configuration
 *
 * @param doctype         - doctype for editor iframe
 * @param cssClass        - css class for editor
 * @param cssFiles        - array of css files, witch will inlude in iframe
 * @param height          - not used now (may be deleted in future)
 * @param lang            - interface language (requires file in i18n dir)
 * @param toolbar         - name of toolbar to load
 * @param absoluteURLs    - convert files and images urls to absolute or not
 * @param allowSource     - is source editing allowing
 * @param stripWhiteSpace - strip лишние whitespaces/tabs or not
 * @param styleWithCSS    - use style=... instead of strong etc.
 * @param fmAllow         - allow using file manger (elFinder)
 * @param fmOpen          - callback for open file manager
 * @param buttons         - object with pairs of buttons classes names and titles (when create new button, you have to add iys name here)
 * @param panels          - named groups of buttons
 * @param panelNames      - title of panels (required for one planned feature)
 * @param toolbars        - named redy to use toolbals (you may combine your own toolbar)
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 */
(function($) {
elRTE.prototype.options   = {
	doctype         : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
	cssClass        : 'el-rte',
	cssfiles        : [],
	height          : null,
	resizable       : true,
	lang            : 'en',
	toolbar         : 'normal',
	absoluteURLs    : true,
	allowSource     : true,
	stripWhiteSpace : true,
	styleWithCSS    : false,
	fmAllow         : true,
	fmOpen          : null,
	/* if set all other tag will be removed */
	allowTags : [],
	/* if set this tags will be removed */
	denyTags : ['applet', 'base', 'basefont', 'bgsound', 'blink', 'body', 'col', 'colgroup', 'isindex', 'frameset', 'html', 'head', 'meta', 'marquee', 'noframes', 'noembed', 'o:p', 'title', 'xml'],
	denyAttr : [],
	/* on paste event this attributes will removed from pasted html */
	pasteDenyAttr : ['id', 'name', 'class', 'style', 'language', 'onclick', 'ondblclick', 'onhover', 'onkeup', 'onkeydown', 'onkeypress'],
	/* If false - all text nodes will be wrapped by paragraph tag */
	allowTextNodes : true,
	/* allow browser specific styles like -moz|-webkit|-o */
	allowBrowsersSpecStyles : false,
	/* allow paste content into editor */
	allowPaste : true,
	/* if true - only text will be pasted (not in ie) */
	pasteOnlyText : false,
	/* user replacement rules */
	replace : [],
	/* user restore rules */
	restore : [],
	pagebreak : '<div style="page-break-after: always;"></div>', //'<!-- pagebreak -->',
	buttons         : {
		'save'                : 'Save',
		'copy'                : 'Copy',
		'cut'                 : 'Cut',
		'css'                 : 'Css style and class',
		'paste'               : 'Paste',
		'pastetext'           : 'Paste only text',
		'pasteformattext'     : 'Paste formatted text',
		'removeformat'        : 'Clean format', 
		'undo'                : 'Undo last action',
		'redo'                : 'Redo previous action',
		'bold'                : 'Bold',
		'italic'              : 'Italic',
		'underline'           : 'Underline',
		'strikethrough'       : 'Strikethrough',
		'superscript'         : 'Superscript',
		'subscript'           : 'Subscript',
		'justifyleft'         : 'Align left',
		'justifyright'        : 'Ailgn right',
		'justifycenter'       : 'Align center',
		'justifyfull'         : 'Align full',
		'indent'              : 'Indent',
		'outdent'             : 'Outdent',
		'rtl' : 'Right to left',
		'ltr' : 'Left to right',
		'forecolor'           : 'Font color',
		'hilitecolor'         : 'Background color',
		'formatblock'         : 'Format',
		'fontsize'            : 'Font size',
		'fontname'            : 'Font',
		'insertorderedlist'   : 'Ordered list',
		'insertunorderedlist' : 'Unordered list',
		'horizontalrule'      : 'Horizontal rule',
		'blockquote'          : 'Blockquote',
		'div'                 : 'Block element (DIV)',
		'link'                : 'Link',
		'unlink'              : 'Delete link',
		'anchor'              : 'Bookmark',
		'image'               : 'Image',
		'pagebreak'           : 'Page break',
		'smiley'              : 'Smiley',
		'flash'               : 'Flash',
		'table'               : 'Table',
		'tablerm'             : 'Delete table',
		'tableprops'          : 'Table properties',
		'tbcellprops'         : 'Table cell properties',
		'tbrowbefore'         : 'Insert row before',
		'tbrowafter'          : 'Insert row after',
		'tbrowrm'             : 'Delete row',
		'tbcolbefore'         : 'Insert column before',
		'tbcolafter'          : 'Insert column after',
		'tbcolrm'             : 'Delete column',
		'tbcellsmerge'        : 'Merge table cells',
		'tbcellsplit'         : 'Split table cell',
		'docstructure'        : 'Toggle display document structure',
		'elfinder'            : 'Open file manager',
		'fullscreen'          : 'Toggle full screen mode',
		'nbsp'                : 'Non breakable space',
		'stopfloat'           : 'Stop element floating',
		'about'               : 'About this software'
	},
	panels      : {
		eol        : [], // special panel, insert's a new line in toolbar
		save       : ['save'],
		copypaste  : ['copy', 'cut', 'paste', 'pastetext', 'pasteformattext', 'removeformat', 'docstructure'],
		undoredo   : ['undo', 'redo'],
		style      : ['bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript'],
		colors     : ['forecolor', 'hilitecolor'],
		alignment  : ['justifyleft', 'justifycenter', 'justifyright', 'justifyfull'],
		indent     : ['outdent', 'indent'],
		format     : ['formatblock', 'fontsize', 'fontname'],
		lists      : ['insertorderedlist', 'insertunorderedlist'],
		elements   : ['horizontalrule', 'blockquote', 'div', 'stopfloat', 'css', 'nbsp', 'smiley', 'pagebreak'],
		direction  : ['ltr', 'rtl'],
		links      : ['link', 'unlink', 'anchor'],
		images     : ['image'],
		media      : ['image', 'flash'],		
		tables     : ['table', 'tableprops', 'tablerm',  'tbrowbefore', 'tbrowafter', 'tbrowrm', 'tbcolbefore', 'tbcolafter', 'tbcolrm', 'tbcellprops', 'tbcellsmerge', 'tbcellsplit'],
		elfinder   : ['elfinder'],
		fullscreen : ['fullscreen', 'about' , 'Code']
	},
	toolbars    : {
		tiny     : ['style'],
		compact  : ['save', 'undoredo', 'style', 'alignment', 'lists', 'links', 'fullscreen'],
		normal   : ['save', 'copypaste', 'undoredo', 'style', 'alignment', 'colors', 'indent', 'lists', 'links', 'elements', 'images', 'fullscreen'],
		complete : ['save', 'copypaste', 'undoredo', 'style', 'alignment', 'colors', 'format', 'indent', 'lists', 'links', 'elements', 'media', 'fullscreen'],
		maxi     : ['save', 'copypaste', 'undoredo', 'elfinder', 'style', 'alignment', 'direction', 'colors', 'format', 'indent', 'lists', 'links', 'elements', 'media', 'tables', 'fullscreen'],
		eldorado : ['save', 'copypaste', 'elfinder', 'undoredo', 'style', 'alignment', 'colors', 'format', 'indent', 'lists', 'links', 'elements', 'media', 'tables', 'fullscreen']
		
	},
	panelNames : {
		save      : 'Save',
		copypaste : 'Copy/Pase',
		undoredo  : 'Undo/Redo',
		style     : 'Text styles',
		colors    : 'Colors',
		alignment : 'Alignment',
		indent    : 'Indent/Outdent',
		format    : 'Text format',
		lists     : 'Lists',
		elements  : 'Misc elements',
		direction : 'Script direction',
		links     : 'Links',
		images    : 'Images',
		media     : 'Media',
		tables    : 'Tables',
		elfinder  : 'File manager (elFinder)'
	}
};
})(jQuery);
/**
 * @class selection  - elRTE utils for working with text selection
 *
 * @param  elRTE  rte  объект-редактор
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
elRTE.prototype.selection = function(rte) {
	this.rte      = rte;
	var self      = this;
	this.w3cRange = null;
	var start, end, node, bm;
	
	$(this.rte.doc)
		.keyup(function(e) {
			if (e.ctrlKey || e.metaKey || (e.keyCode >= 8 && e.keyCode <= 13) || (e.keyCode>=32 && e.keyCode<= 40) || e.keyCode == 46 || (e.keyCode >=96 && e.keyCode <= 111)) {
				self.cleanCache();
			}
		})
		.mousedown(function(e) {
			// self.rte.log(e)
			if (e.target.nodeName == 'HTML') {
				start = self.rte.doc.body;
			} else {
				start = e.target;
			}
			end   = node = null;
		})
		.mouseup(function(e) {
			if (e.target.nodeName == 'HTML') {
				end = self.rte.doc.body;
			} else {
				end = e.target;
			}
			end  = e.target;
			node = null;
		}).click();
		
	/**
	 * возвращает selection
	 *
	 * @return  Selection
	 **/
	function selection() {
		return self.rte.window.getSelection ? self.rte.window.getSelection() : self.rte.window.document.selection;
	}
	
	/**
	 * Вспомогательная функция
	 * Возвращает самого верхнего родителя, отвечающего условию - текущая нода - его единственная непустая дочерняя нода
	 *
	 * @param   DOMElement  n нода, для которой ищем родителя
	 * @param   DOMElement  p если задана - нода, выше которой не поднимаемся
	 * @param   String      s строна поиска (left||right||null)
	 * @return  DOMElement
	 **/
	function realSelected(n, p, s) {
		while (n.nodeName != 'BODY' && n.parentNode && n.parentNode.nodeName != 'BODY' && (p ? n!== p && n.parentNode != p : 1) && ((s=='left' && self.rte.dom.isFirstNotEmpty(n)) || (s=='right' && self.rte.dom.isLastNotEmpty(n)) || (self.rte.dom.isFirstNotEmpty(n) && self.rte.dom.isLastNotEmpty(n))) ) {
			n = n.parentNode;
		}
		return n;
	}
	
	/**
	 * Возвращает TRUE, если выделение "схлопнуто"
	 *
	 * @return  bool
	 **/
	this.collapsed = function() {
		return this.getRangeAt().isCollapsed();
	}
	
	/**
	 * "Схлопывает" выделение 
	 *
	 * @param   bool  toStart  схлопнуть к начальной точке
	 * @return  void
	 **/
	this.collapse = function(st) {
		var s = selection(),
			r = this.getRangeAt();
		r.collapse(st?true:false);
		if (!$.browser.msie) {
			s.removeAllRanges();
			s.addRange(r);
			
		}
		return this;
	}
	
	/**
	 * Возвращает TextRange
	 * Для нормальных браузеров - нативный range
	 * для "самизнаетечего" - эмуляцию w3c range
	 *
	 * @return  range|w3cRange
	 **/
	this.getRangeAt = function(updateW3cRange) {
		if (this.rte.browser.msie) {
			if (!this.w3cRange) {
				this.w3cRange = new this.rte.w3cRange(this.rte);
			}
			updateW3cRange && this.w3cRange.update();
			return this.w3cRange;
		}
		
		var s = selection();
		var r = s.rangeCount > 0 ? s.getRangeAt(0) : this.rte.doc.createRange();
		r.getStart = function() {
			return this.startContainer.nodeType==1 
				? this.startContainer.childNodes[Math.min(this.startOffset, this.startContainer.childNodes.length-1)] 
				: this.startContainer;
		}
		
		r.getEnd = function() {
			return this.endContainer.nodeType==1 
				? this.endContainer.childNodes[ Math.min(this.startOffset == this.endOffset ? this.endOffset : this.endOffset-1, this.endContainer.childNodes.length-1)] 
				: this.endContainer;
		}
		r.isCollapsed = function() {
			return this.collapsed;
		}
		return r;
	}
	
	this.saveIERange = function() {
		if ($.browser.msie) {
			bm = this.getRangeAt().getBookmark();
		}
	}
	
	this.restoreIERange = function() {
		$.browser.msie && bm && this.getRangeAt().moveToBookmark(bm);
	}
	
	this.cloneContents = function() {
		var n = this.rte.dom.create('div'), r, c, i;
		if ($.browser.msie) {
			try { 
				r = this.rte.window.document.selection.createRange(); 
			} catch(e) { 
				r = this.rte.doc.body.createTextRange(); 
			}
			$(n).html(r.htmlText);
		} else {
			c = this.getRangeAt().cloneContents();
			for (i=0; i<c.childNodes.length; i++) {
				n.appendChild(c.childNodes[i].cloneNode(true));
			}
		}
		return n;
	}
	
	/**
	 * Выделяет ноды
	 *
	 * @param   DOMNode  s  нода начала выделения
	 * @param   DOMNode  e  нода конца выделения
	 * @return  selection
	 **/
	this.select = function(s, e) {
		e = e||s;
		
		if (this.rte.browser.msie) {
			var r  = this.rte.doc.body.createTextRange(),
				r1 = r.duplicate(),
				r2 = r.duplicate();
			
			r1.moveToElementText(s);
			r2.moveToElementText(e);
			r.setEndPoint('StartToStart', r1);
			r.setEndPoint('EndToEnd',     r2);
			r.select();
		} else {
			
			var sel = selection(),
				r = this.getRangeAt();
			r.setStartBefore(s);
			r.setEndAfter(e);
			sel.removeAllRanges();
			sel.addRange(r);
		}
		return this.cleanCache();
	}
	
	/**
	 * Выделяет содержимое ноды
	 *
	 * @param   Element  n  нода
	 * @return  selection
	 **/
	this.selectContents = function(n) {
		var r = this.getRangeAt();
		if (n && n.nodeType == 1) {
			if (this.rte.browser.msie) {
				r.range();
				r.r.moveToElementText(n.parentNode);
				r.r.select();
			} else {
				try {
					r.selectNodeContents(n);
				} catch (e) {
					return this.rte.log('unable select node contents '+n);
				}
				var s = selection();
				s.removeAllRanges();
				s.addRange(r);
			}
		}
		return this;
	}
	
	this.deleteContents = function() {
		if (!$.browser.msie) {
			this.getRangeAt().deleteContents();
		}
		return this;
	}
	
	/**
	 * Вставляет ноду в текущее выделение
	 *
	 * @param   Element  n  нода
	 * @return  selection
	 **/
	this.insertNode = function(n, collapse) {
		if (collapse && !this.collapsed()) {
			this.collapse();
		}

		if (this.rte.browser.msie) {
			var html = n.nodeType == 3 ? n.nodeValue : $(this.rte.dom.create('span')).append($(n)).html();
			 var r = this.getRangeAt();
			r.insertNode(html);
		} else {
			var r = this.getRangeAt();
			r.insertNode(n);
			r.setStartAfter(n);
			r.setEndAfter(n);
			var s = selection();
			s.removeAllRanges();
			s.addRange(r);
		}
		return this.cleanCache();
	}

	/**
	 * Вставляет html в текущее выделение
	 *
	 * @param   Element  n  нода
	 * @return  selection
	 **/
	this.insertHtml = function(html, collapse) {
		if (collapse && !this.collapsed()) {
			this.collapse();
		}
		
		if (this.rte.browser.msie) {
			this.getRangeAt().range().pasteHTML(html);
		} else {
			var n = $(this.rte.dom.create('span')).html(html||'').get(0);
			this.insertNode(n);
			$(n).replaceWith($(n).html());
		}
		return this.cleanCache();
	}

	/**
	 * Вставляет ноду в текущее выделение
	 *
	 * @param   Element  n  нода
	 * @return  selection
	 **/
	this.insertText = function(text, collapse) {
		var n = this.rte.doc.createTextNode(text);
		return this.insertHtml(n.nodeValue);
	}

	this.getBookmark = function() {
		this.rte.window.focus();
		var r, r1, r2, _s, _e,
			s = this.rte.dom.createBookmark(),
			e = this.rte.dom.createBookmark();
			
		
		
		if ($.browser.msie) {
			try { 
				r = this.rte.window.document.selection.createRange(); 
			} catch(e) { 
				r = this.rte.doc.body.createTextRange(); 
			}
			
			if (r.item) {
				var n = r.item(0);
				r = this.rte.doc.body.createTextRange();
				r.moveToElementText(n);
			}
			
			r1 = r.duplicate();
			r2 = r.duplicate();
			_s = this.rte.dom.create('span');
			_e = this.rte.dom.create('span');

			_s.appendChild(s);
			_e.appendChild(e);

			r1.collapse(true);
			r1.pasteHTML(_s.innerHTML);
			r2.collapse(false);
			r2.pasteHTML(_e.innerHTML);
		} else {
				var sel = selection();
				var r = sel.rangeCount > 0 ? sel.getRangeAt(0) : this.rte.doc.createRange();
				
			// r  = this.getRangeAt();
			r1 = r.cloneRange();
			r2 = r.cloneRange();
			
			// this.insertNode(this.rte.dom.create('hr'))
			// return
			r2.collapse(false);
			r2.insertNode(e);
			r1.collapse(true);
			r1.insertNode(s);
			this.select(s, e);
		}
		
		return [s.id, e.id];
	}

	this.moveToBookmark = function(b) {
		this.rte.window.focus();
		
		if (b && b.length==2) {
			var s = this.rte.doc.getElementById(b[0]),
				e = this.rte.doc.getElementById(b[1]),
				sel, r;
			if (s && e) {
				this.select(s, e);
				if (this.rte.dom.next(s) == e) {
					this.collapse(true);
				}
				if (!$.browser.msie) {
					sel = selection();
					r = sel.rangeCount > 0 ? sel.getRangeAt(0) : this.rte.doc.createRange();
					sel.removeAllRanges();
					sel.addRange(r);
				}
				
				s.parentNode.removeChild(s);
				e.parentNode.removeChild(e);
			}
		}
		return this;	
	}

	this.removeBookmark = function(b) {
		this.rte.window.focus();
		if (b.length==2) {
			var s = this.rte.doc.getElementById(b[0]),
				e = this.rte.doc.getElementById(b[1]);
			if (s && e) {
				s.parentNode.removeChild(s);
				e.parentNode.removeChild(e);
			}
		}
	}

	/**
	 * Очищает кэш
	 *
	 * @return  selection
	 **/
	this.cleanCache = function() {
		start = end = node = null;
		return this;
	}

	
	/**
	 * Возвращает ноду начала выделения
	 *
	 * @return  DOMElement
	 **/
	this.getStart = function() {
		if (!start) {
			var r = this.getRangeAt();
			start = r.getStart();
		}
		return start;
	}
	
	/**
	 * Возвращает ноду конца выделения
	 *
	 * @return  DOMElement
	 **/
	this.getEnd = function() {
		if (!end) {
			var r = this.getRangeAt();
			end = r.getEnd();
		}
		return end;
	}

	/**
	 * Возвращает выбраную ноду (общий контейнер всех выбранных нод)
	 *
	 * @return  Element
	 **/
	this.getNode = function() {
		if (!node) {
			node = this.rte.dom.findCommonAncestor(this.getStart(), this.getEnd());
		}
		return node;
	}

	
	/**
	 * Возвращает массив выбранных нод
	 *
	 * @param   Object  o  параметры получения и обработки выбраных нод
	 * @return  Array
	 **/
	this.selected = function(o) {
		var opts = {
			collapsed : false,  // вернуть выделение, даже если оно схлопнуто
			blocks    : false,  // блочное выделение
			filter    : false,  // фильтр результатов
			wrap      : 'text', // что оборачиваем
			tag       : 'span'  // во что оборачиваем
		}
		opts = $.extend({}, opts, o);
		
		// блочное выделение - ищем блочную ноду, но не таблицу
		if (opts.blocks) {
			var n  = this.getNode(), _n = null;
			if (_n = this.rte.dom.selfOrParent(n, 'selectionBlock') ) {
				return [_n];
			} 
		}

		var sel    = this.selectedRaw(opts.collapsed, opts.blocks);
		var ret    = [];
		var buffer = [];
		var ndx    = null;

		// оборачиваем ноды в буффере
		function wrap() {
			
			function allowParagraph() {
				for (var i=0; i < buffer.length; i++) {
					if (buffer[i].nodeType == 1 && (self.rte.dom.selfOrParent(buffer[i], /^P$/) || $(buffer[i]).find('p').length>0)) {
						return false;
					}
				};
				return true;
			} 
			
			if (buffer.length>0) {
				var tag  = opts.tag == 'p' && !allowParagraph() ? 'div' : opts.tag;
				var n    = self.rte.dom.wrap(buffer, tag);
				ret[ndx] = n;
				ndx      = null;
				buffer   = [];
			}
		}
		
		// добавляем ноды в буффер
		function addToBuffer(n) {
			if (n.nodeType == 1) {
				if (/^(THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(n.nodeName)) {
					$(n).find('td,th').each(function() {
						var tag = opts.tag == 'p' && $(this).find('p').length>0 ? 'div' : opts.tag;
						var n = self.rte.dom.wrapContents(this, tag);
						return ret.push(n);
					})
				} else if (/^(CAPTION|TD|TH|LI|DT|DD)$/.test(n.nodeName)) {
					var tag = opts.tag == 'p' && $(n).find('p').length>0 ? 'div' : opts.tag;
					var n = self.rte.dom.wrapContents(n, tag);
					return ret.push(n);
				} 
			} 
			var prev = buffer.length>0 ? buffer[buffer.length-1] : null;
			if (prev && prev != self.rte.dom.prev(n)) {
				wrap();
			}
			buffer.push(n); 
			if (ndx === null) {
				ndx = ret.length;
				ret.push('dummy'); // заглушка для оборачиваемых элементов
			}
		}
		
		if (sel.nodes.length>0) {
			
			for (var i=0; i < sel.nodes.length; i++) {
				var n = sel.nodes[i];
					// первую и посл текстовые ноды разрезаем, если необходимо
					 if (n.nodeType == 3 && (i==0 || i == sel.nodes.length-1) && $.trim(n.nodeValue).length>0) {
						if (i==0 && sel.so>0) {
							n = n.splitText(sel.so);
						}
						if (i == sel.nodes.length-1 && sel.eo>0) {
							n.splitText(i==0 && sel.so>0 ? sel.eo - sel.so : sel.eo);
						}
					}

					switch (opts.wrap) {
						// оборачиваем только текстовые ноды с br
						case 'text':
							if ((n.nodeType == 1 && n.nodeName == 'BR') || (n.nodeType == 3 && $.trim(n.nodeValue).length>0)) {
								addToBuffer(n);
							} else if (n.nodeType == 1) {
								ret.push(n);
							}
							break;
						// оборачиваем все инлайн элементы	
						case 'inline':
							if (this.rte.dom.isInline(n)) {
								addToBuffer(n);
							} else if (n.nodeType == 1) {
								
								ret.push(n);
							}
							break;
						// оборачиваем все	
						case 'all':
							if (n.nodeType == 1 || !this.rte.dom.isEmpty(n)) {
								addToBuffer(n);
							}
							break;
						// ничего не оборачиваем
						default:
							if (n.nodeType == 1 || !this.rte.dom.isEmpty(n)) {
								ret.push(n);
							}
					}
			};
			wrap();
		}
	
		if (ret.length) {
			this.rte.window.focus();
			
			this.select(ret[0], ret[ret.length-1]);
		}	
		return opts.filter ? this.rte.dom.filter(ret, opts.filter) : ret;
	}

	this.dump = function(ca, s, e, so, eo) {
		var r = this.getRangeAt();
		this.rte.log('commonAncestorContainer');
		this.rte.log(ca || r.commonAncestorContainer);
		// this.rte.log('commonAncestorContainer childs num')
		// this/rte.log((ca||r.commonAncestorContainer).childNodes.length)
		this.rte.log('startContainer');
		this.rte.log(s || r.startContainer);
		this.rte.log('startOffset: '+(so>=0 ? so : r.startOffset));
		this.rte.log('endContainer');
		this.rte.log(e||r.endContainer);
		this.rte.log('endOffset: '+(eo>=0 ? eo : r.endOffset));
	}

	/**
	 * Возвращает массив выбранных нод, как есть
	 *
	 * @param   bool           возвращать если выделение схлопнуто
	 * @param   bool           "блочное" выделение (текстовые ноды включаются полностью, не зависимо от offset)
	 * @return  Array
	 **/
	this.selectedRaw = function(collapsed, blocks) {
		var res = {so : null, eo : null, nodes : []};
		var r   = this.getRangeAt(true);
		var ca  = r.commonAncestorContainer;
		var s, e;  // start & end nodes
		var sf  = false; // start node fully selected
		var ef  = false; // end node fully selected
		
		// возвращает true, если нода не текстовая или выделена полностью
		function isFullySelected(n, s, e) {
			if (n.nodeType == 3) {
				e = e>=0 ? e : n.nodeValue.length;
				return (s==0 && e==n.nodeValue.length) || $.trim(n.nodeValue).length == $.trim(n.nodeValue.substring(s, e)).length;
			} 
			return true;
		}
		
		// возвращает true, если нода пустая или в ней не выделено ни одного непробельного символа
		function isEmptySelected(n, s, e) {
			if (n.nodeType == 1) {
				return self.rte.dom.isEmpty(n);
			} else if (n.nodeType == 3) {
				return $.trim(n.nodeValue.substring(s||0, e>=0 ? e : n.nodeValue.length)).length == 0;
			} 
			return true;
		}
		
		
		//this.dump()
		// начальная нода
		if (r.startContainer.nodeType == 1) {
			if (r.startOffset<r.startContainer.childNodes.length) {
				s = r.startContainer.childNodes[r.startOffset];
				res.so = s.nodeType == 1 ? null : 0;
			} else {
				s = r.startContainer.childNodes[r.startOffset-1];
				res.so = s.nodeType == 1 ? null : s.nodeValue.length;
			}
		} else {
			s = r.startContainer;
			res.so = r.startOffset;
		} 
		
		// выделение схлопнуто
		if (r.collapsed) {
			if (collapsed) {
				//  блочное выделение
				if (blocks) {
					s = realSelected(s);
					if (!this.rte.dom.isEmpty(s) || (s = this.rte.dom.next(s))) {
						res.nodes = [s];
					} 
					
					// добавляем инлайн соседей 
					if (this.rte.dom.isInline(s)) {
						res.nodes = this.rte.dom.toLineStart(s).concat(res.nodes, this.rte.dom.toLineEnd(s));
					}
					
					// offset для текстовых нод
					if (res.nodes.length>0) {
						res.so = res.nodes[0].nodeType == 1 ? null : 0;
						res.eo = res.nodes[res.nodes.length-1].nodeType == 1 ? null : res.nodes[res.nodes.length-1].nodeValue.length;
					}
					
				} else if (!this.rte.dom.isEmpty(s)) {
					res.nodes = [s];
				}
				
			}
			return res;
		}
		
		// конечная нода
		if (r.endContainer.nodeType == 1) {
			e = r.endContainer.childNodes[r.endOffset-1];
			res.eo = e.nodeType == 1 ? null : e.nodeValue.length;
		} else {
			e = r.endContainer;
			res.eo = r.endOffset;
		} 
		// this.rte.log('select 1')
		//this.dump(ca, s, e, res.so, res.eo)
		
		// начальная нода выделена полностью - поднимаемся наверх по левой стороне
		if (s.nodeType == 1 || blocks || isFullySelected(s, res.so, s.nodeValue.length)) {
//			this.rte.log('start text node is fully selected')
			s = realSelected(s, ca, 'left');
			sf = true;
			res.so = s.nodeType == 1 ? null : 0;
		}
		// конечная нода выделена полностью - поднимаемся наверх по правой стороне
		if (e.nodeType == 1 || blocks || isFullySelected(e, 0,  res.eo)) {
//			this.rte.log('end text node is fully selected')
			e = realSelected(e, ca, 'right');
			ef = true;
			res.eo = e.nodeType == 1 ? null : e.nodeValue.length;
		}

		// блочное выделение - если ноды не элементы - поднимаемся к родителю, но ниже контейнера
		if (blocks) {
			if (s.nodeType != 1 && s.parentNode != ca && s.parentNode.nodeName != 'BODY') {
				s = s.parentNode;
				res.so = null;
			}
			if (e.nodeType != 1 && e.parentNode != ca && e.parentNode.nodeName != 'BODY') {
				e = e.parentNode;
				res.eo = null;
			}
		}

		// если контенер выделен полностью, поднимаемся наверх насколько можно
		if (s.parentNode == e.parentNode && s.parentNode.nodeName != 'BODY' && (sf && this.rte.dom.isFirstNotEmpty(s)) && (ef && this.rte.dom.isLastNotEmpty(e))) {
//			this.rte.log('common parent')
			s = e = s.parentNode;
			res.so = s.nodeType == 1 ? null : 0;
			res.eo = e.nodeType == 1 ? null : e.nodeValue.length;
		}
		// начальная нода == конечной ноде
		if (s == e) {
//			this.rte.log('start is end')
			if (!this.rte.dom.isEmpty(s)) {
				res.nodes.push(s);
			}
			return res;
		}
		 // this.rte.log('start 2')
		  //this.dump(ca, s, e, res.so, res.eo)
		
		// находим начальную и конечную точки - ноды из иерархии родителей начальной и конечно ноды, у которых родитель - контейнер
		var sp = s;
		while (sp.nodeName != 'BODY' && sp.parentNode !== ca && sp.parentNode.nodeName != 'BODY') {
			sp = sp.parentNode;
		}
		//this.rte.log(s.nodeName)
		// this.rte.log('start point')
		// this.rte.log(sp)
		
		var ep = e;
//		this.rte.log(ep)
		while (ep.nodeName != 'BODY' && ep.parentNode !== ca && ep.parentNode.nodeName != 'BODY') {
			// this.rte.log(ep)
			ep = ep.parentNode;
		}
		// this.rte.log('end point')
		// this.rte.log(ep)
		
		
		//  если начальная нода не пустая - добавляем ее
		if (!isEmptySelected(s, res.so, s.nodeType==3 ? s.nodeValue.length : null)) {
			res.nodes.push(s);
		}
		// поднимаемся от начальной ноды до начальной точки
		var n = s;
		while (n !== sp) {
			var _n = n;
			while ((_n = this.rte.dom.next(_n))) {
					res.nodes.push(_n);
			}
			n = n.parentNode;
		}
		// от начальной точки до конечной точки
		n = sp;
		while ((n = this.rte.dom.next(n)) && n!= ep ) {
//			this.rte.log(n)
			res.nodes.push(n);
		}
		// поднимаемся от конечной ноды до конечной точки, результат переворачиваем
		var tmp = [];
		n = e;
		while (n !== ep) {
			var _n = n;
			while ((_n = this.rte.dom.prev(_n))) {
				tmp.push(_n);
			}
			n = n.parentNode;
		}
		if (tmp.length) {
			res.nodes = res.nodes.concat(tmp.reverse());
		}
		//  если конечная нода не пустая и != начальной - добавляем ее
		if (!isEmptySelected(e, 0, e.nodeType==3 ? res.eo : null)) {
			res.nodes.push(e);
		}
		
		if (blocks) {
			// добавляем инлайн соседей слева
			if (this.rte.dom.isInline(s)) {
				res.nodes = this.rte.dom.toLineStart(s).concat(res.nodes);
				res.so    = res.nodes[0].nodeType == 1 ? null : 0;
			}
			// добавляем инлайн соседей справа
			if (this.rte.dom.isInline(e)) {
				res.nodes = res.nodes.concat(this.rte.dom.toLineEnd(e));
				res.eo    = res.nodes[res.nodes.length-1].nodeType == 1 ? null : res.nodes[res.nodes.length-1].nodeValue.length;
			}
		}
		
		// все радуются! :)
		return res;
	}
	
}

})(jQuery);/**
 * @class elRTE User interface controller
 *
 * @param  elRTE  rte объект-редактор
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @todo: this.domElem.removeClass('disabled') - move to ui.update;
 * @todo: add dom and selection as button members
 * Copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui = function(rte) {
	this.rte      = rte;
	this._buttons = [];
	var self      = this,
		tb        = this.rte.options.toolbars[rte.options.toolbar && rte.options.toolbars[rte.options.toolbar] ? rte.options.toolbar : 'normal'],
		tbl       = tb.length,
		p, pname, pl, n, c, b, i;

	// add prototype to all buttons
	for (i in this.buttons) {
		if (this.buttons.hasOwnProperty(i) && i != 'button') {
			this.buttons[i].prototype = this.buttons.button.prototype;
		}
	}

	// create buttons and put on toolbar
	while (tbl--) {
		first = (tbl == 0 ? true : false);
		if (tb[tbl - 1] == 'eol') { first = true; }

		pname = tb[tbl];

		// special 'end of line' panel, starts next panel on a new line
		if (pname == 'eol') {
			$(this.rte.doc.createElement('br')).prependTo(this.rte.toolbar);
			continue;
		}

		p = $('<ul class="panel-'+pname+(first ? ' first' : '')+'" />').prependTo(this.rte.toolbar);
		p.bind('mousedown', function(e) {
			e.preventDefault();
		})
		pl = this.rte.options.panels[pname].length;
		while (pl--) {
			n = this.rte.options.panels[pname][pl];
			c = this.buttons[n] || this.buttons.button;
			this._buttons.push((b = new c(this.rte, n)));
			p.prepend(b.domElem);
		}
	}

	this.update();

	this.disable = function() {
		$.each(self._buttons, function() {
			!this.active && this.domElem.addClass('disabled');
		});
	}

}

/**
 * Обновляет кнопки - вызывает метод update() для каждой кнопки
 *
 * @return void
 **/
elRTE.prototype.ui.prototype.update = function(cleanCache) {
	cleanCache && this.rte.selection.cleanCache();
	var n    = this.rte.selection.getNode(),
		p    = this.rte.dom.parents(n, '*'),
		rtl = this.rte.rtl,
		sep  = rtl ? ' &laquo; ' : ' &raquo; ',
		path = '', name, i;

	function _name(n) {
		var name = n.nodeName.toLowerCase();
		n = $(n)
		if (name == 'img') {
			if (n.hasClass('elrte-media')) {
				name = 'media';
			} else if (n.hasClass('elrte-google-maps')) {
				name = 'google map';
			} else if (n.hasClass('elrte-yandex-maps')) {
				name = 'yandex map';
			} else if (n.hasClass('elrte-pagebreak')) {
				name = 'pagebreak';
			}
		}
		return name;
	}

	if (n && n.nodeType == 1 && n.nodeName != 'BODY') {
		p.unshift(n);
	}

	if (!rtl) {
		p = p.reverse();
	}

	for (i=0; i < p.length; i++) {
		path += (i>0 ? sep : '')+_name(p[i]);
	}

	this.rte.statusbar.html(path);
	$.each(this._buttons, function() {
		this.update();
	});
	this.rte.window.focus();
}



elRTE.prototype.ui.prototype.buttons = {

	/**
	 * @class кнопка на toolbar редактора
	 * реализует поведение по умолчанию и является родителем для других кнопок
	 *
	 * @param  elRTE  rte   объект-редактор
	 * @param  String name  название кнопки (команда исполняемая document.execCommand())
	 **/
	button : function(rte, name) {
		var self     = this;
		this.rte     = rte;
		this.active = false;
		this.name    = name;
		this.val     = null;
		this.domElem = $('<li style="-moz-user-select:-moz-none" class="'+name+' rounded-3" name="'+name+'" title="'+this.rte.i18n(this.rte.options.buttons[name] || name)+'" unselectable="on" />')
			.hover(
				function() { $(this).addClass('hover'); },
				function() { $(this).removeClass('hover'); }
			)
			.click( function(e) {
				e.stopPropagation();
				e.preventDefault();
				if (!$(this).hasClass('disabled')) {
					// try{
						self.command();
					// } catch(e) {
					// 	self.rte.log(e)
					// }
					
				}
				self.rte.window.focus();
			});
	}
}

/**
 * Обработчик нажатия на кнопку на тулбаре. Выполнение команды или открытие окна|меню и тд
 *
 * @return void
 **/
elRTE.prototype.ui.prototype.buttons.button.prototype.command = function() {
	this.rte.history.add();
	try {
		this.rte.doc.execCommand(this.name, false, this.val);
	} catch(e) {
		return this.rte.log('commands failed: '+this.name);
	}

	this.rte.ui.update(true);
}

/**
 * Обновляет состояние кнопки
 *
 * @return void
 **/
elRTE.prototype.ui.prototype.buttons.button.prototype.update = function() {
	try {
		if (!this.rte.doc.queryCommandEnabled(this.name)) {
			return this.domElem.addClass('disabled');
		} else {
			this.domElem.removeClass('disabled');
		}
	} catch (e) {
		return;
	}
	try {
		if (this.rte.doc.queryCommandState(this.name)) {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	} catch (e) { }
}

})(jQuery);
/*
 * Misc utils for elRTE
 *
 * @param Object rte - editor
 * @todo Подумать, что из этого реально нужно и навести порядок. Возможно часть перенести в ellib
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 */
(function($) {
elRTE.prototype.utils = function(rte) {
	this.rte     = rte;
	this.url     = null;
	// domo arigato, Steave, http://blog.stevenlevithan.com/archives/parseuri
	this.reg     = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
	this.baseURL = '';
	this.path    = '';
	/**
	 * entities map
	 **/
	this.entities = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'};
	/**
	 * entities regexp
	 **/
	this.entitiesRegExp = /[<>&\"]/g;
	/**
	 * media info
	 **/
	this.media = [{ 
			type     : 'application/x-shockwave-flash', 
			classid  : ['clsid:d27cdb6e-ae6d-11cf-96b8-444553540000'], 
			codebase : 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0' 
		}, { 
			type     : 'application/x-director', 
			classid  : ['clsid:166b1bca-3f9c-11cf-8075-444553540000'], 
			codebase : 'http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0' 
		}, { 
			type     : 'application/x-mplayer2', 
			classid  : ['clsid:6bf52a52-394a-11d3-b153-00c04f79faa6', 'clsid:22d6f312-b0f6-11d0-94ab-0080c74c7e95', 'clsid:05589fa1-c356-11ce-bf01-00aa0055595a'], 
			codebase : 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701' 
		}, { 
			type     : 'video/quicktime', 
			classid  : ['clsid:02bf25d5-8c17-4b23-bc80-d3488abddc6b'], 
			codebase : 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0' 
		}, { 
			type     : 'audio/x-pn-realaudio-plugin', 
			classid  : ['clsid:cfcdaa03-8be4-11cf-b84b-0020afbbccfa'], 
			codebase : 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'
		}];
	
	// rgb color regexp
	this.rgbRegExp = /\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*/i;
	// regexp to detect color in border/background properties
	this.colorsRegExp = /aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|rgb\s*\([^\)]+\)/i;
	// web safe colors
	this.colors = {
		aqua    : '#00ffff',
		black   : '#000000',
		blue    : '#0000ff',
		fuchsia : '#ff00ff',
		gray    : '#808080',
		green   : '#008000',
		lime    : '#00ff00',
		maroon  : '#800000',
		navy    : '#000080',
		olive   : '#808000',
		orange  : '#ffa500',
		purple  : '#800080',
		red     : '#ff0000',
		silver  : '#c0c0c0',
		teal    : '#008080',
		white   : '#fffffff',
		yellow  : '#ffff00'
	}
	
	var self     = this;
	
	this.rgb2hex = function(str) {
		return this.color2Hex(''+str)
	}
	
	this.toPixels = function(num) {
		var m = num.match(/([0-9]+\.?[0-9]*)\s*(px|pt|em|%)/);
		if (m) {
			num  = m[1];
			unit = m[2];
		} 
		if (num[0] == '.') {
			num = '0'+num;
		}
		num = parseFloat(num);

		if (isNaN(num)) {
			return '';
		}
		var base = parseInt($(document.body).css('font-size')) || 16;
		switch (unit) {
			case 'em': return parseInt(num*base);
			case 'pt': return parseInt(num*base/12);
			case '%' : return parseInt(num*base/100);
		}
		return num;
	}
	
	// TODO: add parse rel path ../../etc
	this.absoluteURL = function(url) {
		!this.url && this._url();
		url = $.trim(url);
		if (!url) {
			return '';
		}
		// ссылки на якоря не переводим в абс
		if (url[0] == '#') {
			return url;
		}
		var u = this.parseURL(url);

		if (!u.host && !u.path && !u.anchor) {
			//this.rte.log('Invalid URL: '+url)
			return '';
		}
		if (!this.rte.options.absoluteURLs) { 
			return url;
		}
		if (u.protocol) {
			//this.rte.log('url already absolute: '+url);
			return url;
		}
		if (u.host && (u.host.indexOf('.')!=-1 || u.host == 'localhost')) {
			//this.rte.log('no protocol');
			return this.url.protocol+'://'+url;
		}
		if (url[0] == '/') {
			url = this.baseURL+url;
		} else {
			if (url.indexOf('./') == 0) {
				url = url.substring(2);
			}
			url = this.baseURL+this.path+url;
		}
		return url;
	}
	
	this.parseURL = function(url) {
		var u   = url.match(this.reg);
		var ret = {};
		$.each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(i) {
			ret[this] = u[i];
		});
		if (!ret.host.match(/[a-z0-9]/i)) {
			ret.host = '';
		}
		return ret;
	}
	
	this.trimEventCallback = function(c) {
		c = c ? c.toString() : '';
		return $.trim(c.replace(/\r*\n/mg, '').replace(/^function\s*on[a-z]+\s*\(\s*event\s*\)\s*\{(.+)\}$/igm, '$1'));
	}
	
	this._url = function() {
		this.url     = this.parseURL(window.location.href);
		this.baseURL = this.url.protocol+'://'+(this.url.userInfo ?  parts.userInfo+'@' : '')+this.url.host+(this.url.port ? ':'+this.url.port : '');
		this.path    = !this.url.file ? this.url.path : this.url.path.substring(0, this.url.path.length - this.url.file.length);
	}


	/**
	 * Create object (map) from array
	 *
	 * @param   Array
	 * @return  Object
	 **/
	this.makeObject = function(o) {
		var m = {};
		$.each(o, function(i, e) {
			m[e] = e;
		});
		return m;
	}

	/**
	 * Encode entities in string
	 *
	 * @param   String
	 * @return  String
	 **/
	this.encode = function(s) {
		var e = this.entities;
		return (''+s).replace(this.entitiesRegExp, function(c) {
			return e[c];
		});
	}
	
	/**
	 * Decode entities in string
	 *
	 * @param   String
	 * @return  String
	 **/
	this.decode = function(s) {
		return $('<div/>').html(s||'').text();
	}
	
	/**
	 * Parse style string into object
	 *
	 * @param   String
	 * @return  Object
	 **/
	this.parseStyle = function(s) {
		var st = {}, a = this.rte.options.allowBrowsersSpecStyles, t, n, v, p;
		
		if (typeof(s) == 'string' && s.length) {
			
			$.each(s.replace(/&quot;/gi, "'").split(';'), function(i, str) {
				if ((p = str.indexOf(':')) !== -1) {
					n = $.trim(str.substr(0, p));
					v = $.trim(str.substr(p+1))
					if (n == 'color' || n == 'background-color') {
						v = v.toLowerCase();
					}
					if (n && v && (a || n.substring(0, 1) != '-')) {
						st[n] = v;
					}
				}
			});
		}
		return st;
	}
	
	
	/**
	 * Compact some style properties and convert colors in hex
	 *
	 * @param   Object
	 * @return  Object
	 **/
	this.compactStyle = function(s) {
		var self = this;

		if (s.border == 'medium none') {
			delete s.border;
		}
		
		$.each(s, function(n, v) {
			if (/color$/i.test(n)) {
				s[n] = self.color2Hex(v);
			} else if (/^(border|background)$/i.test(n)) {
				s[n] = v.replace(self.colorsRegExp, function(m) {
					return self.color2Hex(m);
				});
			}
		});
		
		if (s['border-width']) {
			s.border = s['border-width']+' '+(s['border-style']||'solid')+' '+(s['border-color']||'#000');
			delete s['border-width'];
			delete s['border-style'];
			delete s['border-color'];
		}
		
		if (s['background-image']) {
			s.background = (s['background-color']+' ')||''+s['background-image']+' '+s['background-position']||'0 0'+' '+s['background-repeat']||'repeat';
			delete s['background-image'];
			delete['background-image'];
			delete['background-position'];
			delete['background-repeat'];
		}
		
		if (s['margin-top'] && s['margin-right'] && s['margin-bottom'] && s['margin-left']) {
			s.margin = s['margin-top']+' '+s['margin-right']+' '+s['margin-bottom']+' '+s['margin-left'];
			delete s['margin-top'];
			delete s['margin-right'];
			delete s['margin-bottom'];
			delete s['margin-left'];
		}
		
		if (s['padding-top'] && s['padding-right'] && s['padding-bottom'] && s['padding-left']) {
			s.padding = s['padding-top']+' '+s['padding-right']+' '+s['padding-bottom']+' '+s['padding-left'];
			delete s['padding-top'];
			delete s['padding-right'];
			delete s['padding-bottom'];
			delete s['padding-left'];
		}
		
		if (s['list-style-type'] || s['list-style-position'] || s['list-style-image']) {
			s['list-style'] = $.trim(s['list-style-type']||' '+s['list-style-position']||''+s['list-style-image']||'');
			delete s['list-style-type'];
			delete s['list-style-position'];
			delete s['list-style-image'];
		}
		
		return s;
	}
	
	/**
	 * Serialize style object into string
	 *
	 * @param   Object  style map
	 * @param   Boolean flag - compact style?
	 * @return  String
	 **/
	this.serializeStyle = function(o, c) {
		var s = [];
		// c=true
		$.each(c ? this.compactStyle(o) : o, function(n, v) {
			v && s.push(n+':'+v);
		});
		return s.join(';');
	}

	/**
	 * Parse class string into object
	 *
	 * @param   String
	 * @return  Object
	 **/
	this.parseClass = function(c) {
		c = $.trim(c); 
		// this.rte.log(c)
		return c.length ? this.makeObject(c.split(/\s+/)) : {};
		return c.length ? c.split(/\s+/) : [];
	}

	/**
	 * Serialize class object into string
	 *
	 * @param   Object
	 * @return  String
	 **/
	this.serializeClass = function(c) {
		// return c.join(' ')
		var s = [];
		// this.rte.log(c)
		var rte = this.rte
		$.each(c, function(n) {
			s.push(n);
			// rte.log(typeof(n))
		});
		return s.join(' ');
	}
	
	/**
	 * Return required media type info
	 *
	 * @param   String  mimetype
	 * @param   String  classid
	 * @return  Object
	 **/
	this.mediaInfo = function(t, c) {
		var l = this.media.length;
		
		while (l--) {
			if (t === this.media[l].type || (c && $.inArray(c, this.media[l].classid) != -1)) {
				return this.media[l];
			}
		}
	}

	/**
	 * Return color hex value
	 *
	 * @param   String   color name or rgb
	 * @return  String
	 **/
	this.color2Hex = function(c) {
		var m;
		
		c = c||'';
		
		if (c.indexOf('#') === 0) {
			return c;
		}
		
		
		function hex(s) {
			s = parseInt(s).toString(16);
			return s.length > 1 ? s : '0' + s; 
		};
		
		if (this.colors[c]) {
			return this.colors[c];
		}
		if ((m = c.match(this.rgbRegExp))) {
			return '#'+hex(m[1])+hex(m[2])+hex(m[3]);
		}
		return '';
	}
	

	
	
}

})(jQuery);/**
 * @class w3cRange  - w3c text range emulation for "strange" browsers
 *
 * @param  elRTE  rte  объект-редактор
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.w3cRange = function(rte) {
	var self                     = this;
	this.rte                     = rte;
	this.r                       = null;
	this.collapsed               = true;
	this.startContainer          = null;
	this.endContainer            = null;
	this.startOffset             = 0;
	this.endOffset               = 0;
	this.commonAncestorContainer = null;
	
	this.range = function() {
		try { 
			this.r = this.rte.window.document.selection.createRange(); 
		} catch(e) { 
			this.r = this.rte.doc.body.createTextRange(); 
		}
		return this.r;
	}
	
	this.insertNode = function(html) {
		this.range();
		self.r.collapse(false)
		var r = self.r.duplicate();
		r.pasteHTML(html);
	}
	
	this.getBookmark = function() {
		this.range();
		if (this.r.item) {
			var n = this.r.item(0);
			this.r = this.rte.doc.body.createTextRange();
			this.r.moveToElementText(n);
		}
		return this.r.getBookmark();
	}
	
	this.moveToBookmark = function(bm) {
		this.rte.window.focus();
		this.range().moveToBookmark(bm);
		this.r.select();
	}
	
	/**
	 * Обновляет данные о выделенных нодах
	 *
	 * @return void
	 **/
	this.update = function() {

		function _findPos(start) {
			var marker = '\uFEFF';
			var ndx = offset = 0;
			var r = self.r.duplicate();
			r.collapse(start);
			var p = r.parentElement();
			if (!p || p.nodeName == 'HTML') {
				return {parent : self.rte.doc.body, ndx : ndx, offset : offset};
			}

			r.pasteHTML(marker);
			
			childs = p.childNodes;
			for (var i=0; i < childs.length; i++) {
				var n = childs[i];
				if (i>0 && (n.nodeType!==3 || childs[i-1].nodeType !==3)) {
					ndx++;
				}
				if (n.nodeType !== 3) {
					offset = 0;
				} else {
					var pos = n.nodeValue.indexOf(marker);
					if (pos !== -1) {
						offset += pos;
						break;
					}
					offset += n.nodeValue.length;
				}
			};
			r.moveStart('character', -1);
			r.text = '';
			return {parent : p, ndx : Math.min(ndx, p.childNodes.length-1), offset : offset};
		}

		this.range();
		this.startContainer = this.endContainer = null;

		if (this.r.item) {
			this.collapsed = false;
			var i = this.r.item(0);
			this.setStart(i.parentNode, this.rte.dom.indexOf(i));
			this.setEnd(i.parentNode, this.startOffset+1);
		} else {
			this.collapsed = this.r.boundingWidth == 0;
			var start = _findPos(true); 
			var end   = _findPos(false);
			
			start.parent.normalize();
			end.parent.normalize();
			start.ndx = Math.min(start.ndx, start.parent.childNodes.length-1);
			end.ndx = Math.min(end.ndx, end.parent.childNodes.length-1);
			if (start.parent.childNodes[start.ndx].nodeType && start.parent.childNodes[start.ndx].nodeType == 1) {
				this.setStart(start.parent, start.ndx);
			} else {
				this.setStart(start.parent.childNodes[start.ndx], start.offset);
			}
			if (end.parent.childNodes[end.ndx].nodeType && end.parent.childNodes[end.ndx].nodeType == 1) {
				this.setEnd(end.parent, end.ndx);
			} else {
				this.setEnd(end.parent.childNodes[end.ndx], end.offset);
			}
			// this.dump();
			this.select();
		}
		return this;
	}
	
	this.isCollapsed = function() {
		this.range();
		this.collapsed = this.r.item ? false : this.r.boundingWidth == 0;
		return this.collapsed;
	}
	
	/**
	 * "Схлопывает" выделение
	 *
	 * @param  bool  toStart - схлопывать выделение к началу или к концу
	 * @return void
	 **/
	this.collapse = function(toStart) {
		this.range();
		if (this.r.item) {
			var n = this.r.item(0);
			this.r = this.rte.doc.body.createTextRange();
			this.r.moveToElementText(n);
		}
		this.r.collapse(toStart);
		this.r.select();
		this.collapsed = true;
	}

	this.getStart = function() {
		this.range();
		if (this.r.item) {
			return this.r.item(0);
		}
		var r = this.r.duplicate();
		r.collapse(true);
		var s = r.parentElement();
		return s && s.nodeName == 'BODY' ? s.firstChild : s;
	}
	
	
	this.getEnd = function() {
		this.range();
		if (this.r.item) {
			return this.r.item(0);
		}
		var r = this.r.duplicate();
		r.collapse(false);
		var e = r.parentElement();
		return e && e.nodeName == 'BODY' ? e.lastChild : e;
	}

	
	/**
	 * Устанавливает начaло выделения на указаную ноду
	 *
	 * @param  Element  node    нода
	 * @param  Number   offset  отступ от начала ноды
	 * @return void
	 **/
	this.setStart = function(node, offset) {
		this.startContainer = node;
		this.startOffset    = offset;
		if (this.endContainer) {
			this.commonAncestorContainer = this.rte.dom.findCommonAncestor(this.startContainer, this.endContainer);
		}
	}
	
	/**
	 * Устанавливает конец выделения на указаную ноду
	 *
	 * @param  Element  node    нода
	 * @param  Number   offset  отступ от конца ноды
	 * @return void
	 **/
	this.setEnd = function(node, offset) {
		this.endContainer = node;
		this.endOffset    = offset;
		if (this.startContainer) {
			this.commonAncestorContainer = this.rte.dom.findCommonAncestor(this.startContainer, this.endContainer);
		}
	}
	
	/**
	 * Устанавливает начaло выделения перед указаной нодой
	 *
	 * @param  Element  node    нода
	 * @return void
	 **/
	this.setStartBefore = function(n) {
		if (n.parentNode) {
			this.setStart(n.parentNode, this.rte.dom.indexOf(n));
		}
	}
	
	/**
	 * Устанавливает начaло выделения после указаной ноды
	 *
	 * @param  Element  node    нода
	 * @return void
	 **/
	this.setStartAfter = function(n) {
		if (n.parentNode) {
			this.setStart(n.parentNode, this.rte.dom.indexOf(n)+1);
		}
	}
	
	/**
	 * Устанавливает конец выделения перед указаной нодой
	 *
	 * @param  Element  node    нода
	 * @return void
	 **/
	this.setEndBefore = function(n) {
		if (n.parentNode) {
			this.setEnd(n.parentNode, this.rte.dom.indexOf(n));
		}
	}
	
	/**
	 * Устанавливает конец выделения после указаной ноды
	 *
	 * @param  Element  node    нода
	 * @return void
	 **/
	this.setEndAfter = function(n) {
		if (n.parentNode) {
			this.setEnd(n.parentNode, this.rte.dom.indexOf(n)+1);
		}
	}
	
	/**
	 * Устанавливает новое выделение после изменений
	 *
	 * @return void
	 **/
	this.select = function() {
		// thanks tinymice authors
		function getPos(n, o) {
			if (n.nodeType != 3) {
				return -1;
			}
			var c   ='\uFEFF';
			var val = n.nodeValue;
			var r   = self.rte.doc.body.createTextRange();
			n.nodeValue = val.substring(0, o) + c + val.substring(o);
			r.moveToElementText(n.parentNode);
			r.findText(c);
			var p = Math.abs(r.moveStart('character', -0xFFFFF));
			n.nodeValue = val;
			return p;
		};
		
		this.r = this.rte.doc.body.createTextRange(); 
		var so = this.startOffset;
		var eo = this.endOffset;
		var s = this.startContainer.nodeType == 1 
			? this.startContainer.childNodes[Math.min(so, this.startContainer.childNodes.length - 1)]
			: this.startContainer;
		var e = this.endContainer.nodeType == 1 
			? this.endContainer.childNodes[Math.min(so == eo ? eo : eo - 1, this.endContainer.childNodes.length - 1)]
			: this.endContainer;

		if (this.collapsed) {
			if (s.nodeType == 3) {
				var p = getPos(s, so);
				this.r.move('character', p);
			} else {
				this.r.moveToElementText(s);
				this.r.collapse(true);
			}
		} else {
			var r  = this.rte.doc.body.createTextRange(); 
			var sp = getPos(s, so);
			var ep = getPos(e, eo);
			if (s.nodeType == 3) {
				this.r.move('character', sp);
			} else {
				this.r.moveToElementText(s);
			}
			if (e.nodeType == 3) {
				r.move('character', ep);
			} else {
				r.moveToElementText(e);
			}
			this.r.setEndPoint('EndToEnd', r);
		}
		
		try {
			this.r.select();
		} catch(e) {
			
		}
		if (r) {
			r = null;
		}
	}
	
	this.dump = function() {
		this.rte.log('collapsed: '+this.collapsed);
		//this.rte.log('commonAncestorContainer: '+this.commonAncestorContainer.nodeName||'#text')
		this.rte.log('startContainer: '+(this.startContainer ? this.startContainer.nodeName : 'non'));
		this.rte.log('startOffset: '+this.startOffset);
		this.rte.log('endContainer: '+(this.endContainer ? this.endContainer.nodeName : 'none'));
		this.rte.log('endOffset: '+this.endOffset);
	}
	
}
})(jQuery);
(function($) {
	elRTE.prototype.ui.prototype.buttons.about = function(rte, name)  {
		
		this.constructor.prototype.constructor.call(this, rte, name);
		this.active  = true;
		this.command = function() {
			var opts, d, txt;
			
			opts = {
				rtl : rte.rtl,
				submit : function(e, d) {  d.close(); },
				dialog : {
					width : 560,
					title : this.rte.i18n('About this software'),
					buttons     : {
						Ok : function() { $(this).dialog('destroy'); }
					}
				}
			}

			txt = '<div class="elrte-logo"></div><h3>'+this.rte.i18n('About elRTE')+'</h3><br clear="all"/>'
				+'<div class="elrte-ver">'+this.rte.i18n('Version')+': '+this.rte.version+' ('+this.rte.build+')</div>' 
				+'<div class="elrte-ver">jQuery: '+$('<div/>').jquery+'</div>' 
				+'<div class="elrte-ver">jQueryUI: '+$.ui.version+'</div>' 
				+'<div class="elrte-ver">'+this.rte.i18n('Licence')+': BSD Licence</div>'
				+'<p>'
				+this.rte.i18n('elRTE is an open-source JavaScript based WYSIWYG HTML-editor.')+'<br/>'
				+this.rte.i18n('Main goal of the editor - simplify work with text and formating (HTML) on sites, blogs, forums and other online services.')+'<br/>'
				+this.rte.i18n('You can use it in any commercial or non-commercial projects.')
				+'</p>'
				+'<h4>'+this.rte.i18n('Authors')+'</h4>'
				+'<table class="elrte-authors">'
				+'<tr><td>Dmitry (dio) Levashov &lt;dio@std42.ru&gt;</td><td>'+this.rte.i18n('Chief developer')+'</td></tr>'
				+'<tr><td>Troex Nevelin &lt;troex@fury.scancode.ru&gt;</td><td>'+this.rte.i18n('Developer, tech support')+'</td></tr>'
				+'<tr><td>Valentin Razumnyh &lt;content@std42.ru&gt;</td><td>'+this.rte.i18n('Interface designer')+'</td></tr>'
				+'<tr><td>Tawfek Daghistani &lt;tawfekov@gmail.com&gt;</td><td>'+this.rte.i18n('RTL support')+'</td></tr>'
				+(this.rte.options.lang != 'en' ? '<tr><td>'+this.rte.i18n('_translator')+'</td><td>'+this.rte.i18n('_translation')+'</td></tr>' : '')
				+'</table>'
				+'<div class="elrte-copy">Copyright &copy; 2009-2011, <a href="http://www.std42.ru">Studio 42</a></div>'
				+'<div class="elrte-copy">'+this.rte.i18n('For more information about this software visit the')+' <a href="http://elrte.org">'+this.rte.i18n('elRTE website')+'.</a></div>'
				+'<div class="elrte-copy">Twitter: <a href="http://twitter.com/elrte_elfinder">elrte_elfinder</a></div>';
			
			d = new elDialogForm(opts);
			d.append(txt);
			d.open();
		}
		
		this.update = function() {
			this.domElem.removeClass('disabled');
		}
		
	}
	
})(jQuery);
/**
 * @class кнопка - Закладка (открывает диалоговое окно)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.anchor = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.input = $('<input type="text" />').attr('name', 'anchor').attr('size', '16').addClass('span2')
	var self = this;
	
	this.command = function() {
		var opts = {
			rtl : this.rte.rtl,
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); d.close(); self.set();  },
			dialog : {
				title : this.rte.i18n('Bookmark')
			}
		}

		
		this.anchor = this.rte.dom.selfOrParentAnchor(this.rte.selection.getEnd()) || rte.dom.create('a');
		!this.rte.selection.collapsed() && this.rte.selection.collapse(false);
		this.input.val($(this.anchor).addClass('elrte-anchor').attr('name'));
		this.rte.selection.saveIERange();
		var d = new elDialogForm(opts);
		d.append([this.rte.i18n('Bookmark name'), this.input], null, true).open();
		setTimeout(function() { self.input.focus()}, 20);
	}
	
	this.update = function() {
		var n = this.rte.selection.getNode();
		if (this.rte.dom.selfOrParentLink(n)) {
			this.domElem.addClass('disabled');
		} else if (this.rte.dom.selfOrParentAnchor(n)) {
			this.domElem.removeClass('disabled').addClass('active');
		} else {
			this.domElem.removeClass('disabled').removeClass('active');
		}
	}
	
	this.set = function() {
		var n = $.trim(this.input.val());

		if (n) {
			this.rte.history.add();
			if (!this.anchor.parentNode) {
				this.rte.selection.insertHtml('<a name="'+n+'" title="'+this.rte.i18n('Bookmark')+': '+n+'" class="elrte-anchor"></a>');
			} else {
				this.anchor.name = n;
				this.anchor.title = this.rte.i18n('Bookmark')+': '+n;
			}
		} else if (this.anchor.parentNode) {
			this.rte.history.add();
			this.anchor.parentNode.removeChild(this.anchor);
		}
	}
}
})(jQuery);
/**
 * @class кнопка - Цитата
 * Если выделение схлопнуто и находится внутри цитаты - она удаляется
 * Новые цитаты создаются только из несхлопнутого выделения
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.blockquote = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var n, nodes;
		this.rte.history.add();
		if (this.rte.selection.collapsed() && (n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^BLOCKQUOTE$/))) {
			$(n).replaceWith($(n).html());
		} else {
			nodes = this.rte.selection.selected({wrap : 'all', tag : 'blockquote'});
			nodes.length && this.rte.selection.select(nodes[0], nodes[nodes.length-1]);
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		if (this.rte.selection.collapsed()) {
			if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^BLOCKQUOTE$/)) {
				this.domElem.removeClass('disabled').addClass('active');
			} else {
				this.domElem.addClass('disabled').removeClass('active');
			}
		} else {
			this.domElem.removeClass('disabled active');
		}
	}
}
})(jQuery);
/**
 * @class кнопки "копировать/вырезать/вставить" 
 * в firefox показывает предложение нажать Ctl+c, в остальных - копирует
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.copy = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		
		if (this.rte.browser.mozilla) {
			try {
				this.rte.doc.execCommand(this.name, false, null);
			} catch (e) {
				var s = ' Ctl + C';
				if (this.name == 'cut') {
					s = ' Ctl + X';
				} else if (this.name == 'paste') {
					s = ' Ctl + V';
				}
				var opts = {
					dialog : {
						title   : this.rte.i18n('Warning'),
						buttons : { Ok : function() { $(this).dialog('close'); } }
					}
				}

				var d = new elDialogForm(opts);
				d.append(this.rte.i18n('This operation is disabled in your browser on security reason. Use shortcut instead.')+': '+s).open();
			}
		} else {
			this.constructor.prototype.command.call(this);
		}
	}
}

elRTE.prototype.ui.prototype.buttons.cut   = elRTE.prototype.ui.prototype.buttons.copy;
elRTE.prototype.ui.prototype.buttons.paste = elRTE.prototype.ui.prototype.buttons.copy;

})(jQuery);(function($) {
	
	elRTE.prototype.ui.prototype.buttons.css = function(rte, name) {
		var self = this;
		this.constructor.prototype.constructor.call(this, rte, name);
		this.cssStyle  = $('<input type="text" size="42" name="style" />').addClass('span1');
		this.cssClass  = $('<input type="text" size="42" name="class" />').addClass('span1');
		this.elementID = $('<input type="text" size="42" name="id" />').addClass('span1');
		
		this.command = function() {
			var n = this.node(), opts;
			this.rte.selection.saveIERange();
			if (n) {
				var opts = {
					
					submit : function(e, d) { e.stopPropagation(); e.preventDefault(); d.close(); self.set();  },
					dialog : {
						title : this.rte.i18n('Style'),
						width : 450,
						resizable : true,
						modal : true
					}
				}
				this.cssStyle.val($(n).attr('style'));
				this.cssClass.val($(n).attr('class'));
				this.elementID.val($(n).attr('id'));
				var d = new elDialogForm(opts);
				d.append([this.rte.i18n('Css style'), this.cssStyle],  null, true)
				d.append([this.rte.i18n('Css class'), this.cssClass],  null, true)
				d.append([this.rte.i18n('ID'),        this.elementID], null, true)
				d.open();
				setTimeout(function() { self.cssStyle.focus() }, 20)
			}
		}
		
		this.set = function() {
			var n = this.node();
			this.rte.selection.restoreIERange();
			if (n) {
				$(n).attr('style', this.cssStyle.val());
				$(n).attr('class', this.cssClass.val());
				$(n).attr('id',    this.elementID.val());
				this.rte.ui.update();
			}
		}
		
		this.node = function() {
			var n = this.rte.selection.getNode();
			if (n.nodeType == 3) {
				n = n.parentNode;
			}
			return n.nodeType == 1 && n.nodeName != 'BODY' ? n : null;
		}
		
		this.update = function() {
			this.domElem.toggleClass('disabled', this.node()?false:true);
		}
		
	}
	
})(jQuery);

(function($) {
	/**
	 * @class button - right to left direction (not work yet with text nodes in body)
	 *
	 * @param  elRTE  rte   объект-редактор
	 * @param  String name  название кнопки 
	 *
	 * @author:    Dmitry Levashov (dio) dio@std42.ru
	 * Copyright: Studio 42, http://www.std42.ru
	 **/
	elRTE.prototype.ui.prototype.buttons.rtl = function(rte, name)  {
		this.constructor.prototype.constructor.call(this, rte, name);
		var self = this;
	
		this.command = function() {
			var n = this.rte.selection.getNode(), self = this;
			if ($(n).attr('dir') == 'rtl' || $(n).parents('[dir="rtl"]').length || $(n).find('[dir="rtl"]').length) {
				$(n).removeAttr('dir');
				$(n).parents('[dir="rtl"]').removeAttr('dir');
				$(n).find('[dir="rtl"]').removeAttr('dir');
			} else {
				if (this.rte.dom.is(n, 'textNodes') && this.rte.dom.is(n, 'block')) {
					$(n).attr('dir', 'rtl');
				} else {
					$.each(this.rte.dom.parents(n, 'textNodes'), function(i, n) {
						if (self.rte.dom.is(n, 'block')) {
							$(n).attr('dir', 'rtl');
							return false;
						}
					});
				}
			}
			this.rte.ui.update();
		}

		this.update = function() {
			var n = this.rte.selection.getNode();
			this.domElem.removeClass('disabled');
			if ($(n).attr('dir') == 'rtl' || $(n).parents('[dir="rtl"]').length || $(n).find('[dir="rtl"]').length) {
				this.domElem.addClass('active');
			} else {
				this.domElem.removeClass('active');
			}
		}
	}
	
	/**
	 * @class button - left to right direction (not work yet with text nodes in body)
	 *
	 * @param  elRTE  rte   объект-редактор
	 * @param  String name  название кнопки 
	 *
	 * @author:    Dmitry Levashov (dio) dio@std42.ru
	 * Copyright: Studio 42, http://www.std42.ru
	 **/
	elRTE.prototype.ui.prototype.buttons.ltr = function(rte, name)  {
		this.constructor.prototype.constructor.call(this, rte, name);
		var self = this;
	
		this.command = function() {
			var n = this.rte.selection.getNode(), self = this;
			if ($(n).attr('dir') == 'ltr' || $(n).parents('[dir="ltr"]').length || $(n).find('[dir="ltr"]').length) {
				$(n).removeAttr('dir');
				$(n).parents('[dir="ltr"]').removeAttr('dir');
				$(n).find('[dir="ltr"]').removeAttr('dir');
			} else {
				if (this.rte.dom.is(n, 'textNodes') && this.rte.dom.is(n, 'block')) {
					$(n).attr('dir', 'ltr');
				} else {
					$.each(this.rte.dom.parents(n, 'textNodes'), function(i, n) {
						if (self.rte.dom.is(n, 'block')) {
							$(n).attr('dir', 'ltr');
							return false;
						}
					});
				}
			}
			this.rte.ui.update();
		}

		this.update = function() {
			var n = this.rte.selection.getNode();
			this.domElem.removeClass('disabled');
			if ($(n).attr('dir') == 'ltr' || $(n).parents('[dir="ltr"]').length || $(n).find('[dir="ltr"]').length) {
				this.domElem.addClass('active');
			} else {
				this.domElem.removeClass('active');
			}
		}
	}
	
})(jQuery);/**
 * @class кнопка - DIV
 * Если выделение схлопнуто и находится внутри div'a - он удаляется
 * Новые div'ы создаются только из несхлопнутого выделения
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 * 
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.div = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var n, nodes;
		this.rte.history.add();

		if (this.rte.selection.collapsed()) {
			n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^DIV$/);
			if (n) {
				$(n).replaceWith($(n).html());
			}
		} else {
			nodes = this.rte.selection.selected({wrap : 'all', tag : 'div'});
			nodes.length && this.rte.selection.select(nodes[0], nodes[nodes.length-1]);
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		if (this.rte.selection.collapsed()) {
			if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^DIV$/)) {
				this.domElem.removeClass('disabled').addClass('active');
			} else {
				this.domElem.addClass('disabled active');
			}
		} else {
			this.domElem.removeClass('disabled active');
		}
	}
}
})(jQuery);
/**
 * @class кнопка - Включение/выключение показа структуры документа
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 * 
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.docstructure = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		this.domElem.toggleClass('active');
		$(this.rte.doc.body).toggleClass('el-rte-structure');
	}
	this.command();
	
	this.update = function() {	
		this.domElem.removeClass('disabled');
	}
}
})(jQuery);
/**
 * @class button - open elfinder window (not needed for image or link buttons).Used in ELDORADO.CMS for easy file manipulations.
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.elfinder = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this,
		rte = this.rte;
	this.command = function() {
		if (self.rte.options.fmAllow && typeof(self.rte.options.fmOpen) == 'function') {
			self.rte.options.fmOpen( function(url) { 
				var name = decodeURIComponent(url.split('/').pop().replace(/\+/g, " "));
				
				if (rte.selection.collapsed()) {
					rte.selection.insertHtml('<a href="'+url+'" >'+name+'</a>');
				} else {
					rte.doc.execCommand('createLink', false, url);
				}
				
			} );
		}
	}
	
	this.update = function() {
		if (self.rte.options.fmAllow && typeof(self.rte.options.fmOpen) == 'function') {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

})(jQuery);
(function($) {
	
	elRTE.prototype.ui.prototype.buttons.flash = function(rte, name) {
		this.constructor.prototype.constructor.call(this, rte, name);
		var self = this;
		this.swf = null;
		this.placeholder = null;
		this.src = {
			url    : $('<input type="text" name="url" />').css('width', '99%').addClass('span1'),
			type   : $('<select name="type"/>').addClass('span1').css('font-size','12px')
						.append('<option value="application/x-shockwave-flash">Flash</option>')
						.append('<option value="video/quicktime">Quicktime movie</option>')
						.append('<option value="application/x-mplayer2">Windows media</option>'),
			width  : $('<input type="text" />').attr('size', 5).css('text-align', 'right').addClass('span1'),
			height : $('<input type="text" />').attr('size', 5).css('text-align', 'right').addClass('span1'),
			wmode  : $('<select />').addClass('span1').css('font-size','12px')
						.append($('<option />').val('').text(this.rte.i18n('Not set', 'dialogs')))
						.append($('<option />').val('transparent').text(this.rte.i18n('Transparent'))),
			align  : $('<select />').css('font-size','12px')
						.append($('<option />').val('').text(this.rte.i18n('Not set', 'dialogs')))
						.append($('<option />').val('left'       ).text(this.rte.i18n('Left')))
						.append($('<option />').val('right'      ).text(this.rte.i18n('Right')))
						.append($('<option />').val('top'        ).text(this.rte.i18n('Top')))
						.append($('<option />').val('text-top'   ).text(this.rte.i18n('Text top')))
						.append($('<option />').val('middle'     ).text(this.rte.i18n('middle')))
						.append($('<option />').val('baseline'   ).text(this.rte.i18n('Baseline')))
						.append($('<option />').val('bottom'     ).text(this.rte.i18n('Bottom')))
						.append($('<option />').val('text-bottom').text(this.rte.i18n('Text bottom'))),
			margin : $('<div />').addClass('span1')
		}
		
		this.command = function() {

			var n = this.rte.selection.getEnd(), opts, url='', w='', h='', f, a, d, mid, o, wm;
			this.rte.selection.saveIERange();
			this.src.margin.elPaddingInput({ type : 'margin' });
			this.placeholder = null;
			this.swf = null;
			if ($(n).hasClass('elrte-media') && (mid = $(n).attr('rel')) &&  this.rte.filter.scripts[mid]) {
				this.placeholder = $(n);
				o = this.rte.filter.scripts[mid];
				url = '';
				if (o.embed && o.embed.src) {
					url = o.embed.src;
				}
				if (o.params && o.params.length) {
					l = o.params.length;
					while (l--) {
						if (o.params[l].name == 'src' || o.params[l].name == 'movie') {
							url =  o.params[l].value;
						}
					}
				}
				
				if (o.embed) {
					w = o.embed.width||parseInt(o.embed.style.width)||'';
					h = o.embed.height||parseInt(o.embed.style.height)||'';
					wm = o.embed.wmode||'';
				} else if (o.obj) {
					w = o.obj.width||parseInt(o.obj.style.width)||'';
					h = o.obj.height||parseInt(o.obj.style.height)||'';
					wm = o.obj.wmode||'';
				}
				
				if (o.obj) {
					f = o.obj.style['float']||'';
					a = o.obj.style['vertical-align']||'';
				} else if (o.embed) {
					f = o.embed.style['float']||'';
					a = o.embed.style['vertical-align']||'';
				}
				this.src.margin.val(n);
				this.src.type.val(o.embed ? o.embed.type : '');
			}
			if ($(n).hasClass('elrte-swf-placeholder')) {
				this.placeholder = $(n);
				url = $(n).attr('rel');
				w = parseInt($(n).css('width'))||'';
				h = parseInt($(n).css('height'))||'';
				f = $(n).css('float');
				a = $(n).css('vertical-align');
				this.src.margin.val(n);
				this.src.wmode.val($(n).attr('wmode'));
			} 
			this.src.url.val(url);
			this.src.width.val(w);
			this.src.height.val(h);
			this.src.align.val(f||a);
			this.src.wmode.val(wm);
			

			

			var opts = {
				rtl : this.rte.rtl,
				submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
				dialog : {
					width    : 580,
					position : 'top',
					title    : this.rte.i18n('Flash')
				}
			}
			var d = new elDialogForm(opts);
			
			if (this.rte.options.fmAllow && this.rte.options.fmOpen) {
				var src = $('<span />').append(this.src.url.css('width', '85%'))
						.append(
							$('<span />').addClass('ui-state-default ui-corner-all')
								.css({'float' : 'right', 'margin-right' : '3px'})
								.attr('title', self.rte.i18n('Open file manger'))
								.append($('<span />').addClass('ui-icon ui-icon-folder-open'))
									.click( function() {
										self.rte.options.fmOpen( function(url) { self.src.url.val(url).change(); } );
									})
									.hover(function() {$(this).addClass('ui-state-hover')}, function() { $(this).removeClass('ui-state-hover')})
							);
			} else {
				var src = this.src.url;
			}
			
			d.append([this.rte.i18n('URL'), src], null, true);
			d.append([this.rte.i18n('Type'), this.src.type], null, true);
			d.append([this.rte.i18n('Size'), $('<span />').append(this.src.width).append(' x ').append(this.src.height).append(' px')], null, true)
			d.append([this.rte.i18n('Wmode'), this.src.wmode], null, true);
			d.append([this.rte.i18n('Alignment'), this.src.align], null, true);
			d.append([this.rte.i18n('Margins'), this.src.margin], null, true);
			
			
			
			d.open();
			// setTimeout( function() {self.src.url.focus()}, 100)
			
			
			var fs = $('<fieldset />').append($('<legend style="width:60px;text-align:center;" />').text(this.rte.i18n('Preview')))
			d.append(fs, 'main');
			var frame = document.createElement('iframe');
			$(frame).attr('src', '#').addClass('el-rte-preview').appendTo(fs);
			html = this.rte.options.doctype+'<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body style="padding:0;margin:0;font-size:11px">elRTE 是一個開放源碼 JavaScript 為基礎的所見即所得 HTML 編輯器。用 jQuery UI 開發所見即所得（WYSIWYG）編輯器，完整度剖高，乍看之下有 tinymce 的感覺。基於 jQuery 速度跟大小與發展上應有他的優勢在。elRTE 還另外開發了一套 elFinder 有綁在編輯器裡面的檔案管理功能，也很強大完整。</body></html>';
			frame.contentWindow.document.open();
			frame.contentWindow.document.write(html);
			frame.contentWindow.document.close();
			this.frame = frame.contentWindow.document;
			this.preview = $(frame.contentWindow.document.body);
			 				 
			this.src.type.change(function() {
				self.src.url.change();
			});

			this.src.width.change(function() {
				if (self.swf) {
					var w = parseInt($(this).val())||'';
					$(this).val(w);
					self.swf.css('width', w);
					self.swf.children('embed').css('width', w);
				} else {
					$(this).val('');
				}
			});

			this.src.height.change(function() {
				if (self.swf) {
					var h = parseInt($(this).val())||'';
					$(this).val(h);
					self.swf.css('height', h);
					self.swf.children('embed').css('height', h);
				} else {
					$(this).val('');
				}
			});
			
			this.src.wmode.change(function() {
				if (self.swf) {
					var wm = $(this).val();
					if (wm) {
						self.swf.attr('wmode', wm);
						self.swf.children('embed').attr('wmode', wm);
					} else {
						self.swf.removeAttr('wmode');
						self.swf.children('embed').removeAttr('wmode');
					}
				}
			});
			
			this.src.align.change(function() {
				var v = $(this).val(), f = v=='left' || v=='right';
				if (self.swf) {
					self.swf.css({
						'float' : f ? v : '',
						'vertical-align' : f ? '' : v
					});
				} else {
					$(this).val('');
				}
			});
			
			this.src.margin.change(function() {
				if (self.swf) {
					var m = self.src.margin.val();
					if (m.css) {
						self.swf.css('margin', m.css);
					} else {
						self.swf.css('margin-top', m.top);
						self.swf.css('margin-right', m.right);
						self.swf.css('margin-bottom', m.bottom);
						self.swf.css('margin-left', m.left);						
					}
				}
			});
			
			this.src.url.change(function() {
				var url = self.rte.utils.absoluteURL($(this).val()), i, swf;
				if (url) {
					i = self.rte.utils.mediaInfo(self.src.type.val());
					if (!i) {
						i = self.rte.util.mediaInfo('application/x-shockwave-flash');
					}
					swf = '<object classid="'+i.classid+'" codebase="'+i.codebase+'"><param name="src" value="'+url+'" /><embed quality="high" src="'+url+'" type="'+i.type+'"></object>';
					self.preview.children('object').remove().end().prepend(swf);
					self.swf = self.preview.children('object').eq(0);
				} else if (self.swf){
					self.swf.remove();
					self.swf = null;
				}
				self.src.width.trigger('change');
				self.src.height.trigger('change');
				self.src.align.trigger('change');

			}).trigger('change');
		};
		
		this.set = function() {
			self.swf = null
			var url = this.rte.utils.absoluteURL(this.src.url.val()),
				w = parseInt(this.src.width.val()) || '',
				h = parseInt(this.src.height.val()) || '',
				wm = this.src.wmode.val(),
				a = this.src.align.val(),
				f = a == 'left' || a == 'right' ? a : '',
				mid = this.placeholder ? this.placeholder.attr('rel') : '', o, _o, c, 
				m = this.src.margin.val(), margin;

			
			
			if (!url) {
				if (this.placeholder) {
					this.placeholder.remove();
					delete this.rte.filter.scripts[mid];
				}
			} else {
				i = self.rte.utils.mediaInfo(self.src.type.val());
				if (!i) {
					i = self.rte.util.mediaInfo('application/x-shockwave-flash');
				}
				c = this.rte.filter.videoHostRegExp.test(url) ? url.replace(this.rte.filter.videoHostRegExp, "$2") : i.type.replace(/^\w+\/(.+)/, "$1");

				o = {
					obj : {
						classid : i.classid[0],
						codebase : i.codebase,
						style : {}
					},
					params :[ { name : 'src', value : url } ],
					embed :{
						src : url,
						type : i.type,
						quality : 'high',
						wmode : wm,
						style : {}
					}
				};
				
				if (w) {
					o.obj.width = w;
					o.embed.width = w;
				}
				if (h) {
					o.obj.height = h;
					o.embed.height = h;
				}
				if (f) {
					o.obj.style['float'] = f;
				} else if (a) {
					o.obj.style['vertical-align'] = a;
				}
				
				if (m.css) {
					margin = { margin : m.css };
				} else {
					margin = {
						'margin-top' : m.top,
						'margin-right' : m.right,
						'margin-bottom' : m.bottom,
						'margin-left' : m.left
					};
				}
				
				o.obj.style = $.extend({}, o.obj.style, margin);
				
				if (this.placeholder && mid) {
					_o = this.rte.filter.scripts[mid]||{};

					o = $.extend(true, _o, o);
					delete o.obj.style.width;
					delete o.obj.style.height;
					delete o.embed.style.width;
					delete o.embed.style.height;
					this.rte.filter.scripts[mid] = o;
					this.placeholder.removeAttr('class');
				} else {
					var id = 'media'+Math.random().toString().substring(2);
					this.rte.filter.scripts[id] = o;
					this.placeholder = $(this.rte.dom.create('img')).attr('rel', id).attr('src', this.rte.filter.url+'pixel.gif');
					var ins = true;
				}
				this.placeholder.attr('title', this.rte.utils.encode(url)).attr('width', w||150).attr('height', h||100).addClass('elrte-protected elrte-media elrte-media-'+c).css(o.obj.style);
				if (f) {
					this.placeholder.css('float', f).css('vertical-align', '');
				} else if (a) {
					this.placeholder.css('float', '').css('vertical-align', a);
				} else {
					this.placeholder.css('float', '').css('vertical-align', '');
				}
				
				if (ins) {
					this.rte.window.focus();
					this.rte.selection.restoreIERange();
					this.rte.selection.insertNode(this.placeholder.get(0));
				}
			}
		}
		
		this.update = function() {
			this.domElem.removeClass('disabled');
			var n = this.rte.selection.getNode();
			this.domElem.toggleClass('active', n && n.nodeName == 'IMG' && $(n).hasClass('elrte-media'))
			
		}
		
		
	}
})(jQuery);/**
 * @class drop-down menu - font-family for selected text
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.fontname = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	var opts = {
		tpl      : '<span style="font-family:%val">%label</span>',
		select   : function(v) { self.set(v); },
		src      : {
			''                                              : this.rte.i18n('Font'),
			'andale mono,sans-serif'                        : 'Andale Mono',
			'arial,helvetica,sans-serif'                    : 'Arial',
			'arial black,gadget,sans-serif'                 : 'Arial Black',
			'book antiqua,palatino,sans-serif'              : 'Book Antiqua',
			'comic sans ms,cursive'                         : 'Comic Sans MS',
			'courier new,courier,monospace'                 : 'Courier New',
			'georgia,palatino,serif'                        : 'Georgia',
			'helvetica,sans-serif'                          : 'Helvetica',
			'impact,sans-serif'                             : 'Impact',
			'lucida console,monaco,monospace'               : 'Lucida console',
			'lucida sans unicode,lucida grande,sans-serif'  : 'Lucida grande',
			'tahoma,sans-serif'                             : 'Tahoma',
			'times new roman,times,serif'                   : 'Times New Roman',
			'trebuchet ms,lucida grande,verdana,sans-serif' : 'Trebuchet MS',
			'verdana,geneva,sans-serif'                     : 'Verdana'
		}
	}
	
	this.select = this.domElem.elSelect(opts);
	
	this.command = function() {
	}
	
	this.set = function(size) {
		this.rte.history.add();
		var nodes = this.rte.selection.selected({filter : 'textContainsNodes'});
		$.each(nodes, function() {
			$this = /^(THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(this.nodeName) ? $(this).find('td,th') : $(this);
			$(this).css('font-family', size).find('[style]').css('font-family', '');
		});
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled'); 
		var n = this.rte.selection.getNode();
		if (n.nodeType != 1) {
			n = n.parentNode;
		}
		var v = $(n).css('font-family');
		v = v ? v.toString().toLowerCase().replace(/,\s+/g, ',').replace(/'|"/g, '') : '';
		this.select.val(opts.src[v] ? v : '');
	}
}

})(jQuery);/**
 * @class drop-down menu - font size for selected text
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.fontsize = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	var opts = {
		labelTpl : '%label',
		tpl      : '<span style="font-size:%val;line-height:1.2em">%label</span>',
		select   : function(v) { self.set(v); },
		src      : {
			''         : this.rte.i18n('Font size'),
			'xx-small' : this.rte.i18n('Small (8pt)'), 
			'x-small'  : this.rte.i18n('Small (10px)'), 
			'small'    : this.rte.i18n('Small (12pt)'), 
			'medium'   : this.rte.i18n('Normal (14pt)'),
			'large'    : this.rte.i18n('Large (18pt)'),
			'x-large'  : this.rte.i18n('Large (24pt)'),
			'xx-large' : this.rte.i18n('Large (36pt)')
		}
	}
	
	this.select = this.domElem.elSelect(opts);
	
	this.command = function() {
	}
	
	this.set = function(size) {
		this.rte.history.add();
		var nodes = this.rte.selection.selected({filter : 'textContainsNodes'});
		$.each(nodes, function() {
			$this = /^(THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(this.nodeName) ? $(this).find('td,th') : $(this);
			$this.css('font-size', size).find("[style]").css('font-size', '');
		});
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.selection.getNode();
		this.select.val((m = this.rte.dom.attr(n, 'style').match(/font-size:\s*([^;]+)/i)) ? m[1] : '');
	}
}

})(jQuery);/**
 * @class color pallete for text color and background
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.forecolor = function(rte, name) {
	var self = this;
	this.constructor.prototype.constructor.call(this, rte, name);
	var opts = {
		'class' : '',
		palettePosition : 'outer',
		color   : this.defaultColor,
		update  : function(c) { self.indicator.css('background-color', c); },
		change  : function(c) { self.set(c) }
	}
	
	this.defaultColor = this.name == 'forecolor' ? '#000000' : '#ffffff';
	this.picker       = this.domElem.elColorPicker(opts);
	this.indicator    = $('<div />').addClass('color-indicator').prependTo(this.domElem);
	
	this.command = function() {
	}
	
	this.set = function(c) {
		if (!this.rte.selection.collapsed()) {
			this.rte.history.add();
			var nodes = this.rte.selection.selected({collapse : false, wrap : 'text'}),
				css   = this.name == 'forecolor' ? 'color' : 'background-color';			
			$.each(nodes, function() {
				if (/^(THEAD|TBODY|TFOOT|TR)$/.test(this.nodeName)) {
					$(this).find('td,th').each(function() {
						$(this).css(css, c).find('*').css(css, '');
					})
				} else {
					$(this).css(css, c).find('*').css(css, '');
				}
			});
			this.rte.ui.update(true);
		}
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.selection.getNode();
		this.picker.val(this.rte.utils.rgb2hex($(n.nodeType != 1 ? n.parentNode : n).css(this.name == 'forecolor' ? 'color' : 'background-color'))||this.defaultColor)
	}
}

elRTE.prototype.ui.prototype.buttons.hilitecolor = elRTE.prototype.ui.prototype.buttons.forecolor;

})(jQuery);/**
 * @class drop-down menu - formatting text block
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.formatblock = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);

	var cmd = this.rte.browser.msie 
		? function(v) { self.val = v; self.constructor.prototype.command.call(self); }
		: function(v) { self.ieCommand(v); } 
	var self = this;
	var opts = {
		labelTpl : '%label',
		tpls     : {'' : '%label'},
		select   : function(v) { self.formatBlock(v); },
		src      : {
			'span'    : this.rte.i18n('Format'),
			'h1'      : this.rte.i18n('Heading 1'),
			'h2'      : this.rte.i18n('Heading 2'),
			'h3'      : this.rte.i18n('Heading 3'),
			'h4'      : this.rte.i18n('Heading 4'),
			'h5'      : this.rte.i18n('Heading 5'),
			'h6'      : this.rte.i18n('Heading 6'),
			'p'       : this.rte.i18n('Paragraph'),
			'address' : this.rte.i18n('Address'),
			'pre'     : this.rte.i18n('Preformatted'),
			'div'     : this.rte.i18n('Normal (DIV)')
		}
	}

	this.select = this.domElem.elSelect(opts);
	
	this.command = function() {

	}
	
	this.formatBlock = function(v) {

		function format(n, tag) {
			
			function replaceChilds(p) {
				$(p).find('h1,h2,h3,h4,h5,h6,p,address,pre').each(function() {
					$(this).replaceWith($(this).html());
				});
				return p;
			}
			
			if (/^(LI|DT|DD|TD|TH|CAPTION)$/.test(n.nodeName)) {
				!self.rte.dom.isEmpty(n) && self.rte.dom.wrapContents(replaceChilds(n), tag);
			} else if (/^(UL|OL|DL|TABLE)$/.test(n.nodeName)) {
				self.rte.dom.wrap(n, tag);
			} else {
				!self.rte.dom.isEmpty(n) && $(replaceChilds(n)).replaceWith( $(self.rte.dom.create(tag)).html($(n).html()));
			}
			
		}
		this.rte.history.add();

		var tag = v.toUpperCase(),
			i, n, $n,
			c = this.rte.selection.collapsed(),
			bm = this.rte.selection.getBookmark(),
			nodes = this.rte.selection.selected({
				collapsed : true,
				blocks    : true,
				filter    : 'textContainsNodes',
				wrap      : 'inline',
				tag       : 'span'
			})
			l = nodes.length,
			s = $(nodes[0]).prev(),
			e = $(nodes[nodes.length-1]).next();

		while (l--) {
			n = nodes[l];
			$n = $(n);
			if (tag == 'DIV' || tag == 'SPAN') {
				if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)) {
					$n.replaceWith($(this.rte.dom.create('div')).html($n.html()||''));
				}
			} else {
				if (/^(THEAD|TBODY|TFOOT|TR)$/.test(n.nodeName)) {
					$n.find('td,th').each(function() { format(this, tag); });
				} else if (n.nodeName != tag) {
					format(n, tag);
				}
			}
		}

		this.rte.selection.moveToBookmark(bm);

		this.rte.ui.update(true);
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(H[1-6]|P|ADDRESS|PRE)$/);
		this.select.val(n ? n.nodeName.toLowerCase() : 'span');
	}
}
})(jQuery);
/**
 * @class button - switch to fullscreen mode and back
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.fullscreen = function(rte, name) {
	var self     = this;
	this.constructor.prototype.constructor.call(this, rte, name);
	this.active  = true;
	this.editor = rte.editor;
	this.wz = rte.workzone;
	this.height  = 0;
	this.delta = 0;
	this._class = 'el-fullscreen';
	
	setTimeout(function() {
		self.height  = self.wz.height();
		self.delta   = self.editor.outerHeight()-self.height;
	}, 50);
	
	
	/**
	 * Update editor height on window resize in fullscreen view
	 *
	 **/
	function resize() {
		self.wz.height($(window).height()-self.delta);
		self.rte.updateHeight();
	}
	
	this.command = function() {
		var w = $(window),
			e = this.editor,
			p = e.parents().filter(function(i, n) { return  !/^(html|body)$/i.test(n.nodeName) && $(n).css('position') == 'relative'; }),
			wz = this.wz,
			c = this._class,
			f = e.hasClass(c),
			rte = this.rte,
			s = this.rte.selection,
			m = $.browser.mozilla,
			b, h;

		function save() {
			if (m) {
				b = s.getBookmark();
			}
		}
		
		function restore() {
			if (m) {
				self.wz.children().toggle();
				self.rte.source.focus();
				self.wz.children().toggle();
				s.moveToBookmark(b);
			}
		}

		save();
		p.css('position', f ? 'relative' : 'static');	
		
		if (f) {
			e.removeClass(c);
			wz.height(this.height);
			w.unbind('resize', resize);
			this.domElem.removeClass('active');
		} else {
			e.addClass(c).removeAttr('style');
			wz.height(w.height() - this.delta).css('width', '100%');
			w.bind('resize', resize);
			this.domElem.addClass('active');
		}
		rte.updateHeight();	
		rte.resizable(f);
		restore();
		
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
	}
}
})(jQuery);
/**
 * @class button - horizontal rule (open dialog window)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.horizontalrule = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.src = {
		width   : $('<input type="text" />').attr({'name' : 'width', 'size' : 4}).css('text-align', 'right').css('width','30px'),
		wunit   : $('<select />').attr('name', 'wunit').addClass('span1').css('font-size', '12px')
					.append($('<option />').val('%').text('%'))
					.append($('<option />').val('px').text('px'))
					.val('%'),
		height  : $('<input type="text" />').attr({'name' : 'height', 'size' : 4}).css('text-align', 'right').css('width','30px'),
		bg      : $('<div />').addClass('span1'),
		border  : $('<div />').css('width','20px'),
		'class' : $('<input type="text" />').addClass('span2'),
		style   : $('<input type="text" />').addClass('span2')
	}
	
	this.command = function() {
		this.src.bg.elColorPicker({palettePosition : 'outer', 'class' : 'el-colorpicker ui-icon ui-icon-pencil'});
		
		var n   = this.rte.selection.getEnd();
		this.hr = n.nodeName == 'HR' ? $(n) : $(rte.doc.createElement('hr')).css({width : '100%', height : '1px'});
		this.src.border.elBorderSelect({styleHeight : 73, value : this.hr});
		
		var _w  = this.hr.css('width') || this.hr.attr('width');
		this.src.width.val(parseInt(_w) || 100);
		this.src.wunit.val(_w.indexOf('px') != -1 ? 'px' : '%');
		
		this.src.height.val( this.rte.utils.toPixels(this.hr.css('height') || this.hr.attr('height')) || 1) ;
		
		this.src.bg.val(this.rte.utils.color2Hex(this.hr.css('background-color')));
		this.src['class'].val(this.rte.dom.attr(this.hr, 'class'));
		this.src.style.val(this.rte.dom.attr(this.hr, 'style'));
		
		var opts = {
			rtl : this.rte.rtl,
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			dialog : {
				title : this.rte.i18n('Horizontal rule')
			}
		}

		var d = new elDialogForm(opts);
		d.append([this.rte.i18n('Width'),          $('<span />').append(this.src.width).append(this.src.wunit) ], null, true)
			.append([this.rte.i18n('Height'),      $('<span />').append(this.src.height).append(' px')], null, true)
			.append([this.rte.i18n('Border'),      this.src.border], null, true)
			.append([this.rte.i18n('Background'),  this.src.bg], null, true)
			.append([this.rte.i18n('Css class'),   this.src['class']], null, true)
			.append([this.rte.i18n('Css style'),   this.src.style], null, true)
			.open();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		if (this.rte.selection.getEnd().nodeName == 'HR') {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	}
	
	this.set = function() {
		this.rte.history.add();
		!this.hr.parentNode && this.rte.selection.insertNode(this.hr.get(0));
		var attr = {
			noshade : true,
			style   : this.src.style.val()
		}
		var b = this.src.border.val();
		var css = {
			width  : (parseInt(this.src.width.val()) || 100)+this.src.wunit.val(),
			height : parseInt(this.src.height.val()) || 1,
			'background-color' : this.src.bg.val(),
			border : b.width && b.style ? b.width+' '+b.style+' '+b.color : ''
		}

		this.hr.removeAttr('class')
			.removeAttr('style')
			.removeAttr('width')
			.removeAttr('height')
			.removeAttr('align')
			.attr(attr)
			.css(css);
		
		if (this.src['class'].val()) {
			this.hr.attr('class', this.src['class'].val());	
		}
		this.rte.ui.update()
	}
	
}
})(jQuery);
/**
 * @class button - insert/edit image (open dialog window)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.image = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this,
		rte  = self.rte,
		proportion = 0,
		width = 0,
		height = 0,
		bookmarks = null,
		reset = function(nosrc) {
			$.each(self.src, function(i, elements) {
				$.each(elements, function(n, el) {
					if (n == 'src' && nosrc) {
						return;
					}
					el.val('');
				});
			});
		},
		values = function(img) {
			$.each(self.src, function(i, elements) {
				$.each(elements, function(n, el) {
					var val, w, c, s, border;
					
					if (n == 'width') {
						val = img.width();
					} else if (n == 'height') {
						val = img.height();
					} else if (n == 'border') {
						val = '';
						border = img.css('border') || rte.utils.parseStyle(img.attr('style')).border || '';

						if (border) {
							w = border.match(/(\d(px|em|%))/);
							c = border.match(/(#[a-z0-9]+)/);
							val = {
								width : w ? w[1] : border,
								style : border,
								color : rte.utils.color2Hex(c ? c[1] : border)
							}
						} 
					} else if (n == 'margin') {
						val = img;
					} else if (n == 'align') { 
						val = img.css('float');

						if (val != 'left' && val != 'right') {
							val = img.css('vertical-align');
						}
					 }else {
						val = img.attr(n)||'';
					}
					
					if (i == 'events') {
						val = rte.utils.trimEventCallback(val);
					}

					el.val(val);
				});
			});
		},
		preview = function() {
			var src = self.src.main.src.val();
			
			reset(true);
			
			if (!src) {
				self.preview.children('img').remove();
				self.prevImg = null;
			} else {
				if (self.prevImg) {
					self.prevImg
						.removeAttr('src')
						.removeAttr('style')
						.removeAttr('class')
						.removeAttr('id')
						.removeAttr('title')
						.removeAttr('alt')
						.removeAttr('longdesc');
						
					$.each(self.src.events, function(name, input) {
						self.prevImg.removeAttr(name);
					});
				} else {
					self.prevImg = $('<img/>').prependTo(self.preview);
				}
				self.prevImg.load(function() {
					self.prevImg.unbind('load');
					setTimeout(function() {
						width      = self.prevImg.width();
						height     = self.prevImg.height();
						proportion = (width/height).toFixed(2);
						self.src.main.width.val(width);
						self.src.main.height.val(height);
						
					}, 100);
				})
				.attr('src', src);
			}
			
		},
		size = function(e) {
			var w = parseInt(self.src.main.width.val())||0,
				h = parseInt(self.src.main.height.val())||0;
				
			if (self.prevImg) {
				if (w && h) {
					if (e.target === self.src.main.width[0]) {
						h = parseInt(w/proportion);
					} else {
						w = parseInt(h*proportion);
					}
				} else {
					w = width;
					h = height;
				}
				self.src.main.height.val(h);
				self.src.main.width.val(w);
				self.prevImg.width(w).height(h);
				self.src.adv.style.val(self.prevImg.attr('style'));
			}
		}
		;
	
	this.img     = null;
	this.prevImg = null;
	this.preview = $('<div class="elrte-image-preview"/>').text('elRTE 是一個開放源碼 JavaScript 為基礎的所見即所得 HTML 編輯器。用 jQuery UI 開發所見即所得（WYSIWYG）編輯器，完整度剖高，乍看之下有 tinymce 的感覺。基於 jQuery 速度跟大小與發展上應有他的優勢在。elRTE 還另外開發了一套 elFinder 有綁在編輯器裡面的檔案管理功能，也很強大完整。');
	
	this.init = function() {	
		this.labels = {
			main   : 'Properies',
			link   : 'Link',
			adv    : 'Advanced',
			events : 'Events',
			id       : 'ID',
			'class'  : 'Css class',
			style    : 'Css style',
			longdesc : 'Detail description URL',
			href    : 'URL',
			target  : 'Open in',
			title   : 'Title'
		}
		
		this.src = {
			main : {
				src    : $('<input type="text" />').addClass('span1').change(preview),
				title  : $('<input type="text" />').addClass('span4'),
				alt    : $('<input type="text" />').addClass('span4'),
				width  : $('<input type="text" />').attr('size', 5).css('text-align', 'right').addClass('span1').change(size),
				height : $('<input type="text" />').attr('size', 5).css('text-align', 'right').addClass('span1').change(size),
				margin : $('<div />').addClass('span1').elPaddingInput({
					type : 'margin', 
					change : function() {
						var margin = self.src.main.margin.val();
					
						if (self.prevImg) {
							if (margin.css) {
								self.prevImg.css('margin', margin.css)
							} else {
								self.prevImg.css({
									'margin-left'   : margin.left,
									'margin-top'    : margin.top,
									'margin-right'  : margin.right,
									'margin-bottom' : margin.bottom
								});
							}
						}
					} 
				}).addClass('span1'),
				align  : $('<select />').addClass('span2').css('font-size','12px')
							.append($('<option />').val('').text(this.rte.i18n('Not set', 'dialogs')))
							.append($('<option />').val('left'       ).text(this.rte.i18n('Left')))
							.append($('<option />').val('right'      ).text(this.rte.i18n('Right')))
							.append($('<option />').val('top'        ).text(this.rte.i18n('Top')))
							.append($('<option />').val('text-top'   ).text(this.rte.i18n('Text top')))
							.append($('<option />').val('middle'     ).text(this.rte.i18n('middle')))
							.append($('<option />').val('baseline'   ).text(this.rte.i18n('Baseline')))
							.append($('<option />').val('bottom'     ).text(this.rte.i18n('Bottom')))
							.append($('<option />').val('text-bottom').text(this.rte.i18n('Text bottom')))
							.change(function() {
								var val = $(this).val(),
									css = {
										'float' : '',
										'vertical-align' : ''
									};
								if (self.prevImg) {
									if (val == 'left' || val == 'right') {
										css['float'] = val;
										css['vertical-align'] = '';
									} else if (val) {
										css['float'] = '';
										css['vertical-align'] = val;
									} 
									self.prevImg.css(css);
								}
							})
						,
				border : $('<div />').elBorderSelect({
					name : 'border',
					change : function() {
						var border = self.src.main.border.val();
						if (self.prevImg) {
							self.prevImg.css('border', border.width ? border.width+' '+border.style+' '+border.color : '');
						}
					}
				})
			},

			adv : {},
			events : {}
		}
		
		$.each(['id', 'class', 'style', 'longdesc'], function(i, name) {
			self.src.adv[name] = $('<input type="text" />').addClass('span4');
		});
		
		this.src.adv['class'].change(function() {
			if (self.prevImg) {
				self.prevImg.attr('class', $(this).val());
			}
		});
		
		this.src.adv.style.change(function() {
			if (self.prevImg) {
				self.prevImg.attr('style', $(this).val());
				values(self.prevImg);
			}
		});
		
		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />').addClass('span4');
		});
	}
	
	this.command = function() {
		!this.src && this.init();
		
		var img, 
			opts = {
				rtl : rte.rtl,
				submit : function(e, d) { 
					e.stopPropagation(); 
					e.preventDefault(); 
					self.set(); 

					dialog.close(); 
				},
				close : function() {

					bookmarks && rte.selection.moveToBookmark(bookmarks)
				},
				dialog : {
					autoOpen  : false,
					width     : 500,
					position  : 'top',
					title     : rte.i18n('Image'),
					resizable : true,
					open      : function() {
						$.fn.resizable && $(this).parents('.ui-dialog:first').resizable('option', 'alsoResize', '.elrte-image-preview');
					}
				}
			},
			dialog = new elDialogForm(opts),
			fm = !!rte.options.fmOpen,
			src = fm
				? $('<div class="elrte-image-src-fm"><span class="ui-state-default ui-corner-all"><span class="ui-icon ui-icon-folder-open"/></span></div>')
					.append(this.src.main.src.css('width', '87%'))
				: this.src.main.src;
			
			;
		
		reset();
		this.preview.children('img').remove();
		this.prevImg = null;
		img = rte.selection.getEnd();
		
		this.img = img.nodeName == 'IMG' && !$(img).is('.elrte-protected')
			? $(img)
			: $('<img/>');
		
		bookmarks = rte.selection.getBookmark();

		if (fm) {
			src.children('.ui-state-default')
				.click( function() {
					rte.options.fmOpen( function(url) { self.src.main.src.val(url).change() } );
				})
				.hover(function() {
					$(this).toggleClass('ui-state-hover');
				});
		}
		
		dialog.tab('main', this.rte.i18n('Properies'))
			.append([this.rte.i18n('Image URL'), src],                 'main', true)
			.append([this.rte.i18n('Title'),     this.src.main.title], 'main', true)
			.append([this.rte.i18n('Alt text'),  this.src.main.alt],   'main', true)
			.append([this.rte.i18n('Size'), $('<span />').append(this.src.main.width).append(' x ').append(this.src.main.height).append(' px')], 'main', true)
			.append([this.rte.i18n('Alignment'), this.src.main.align],  'main', true)
			.append([this.rte.i18n('Margins'),   this.src.main.margin], 'main', true)
			.append([this.rte.i18n('Border'),    this.src.main.border], 'main', true)
		
		dialog.append($('<fieldset><legend style="width:60px;text-align:center;">'+this.rte.i18n('Preview')+'</legend></fieldset>').append(this.preview), 'main');
		
		
		
		$.each(this.src, function(tabname, elements) {
		
			if (tabname == 'main') {
				return;
			}
			dialog.tab(tabname, rte.i18n(self.labels[tabname]));
			
			$.each(elements, function(name, el) {
				self.src[tabname][name].val(tabname == 'events' ? rte.utils.trimEventCallback(self.img.attr(name)) : self.img.attr(name)||'');
				dialog.append([rte.i18n(self.labels[name] || name), self.src[tabname][name]], tabname, true);
			});
		});
		
		dialog.open();		
		
		if (this.img.attr('src')) {
			values(this.img);
			this.prevImg = this.img.clone().prependTo(this.preview);
			proportion   = (this.img.width()/this.img.height()).toFixed(2);
			width        = parseInt(this.img.width());
			height       = parseInt(this.img.height());
		}
	}
		
	this.set = function() {
		var src = this.src.main.src.val(),
			link;
		
		this.rte.history.add();
		bookmarks && rte.selection.moveToBookmark(bookmarks);
		
		if (!src) {
			link = rte.dom.selfOrParentLink(this.img[0]);
			link && link.remove();
			return this.img.remove();
		}
		
		!this.img[0].parentNode && (this.img = $(this.rte.doc.createElement('img')));
		
		this.img.attr('src', src)
			.attr('style', this.src.adv.style.val());
		
		$.each(this.src, function(i, elements) {
			$.each(elements, function(name, el) {
				var val = el.val(), style;
				
				switch (name) {
					case 'width':
						self.img.css('width', val);
						break;
					case 'height':
						self.img.css('height', val);
						break;
					case 'align':
						self.img.css(val == 'left' || val == 'right' ? 'float' : 'vertical-align', val);
						break;
					case 'margin':
						if (val.css) {
							self.img.css('margin', val.css);
						} else {
							self.img.css({
								'margin-left'   : val.left,
								'margin-top'    : val.top,
								'margin-right'  : val.right,
								'margin-bottom' : val.bottom
							});
						}
						break;
					case 'border':
						if (!val.width) {
							val = '';
						} else {
							val = 'border:'+val.css+';'+$.trim((self.img.attr('style')||'').replace(/border\-[^;]+;?/ig, ''));
							name = 'style';
							self.img.attr('style', val)
							return;
						}

						break;
					case 'src':
					case 'style':
						return;
					default:
						val ? self.img.attr(name, val) : self.img.removeAttr(name);
				}
			});
		});
		
		!this.img[0].parentNode && rte.selection.insertNode(this.img[0]);
		this.rte.ui.update();
	}

	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.selection.getEnd(),
			$n = $(n);
		if (n.nodeName == 'IMG' && !$n.hasClass('elrte-protected')) {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	}
	
}
})(jQuery);
/**
 * @class Увеличение отступа
 * списки - если выделен один элемент - увеличивается вложенность списка, в остальных случаях - padding у родительского ul|ol
 * Если таблица выделена полностью - ей добавляется margin, если частично - увеличивается padding для ячеек
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.indent = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	this.command = function() {
		this.rte.history.add();
		var nodes = this.rte.selection.selected({collapsed : true, blocks : true, wrap : 'inline', tag : 'p'});

		function indent(n) {
			var css = /(IMG|HR|TABLE|EMBED|OBJECT)/.test(n.nodeName) ? 'margin-left' : 'padding-left';
			var val = self.rte.dom.attr(n, 'style').indexOf(css) != -1 ? parseInt($(n).css(css))||0 : 0;
			$(n).css(css, val+40+'px');
		}
		
		for (var i=0; i < nodes.length; i++) {
			if (/^(TABLE|THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(nodes[i].nodeName)) {
				$(nodes[i]).find('td,th').each(function() {
					indent(this);
				});
			} else if (/^LI$/.test(nodes[i].nodeName)) {
				var n = $(nodes[i]);
				$(this.rte.dom.create(nodes[i].parentNode.nodeName))
					.append($(this.rte.dom.create('li')).html(n.html()||'')).appendTo(n.html('&nbsp;'));
			} else {
				indent(nodes[i]);
			}
		};
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
	}

}
})(jQuery);
/**
 * @class button - justify text
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.justifyleft = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.align = this.name == 'justifyfull' ? 'justify' : this.name.replace('justify', '');

	this.command = function() {
		var s = this.rte.selection.selected({collapsed:true, blocks : true, tag : 'div'}),
			l = s.length;
		l && this.rte.history.add();
		while (l--) {
			this.rte.dom.filter(s[l], 'textNodes') && $(s[l]).css('text-align', this.align);
		}
		this.rte.ui.update();
	}
	
	this.update = function() {
		var s = this.rte.selection.getNode(), 
			n = s.nodeName == 'BODY' ? s : this.rte.dom.selfOrParent(s, 'textNodes')||(s.parentNode && s.parentNode.nodeName == 'BODY' ? s.parentNode : null);
		if (n) {
			this.domElem.removeClass('disabled').toggleClass('active', $(n).css('text-align') == this.align);
		} else {
			this.domElem.addClass('disabled');
		}
	}
	
}

elRTE.prototype.ui.prototype.buttons.justifycenter = elRTE.prototype.ui.prototype.buttons.justifyleft;
elRTE.prototype.ui.prototype.buttons.justifyright  = elRTE.prototype.ui.prototype.buttons.justifyleft;
elRTE.prototype.ui.prototype.buttons.justifyfull   = elRTE.prototype.ui.prototype.buttons.justifyleft;

})(jQuery);
/**
 * @class button - insert/edit link (open dialog window)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.link = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.img = false;
	
	this.bm;
	
	function init() {
		self.labels = {
			id        : 'ID',
			'class'   : 'Css class',
			style     : 'Css style',
			dir       : 'Script direction',
			lang      : 'Language',
			charset   : 'Charset',
			type      : 'Target MIME type',
			rel       : 'Relationship page to target (rel)',
			rev       : 'Relationship target to page (rev)',
			tabindex  : 'Tab index',
			accesskey : 'Access key'
		}
		self.src = {
			main : {
				href   : $('<input type="text" />').addClass('span1'),
				title  : $('<input type="text" />').addClass('span1'),
				anchor : $('<select />').attr('name', 'anchor').addClass('span1'),
				target : $('<select />').addClass('span1')
					.append($('<option />').text(self.rte.i18n('In this window')).val(''))
					.append($('<option />').text(self.rte.i18n('In new window (_blank)')).val('_blank'))
				// 	.append($('<option />').text(self.rte.i18n('In new parent window (_parent)')).val('_parent'))
				// 	.append($('<option />').text(self.rte.i18n('In top frame (_top)')).val('_top'))
			},

			popup : {
				use        : $('<input type="checkbox" />'),
				url        : $('<input type="text" />'    ).addClass('span1').val('http://'),
				name       : $('<input type="text" />'    ).addClass('span1'),
				width      : $('<input type="text" />'    ).addClass('span1').attr({size : 6, title : self.rte.i18n('Width')} ).css('text-align', 'right'),
				height     : $('<input type="text" />'    ).addClass('span1').attr({size : 6, title : self.rte.i18n('Height')}).css('text-align', 'right'),
				left       : $('<input type="text" />'    ).addClass('span1').attr({size : 6, title : self.rte.i18n('Left')}  ).css('text-align', 'right'),
				top        : $('<input type="text" />'    ).addClass('span1').attr({size : 6, title : self.rte.i18n('Top')}   ).css('text-align', 'right'),
				location   : $('<input type="checkbox" />'),
				menubar    : $('<input type="checkbox" />'),
				toolbar    : $('<input type="checkbox" />'),
				scrollbars : $('<input type="checkbox" />'),
				status     : $('<input type="checkbox" />'),
				resizable  : $('<input type="checkbox" />'),
				dependent  : $('<input type="checkbox" />'),
				retfalse   : $('<input type="checkbox" />').attr('checked', true)
			},

			adv : {
				id        : $('<input type="text" />').addClass('span1'),
				'class'   : $('<input type="text" />').addClass('span1'),
				style     : $('<input type="text" />').addClass('span1'),
				dir       : $('<select />')
							.append($('<option />').text(self.rte.i18n('Not set')).val(''))
							.append($('<option />').text(self.rte.i18n('Left to right')).val('ltr'))
							.append($('<option />').text(self.rte.i18n('Right to left')).val('rtl')),
				lang      : $('<input type="text" />').addClass('span1'),
				charset   : $('<input type="text" />').addClass('span1'),
				type      : $('<input type="text" />').addClass('span1'),
				rel       : $('<input type="text" />').addClass('span1'),
				rev       : $('<input type="text" />').addClass('span1'),
				tabindex  : $('<input type="text" />').addClass('span1'),
				accesskey : $('<input type="text" />').addClass('span1')
			},
			events : {}
		}

		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />').addClass('span1');
		});

		$.each(self.src, function() {
			for (var n in this) {
				// this[n].attr('name', n);
				var t = this[n].attr('type');
				if (!t || (t == 'text'  && !this[n].attr('size')) ) {
					this[n].css('width', '100%');
				}
			}
		});
		
	}
	
	this.command = function() {
		var n = this.rte.selection.getNode(),
			sel, i, v, opts, l, r, link, href, s;
		
		!this.src && init();
		// this.rte.selection.saveIERange();

		this.bm = this.rte.selection.getBookmark();

		function isLink(n) { return n.nodeName == 'A' && n.href; }
		
		this.link = this.rte.dom.selfOrParentLink(n);
		
		if (!this.link) {
			sel = $.browser.msie ? this.rte.selection.selected() : this.rte.selection.selected({wrap : false});
			if (sel.length) {
				for (i=0; i < sel.length; i++) {
					if (isLink(sel[i])) {
						this.link = sel[i];
						break;
					}
				};
				if (!this.link) {
					this.link = this.rte.dom.parent(sel[0], isLink) || this.rte.dom.parent(sel[sel.length-1], isLink);
				}
			}
		}
		
		this.link = this.link ? $(this.link) : $(this.rte.doc.createElement('a'));
		this.img = n.nodeName == 'IMG' ? n : null;
		this.updatePopup();
		
		this.src.main.anchor.empty();
		$('a[href!=""][name]', this.rte.doc).each(function() {
			var n = $(this).attr('name');
			self.src.main.anchor.append($('<option />').val(n).text(n));
		});
		if (this.src.main.anchor.children().length) {
			this.src.main.anchor.prepend($('<option />').val('').text(this.rte.i18n('Select bookmark')) )
				.change(function() {
					var v = $(this).val();
					if (v) {
						self.src.main.href.val('#'+v);
					}
				});
		}
		
		opts = {
			rtl : this.rte.rtl,
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			tabs : { show : function(e, ui) { if (ui.index==3) { self.updateOnclick(); } } },
			close : function() {self.rte.browser.msie && self.rte.selection.restoreIERange(); },
			dialog : {
				width : 'auto',
				width : 430,
				title : this.rte.i18n('Link')
				
			}
		}

		d = new elDialogForm(opts);

		l = $('<div />')
			.append( $('<label />').append(this.src.popup.location).append(this.rte.i18n('Location bar')))
			.append( $('<label />').append(this.src.popup.menubar).append(this.rte.i18n('Menu bar')))
			.append( $('<label />').append(this.src.popup.toolbar).append(this.rte.i18n('Toolbar')))				
			.append( $('<label />').append(this.src.popup.scrollbars).append(this.rte.i18n('Scrollbars')));
		r = $('<div />')
			.append( $('<label />').append(this.src.popup.status).append(this.rte.i18n('Status bar')))
			.append( $('<label />').append(this.src.popup.resizable).append(this.rte.i18n('Resizable')))
			.append( $('<label />').append(this.src.popup.dependent).append(this.rte.i18n('Depedent')))				
			.append( $('<label />').append(this.src.popup.retfalse).append(this.rte.i18n('Add return false')));

		d.tab('main', this.rte.i18n('Properies'))
			.tab('popup',  this.rte.i18n('Popup'))
			.tab('adv',    this.rte.i18n('Advanced'))
			.tab('events', this.rte.i18n('Events'))
			.append($('<label />').append(this.src.popup.use).append(this.rte.i18n('Open link in popup window')), 'popup')
			.separator('popup')
			.append([this.rte.i18n('URL'),  this.src.popup.url],  'popup', true)
			.append([this.rte.i18n('Window name'), this.src.popup.name], 'popup', true)
			.append([this.rte.i18n('Window size'), $('<span />').append(this.src.popup.width).append(' x ').append(this.src.popup.height).append(' px')], 'popup', true)
			.append([this.rte.i18n('Window position'), $('<span />').append(this.src.popup.left).append(' x ').append(this.src.popup.top).append(' px')], 'popup', true)				
			.separator('popup')
			.append([l, r], 'popup', true);

		link = this.link.get(0);
		href = this.rte.dom.attr(link, 'href');
		this.src.main.href.val(href).change(function() {
			$(this).val(self.rte.utils.absoluteURL($(this).val()));
		});
		
		if (this.rte.options.fmAllow && this.rte.options.fmOpen) {
			var s = $('<span />').append(this.src.main.href.css('width', '87%'))
				.append(
					$('<span />').addClass('ui-state-default ui-corner-all')
						.css({'float' : 'right', 'margin-right' : '3px'})
						.attr('title', self.rte.i18n('Open file manger'))
						.append($('<span />').addClass('ui-icon ui-icon-folder-open'))
							.click( function() {
								self.rte.options.fmOpen( function(url) { self.src.main.href.val(url).change(); } );
							})
							.hover(function() {$(this).addClass('ui-state-hover')}, function() { $(this).removeClass('ui-state-hover')})
				);
			d.append([this.rte.i18n('Link URL'), s], 'main', true);
		} else {
			d.append([this.rte.i18n('Link URL'), this.src.main.href], 'main', true);
		}
		this.src.main.href.change();
		
		d.append([this.rte.i18n('Title'), this.src.main.title.val(this.rte.dom.attr(link, 'title'))], 'main', true);
		if (this.src.main.anchor.children().length) {
			d.append([this.rte.i18n('Bookmark'), this.src.main.anchor.val(href)], 'main', true)
		}
		if (!(this.rte.options.doctype.match(/xhtml/) && this.rte.options.doctype.match(/strict/))) {
			d.append([this.rte.i18n('Target'), this.src.main.target.val(this.link.attr('target')||'')], 'main', true);
		}
		


		for (var n in this.src.adv) {
			this.src.adv[n].val(this.rte.dom.attr(link, n));
			d.append([this.rte.i18n(this.labels[n] ? this.labels[n] : n), this.src.adv[n]], 'adv', true);
		}
		for (var n in this.src.events) {
			var v = this.rte.utils.trimEventCallback(this.rte.dom.attr(link, n));
			this.src.events[n].val(v);
			d.append([this.rte.i18n(this.labels[n] ? this.labels[n] : n), this.src.events[n]], 'events', true);
		}
		
		this.src.popup.use.change(function() {
			var c = $(this).attr('checked');
			$.each(self.src.popup, function() {
				if ($(this).attr('name') != 'use') {
					if (c) {
						$(this).removeAttr('disabled');
					} else {
						$(this).attr('disabled', true);
					}
				}
			})
		});
		this.src.popup.use.change();

		d.open();
	}
	
	this.update = function() {
		var n = this.rte.selection.getNode();
		
		// var t = this.rte.dom.selectionHas(function(n) { return n.nodeName == 'A' && n.href; });
		// this.rte.log(t)
		
		if (this.rte.dom.selfOrParentLink(n)) {
			this.domElem.removeClass('disabled').addClass('active');
		} else if (this.rte.dom.selectionHas(function(n) { return n.nodeName == 'A' && n.href; })) {
			this.domElem.removeClass('disabled').addClass('active');
		} else if (!this.rte.selection.collapsed() || n.nodeName == 'IMG') {
			this.domElem.removeClass('disabled active');
		} else {
			this.domElem.addClass('disabled').removeClass('active');
		}
	}
	
	this.updatePopup = function() {
		var onclick = ''+this.link.attr('onclick');
		// onclick = onclick ? $.trim(onclick.toString()) : ''
		if ( onclick.length>0 && (m = onclick.match(/window.open\('([^']+)',\s*'([^']*)',\s*'([^']*)'\s*.*\);\s*(return\s+false)?/))) {
			this.src.popup.use.attr('checked', 'on')
			this.src.popup.url.val(m[1]);
			this.src.popup.name.val(m[2]);

			if ( /location=yes/.test(m[3]) ) {
				this.src.popup.location.attr('checked', true);
			}
			if ( /menubar=yes/.test(m[3]) ) {
				this.src.popup.menubar.attr('checked', true);
			}
			if ( /toolbar=yes/.test(m[3]) ) {
				this.src.popup.toolbar.attr('checked', true);
			}
			if ( /scrollbars=yes/.test(m[3]) ) {
				this.src.popup.scrollbars.attr('checked', true);
			}
			if ( /status=yes/.test(m[3]) ) {
				this.src.popup.status.attr('checked', true);
			}
			if ( /resizable=yes/.test(m[3]) ) {
				this.src.popup.resizable.attr('checked', true);
			}
			if ( /dependent=yes/.test(m[3]) ) {
				this.src.popup.dependent.attr('checked', true);
			}
			if ((_m = m[3].match(/width=([^,]+)/))) {
				this.src.popup.width.val(_m[1]);
			}
			if ((_m = m[3].match(/height=([^,]+)/))) {
				this.src.popup.height.val(_m[1]);
			}
			if ((_m = m[3].match(/left=([^,]+)/))) {
				this.src.popup.left.val(_m[1]);
			}
			if ((_m = m[3].match(/top=([^,]+)/))) {
				this.src.popup.top.val(_m[1]);
			}
			if (m[4]) {
				this.src.popup.retfalse.attr('checked', true);
			}
		} else {
			$.each(this.src.popup, function() {
				var $this = $(this);
				if ($this.attr('type') == 'text') {
					$this.val($this.attr('name') == 'url' ? 'http://' : '');
				} else {
					if ($this.attr('name') == 'retfalse') {
						this.attr('checked', true);
					} else {
						$this.removeAttr('checked');
					}
				}
			});
		}
		
	}
	
	this.updateOnclick = function () {
		var url = this.src.popup.url.val();
		if (this.src.popup.use.attr('checked') && url) {
			var params = '';
			if (this.src.popup.location.attr('checked')) {
				params += 'location=yes,';
			}
			if (this.src.popup.menubar.attr('checked')) {
				params += 'menubar=yes,';
			}
			if (this.src.popup.toolbar.attr('checked')) {
				params += 'toolbar=yes,';
			}
			if (this.src.popup.scrollbars.attr('checked')) {
				params += 'scrollbars=yes,';
			}
			if (this.src.popup.status.attr('checked')) {
				params += 'status=yes,';
			}
			if (this.src.popup.resizable.attr('checked')) {
				params += 'resizable=yes,';
			}
			if (this.src.popup.dependent.attr('checked')) {
				params += 'dependent=yes,';
			}
			if (this.src.popup.width.val()) {
				params += 'width='+this.src.popup.width.val()+',';
			}
			if (this.src.popup.height.val()) {
				params += 'height='+this.src.popup.height.val()+',';
			}
			if (this.src.popup.left.val()) {
				params += 'left='+this.src.popup.left.val()+',';
			}
			if (this.src.popup.top.val()) {
				params += 'top='+this.src.popup.top.val()+',';
			}
			if (params.length>0) {
				params = params.substring(0, params.length-1)
			}
			var retfalse = this.src.popup.retfalse.attr('checked') ? 'return false;' : '';
			var onclick = "window.open('"+url+"', '"+$.trim(this.src.popup.name.val())+"', '"+params+"'); "+retfalse;
			this.src.events.onclick.val(onclick);
			if (!this.src.main.href.val()) {
				this.src.main.href.val('#');
			}
		} else {
			var v = this.src.events.onclick.val();
			v = v.replace(/window\.open\([^\)]+\)\s*;?\s*return\s*false\s*;?/i, '');
			this.src.events.onclick.val(v);
		}
	}
	
	this.set = function() {
		var href, fakeURL;
		this.updateOnclick();
		this.rte.selection.moveToBookmark(this.bm);
		// this.rte.selection.restoreIERange();
		this.rte.history.add();
		href = this.rte.utils.absoluteURL(this.src.main.href.val());
		if (!href) {
			// this.link.parentNode && this.rte.doc.execCommand('unlink', false, null);
			var bm = this.rte.selection.getBookmark();
			this.rte.dom.unwrap(this.link[0]);
			this.rte.selection.moveToBookmark(bm);

		} else {
				if (this.img && this.img.parentNode) {
					this.link = $(this.rte.dom.create('a')).attr('href', href);
					this.rte.dom.wrap(this.img, this.link[0]);
				} else if (!this.link[0].parentNode) {
					fakeURL = '#--el-editor---'+Math.random();
					this.rte.doc.execCommand('createLink', false, fakeURL);
					this.link = $('a[href="'+fakeURL+'"]', this.rte.doc);
					this.link.each(function() {
						var $this = $(this);

						// удаляем ссылки вокруг пустых элементов
						if (!$.trim($this.html()) && !$.trim($this.text())) {
							$this.replaceWith($this.text()); //  сохраняем пробелы :)
						}
					});
				}

			this.src.main.href.val(href);
			for (var tab in this.src) {
				if (tab != 'popup') {
					for (var n in this.src[tab]) {
						if (n != 'anchors') {
							var v = $.trim(this.src[tab][n].val());
							if (v) {
								this.link.attr(n, v);
							} else {
								this.link.removeAttr(n);
							}
						}
					}
				}
			};


			this.img && this.rte.selection.select(this.img);
		}
		this.rte.ui.update(true);
	}
	
}

})(jQuery);
/**
 * @class button - insert non breakable space
 * Если выделение схлопнуто и находится внутри div'a - он удаляется
 * Новые div'ы создаются только из несхлопнутого выделения
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.nbsp = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		this.rte.history.add();
		this.rte.selection.insertHtml('&nbsp;', true);
		this.rte.window.focus();
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
	}
}
})(jQuery);

/**
 * @class button - outdent text
 * уменьшает padding/margin/самомнение ;)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 * @todo decrease lists nesting level!
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.outdent = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;

	this.command = function() {
		var v = this.find();
		if (v.node) {
			this.rte.history.add();
			$(v.node).css(v.type, (v.val>40 ? v.val-40 : 0)+'px');
			this.rte.ui.update();
		}
	}
	
	this.find = function(n) {
		function checkNode(n) {
			var ret = {type : '', val : 0};
			var s;
			if ((s = self.rte.dom.attr(n, 'style'))) {
				ret.type = s.indexOf('padding-left') != -1
					? 'padding-left'
					: (s.indexOf('margin-left') != -1 ? 'margin-left' : '');
				ret.val = ret.type ? parseInt($(n).css(ret.type))||0 : 0;
			}
			return ret;
		}
		
		var n = this.rte.selection.getNode();
		var ret = checkNode(n);
		if (ret.val) {
			ret.node = n;
		} else {
			$.each(this.rte.dom.parents(n, '*'), function() {
				ret = checkNode(this);
				if (ret.val) {
					ret.node = this;
					return ret;
				}
			})
		}
		return ret;
	}
	
	this.update = function() {
		var v = this.find();
		if (v.node) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}

	
}

})(jQuery);
(function($) {
	elRTE.prototype.ui.prototype.buttons.pagebreak = function(rte, name) {
		this.constructor.prototype.constructor.call(this, rte, name);
		
		// prevent resize
		$(this.rte.doc.body).bind('mousedown', function(e) {
			if ($(e.target).hasClass('elrte-pagebreak')) {
				e.preventDefault();
			}
		})
		
		this.command = function() {
			this.rte.selection.insertHtml('<img src="'+this.rte.filter.url+'pixel.gif" class="elrte-protected elrte-pagebreak"/>', false);
		}
		
		this.update = function() {
			this.domElem.removeClass('disabled');
		}
	}
	
})(jQuery);/**
 * @class button - insert formatted text (open dialog window)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.pasteformattext = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.iframe = $(document.createElement('iframe')).addClass('el-rte-paste-input');
	this.doc    = null;
	var self    = this;
	
	this.command = function() {
		this.rte.selection.saveIERange();
		var self = this,
			opts = {
			submit : function(e, d) {
				e.stopPropagation();
				e.preventDefault();
				self.paste();
				d.close();
			},
			dialog : {
				width : 500,
				title : this.rte.i18n('Paste formatted text')
			}
		},
		d = new elDialogForm(opts);
		d.append(this.iframe).open();
		this.doc = this.iframe.get(0).contentWindow.document;
		html = this.rte.options.doctype
			+'<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
		html += '</head><body> <br /> </body></html>';	
		
		this.doc.open();
		this.doc.write(html);
		this.doc.close();

		if (!this.rte.browser.msie) {
			try { this.doc.designMode = "on"; } 
			catch(e) { }
		} else {
			this.doc.body.contentEditable = true;
		}
		setTimeout(function() { self.iframe[0].contentWindow.focus(); }, 50);
	}
	
	this.paste = function() {
		$(this.doc.body).find('[class]').removeAttr('class');
		var html = $.trim($(this.doc.body).html());
		if (html) {
			this.rte.history.add();
			this.rte.selection.restoreIERange();
			this.rte.selection.insertHtml(this.rte.filter.wysiwyg2wysiwyg(this.rte.filter.proccess('paste', html)));
			this.rte.ui.update(true);
		}
	}

	this.update = function() {
		this.domElem.removeClass('disabled');
	}
}
})(jQuery);

/**
 * @class кнопка "вставить только текст" 
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.pastetext = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.input = $('<textarea />').addClass('el-rte-paste-input');
	var self   = this;
	
	this.command = function() {
		this.rte.browser.msie && this.rte.selection.saveIERange();
		var opts = {
			submit : function(e, d) {
				e.stopPropagation();
				e.preventDefault();
				self.paste();
				d.close();
			},
			dialog : {
				width : 500,
				title : this.rte.i18n('Paste only text')
			}
		}
		var d = new elDialogForm(opts);
		d.append(this.input).open();
	}
	
	this.paste = function() {
		var txt = $.trim(this.input.val());
		if (txt) {
			this.rte.history.add();
			this.rte.browser.msie && this.rte.selection.restoreIERange();
			this.rte.selection.insertText(txt.replace(/\r?\n/g, '<br />'), true);
			this.rte.ui.update(true);
		}
		this.input.val('');
	}

	this.update = function() {
		this.domElem.removeClass('disabled');
	}
	
}
})(jQuery);
/**
 * @class button - save editor content (submit form)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.save = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.active = true;
	
	this.command = function() {
		this.rte.save();
	}
	
	this.update = function() { }
}
})(jQuery);
/**
 * @class button - insert smiley (open dialog window)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки
 *
 * @author:    eSabbath
 *
 **/
(function($) { 
elRTE.prototype.ui.prototype.buttons.smiley = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.img = null;
	this.url = this.rte.filter.url+'smileys/';
	this.smileys = {
		'smile' : 'smile.png',
		'happy' : 'happy.png',
		'tongue' : 'tongue.png',
		'surprised' : 'surprised.png',
		'waii' : 'waii.png',
		'wink' : 'wink.png',
		'evilgrin' : 'evilgrin.png',
		'grin' : 'grin.png',
		
		
		'unhappy' : 'unhappy.png'
	};
	this.width = 120;

	this.command = function() {
		var self = this, url = this.url, d, opts, img;

		this.rte.browser.msie && this.rte.selection.saveIERange();

		opts = {
			dialog : {
				height  : 120,
				width   : this.width,
				title   : this.rte.i18n('Smiley'),
				buttons : {}
			}
		}
		d = new elDialogForm(opts);
		$.each(this.smileys, function(name, img) {
			d.append($('<img src="'+url+img+'" title="'+name+'" id="'+name+'" class="el-rte-smiley"/>').click(function() { self.set(this.id, d);	}));
		});
		d.open();
	}

	this.update = function() {
		this.domElem.removeClass('disabled');
		this.domElem.removeClass('active');
	}

	this.set = function(s, d) {
		this.rte.browser.msie && this.rte.selection.restoreIERange();
		if (this.smileys[s]) {
			this.img = $(this.rte.doc.createElement('img'));
			this.img.attr({
				src    : this.url + this.smileys[s],
				title  : s,
				alt    : s
			});
			this.rte.selection.insertNode(this.img.get(0));
			this.rte.ui.update();
		}
		d.close();
	}
}
})(jQuery);
/**
 * @class button - stops elements floating. Insert div with style="clear:all"
 * Если выделение схлопнуто и находится внутри div'a с аттрибутом или css clear - он удаляется
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.stopfloat = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);

	this.find = function() {
		if (this.rte.selection.collapsed()) {
			var n = this.rte.dom.selfOrParent(this.rte.selection.getEnd(), /^DIV$/);
			if (n && (this.rte.dom.attr(n, 'clear') || $(n).css('clear') != 'none')) {
				return n;
			}
		}
	}
	
	this.command = function() {
		var n;
		if ((n = this.find())) {
			var n = $(n);
			this.rte.history.add();
			if (!n.children().length && !$.trim(n.text()).length) {
				n.remove();
			} else {
				n.removeAttr('clear').css('clear', '');
			}
		} else {
			this.rte.history.add();
			this.rte.selection.insertNode($(this.rte.dom.create('div')).css('clear', 'both').get(0), true);
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		if (this.find()) {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	}
}

})(jQuery);/**
 * @class button - create/edit table (open dialog window)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.table = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self    = this;
	this.src    = null;
	this.labels = null;
	
	function init() {
		self.labels = {
			main      : 'Properies',
			adv       : 'Advanced',
			events    : 'Events',
			id        : 'ID',
			'class'   : 'Css class',
			style     : 'Css style',
			dir       : 'Script direction',
			summary   : 'Summary',
			lang      : 'Language',
			href      : 'URL'
		}
		
		self.src = {
			main : {
				caption : $('<input type="text" />').addClass('span4').attr('size', 20),
				rows    : $('<input type="text" />').css('width','20px').attr('size', 5).val(2),
				cols    : $('<input type="text" />').css('width','20px').attr('size', 5).val(2),
				width   : $('<input type="text" />').css('width','30px').attr('size', 5),
				wunit   : $('<select />').css('width','40px').css('font-size','12px').attr('size', 1)
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),				
				height  : $('<input type="text" />').css('width','30px').attr('size', 5),
				hunit   : $('<select />').css('width','40px').css('font-size','12px').attr('size', 1)
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),	
				align   : $('<select />').addClass('span1').css('font-size','12px').attr('size', 1)
							.append($('<option />').val('').text(self.rte.i18n('Not set')))
							.append($('<option />').val('left').text(self.rte.i18n('Left')))
							.append($('<option />').val('center').text(self.rte.i18n('Center')))	
							.append($('<option />').val('right').text(self.rte.i18n('Right'))),	
				spacing : $('<input type="text" />').addClass('span1').attr('size', 5),
				padding : $('<input type="text" />').addClass('span1').attr('size', 5),
				border  : $('<div />').addClass('span1'),
				// frame   : $('<select />')
				// 			.append($('<option />').val('void').text(self.rte.i18n('No')))
				// 			.append($('<option />').val('border').text(self.rte.i18n('Yes'))),
				rules   : $('<select />').addClass('span1').css('font-size','12px').attr('size', 1)
							.append($('<option />').val('none').text(self.rte.i18n('No')))
							.append($('<option />').val('all').text(self.rte.i18n('Cells')))
							.append($('<option />').val('groups').text(self.rte.i18n('Groups')))
							.append($('<option />').val('rows').text(self.rte.i18n('Rows')))
							.append($('<option />').val('cols').text(self.rte.i18n('Columns'))),
				margin  : $('<div />').addClass('span1'),
				bg      : $('<div />').addClass('span1'),
				bgimg   : $('<input type="text" />').addClass('span4')
			},
			
			adv : {
				id        : $('<input type="text" />').addClass('span4').attr('size', 10),
				summary   : $('<input type="text" />').addClass('span4').attr('size', 10),
				'class'   : $('<input type="text" />').addClass('span4').attr('size', 10),
				style     : $('<input type="text" />').addClass('span4').attr('size', 10),
				dir       : $('<select />').addClass('span1').css('font-size','12px')
								.append($('<option />').text(self.rte.i18n('Not set')).val(''))
								.append($('<option />').text(self.rte.i18n('Left to right')).val('ltr'))
								.append($('<option />').text(self.rte.i18n('Right to left')).val('rtl')),
				lang      : $('<input type="text" />').addClass('span4').attr('size', 10)
			},
			
			events : {}
		}
		
		$.each(self.src, function() {
			for (var n in this) {
				this[n].attr('name', n);
				var t = this[n].get(0).nodeName; 
				if (t == 'INPUT' && n != 'bgimg') {
					this[n].css(this[n].attr('size') ? {'text-align' : 'right'} : {width : '100%'});
				} else if (t == 'SELECT' && n!='wunit' && n!='hunit') {
					this[n].css('width', '100%');
				}
			}
		});
		
		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />').attr('size', '10').addClass('span4');
		});
		
		self.src.main.align.change(function() {
			var v = $(this).val();
			if (v == 'center') {
				self.src.main.margin.val({left : 'auto', right : 'auto'});
			} else {
				var m = self.src.main.margin.val();
				if (m.left == 'auto' && m.right == 'auto') {
					self.src.main.margin.val({left : '', right : ''});
				}
			}
		});
		
		self.src.main.bgimg.change(function() {
			var t = $(this);
			t.val(self.rte.utils.absoluteURL(t.val()));
		})
		
	}
	
	this.command = function() {
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^TABLE$/);
		
		if (this.name == 'table') {
			this.table = $(this.rte.doc.createElement('table'));	
		} else {
			this.table = n ? $(n) : $(this.rte.doc.createElement('table'));					
		}
		
		!this.src && init();
		this.src.main.border.elBorderSelect({styleHeight : 117});
		this.src.main.bg.elColorPicker({palettePosition : 'outer', 'class' : 'el-colorpicker ui-icon ui-icon-pencil'});
		this.src.main.margin.elPaddingInput({ type : 'margin', value : this.table});
		
		if (this.table.parents().length) {
			this.src.main.rows.val('').attr('disabled', true);
			this.src.main.cols.val('').attr('disabled', true);
		} else {
			this.src.main.rows.val(2).removeAttr('disabled');
			this.src.main.cols.val(2).removeAttr('disabled');
		}
		
		var w = this.table.css('width') || this.table.attr('width');
		this.src.main.width.val(parseInt(w)||'');
		this.src.main.wunit.val(w.indexOf('px') != -1 ? 'px' : '%');
		
		var h = this.table.css('height') || this.table.attr('height');	
		this.src.main.height.val(parseInt(h)||'');
		this.src.main.hunit.val(h && h.indexOf('px') != -1 ? 'px' : '%');

		var f = this.table.css('float');
		this.src.main.align.val('');
		if (f == 'left' || f == 'right') {
			this.src.main.align.val(f);
		} else {
			var ml = this.table.css('margin-left');
			var mr = this.table.css('margin-right');
			if (ml == 'auto' && mr == 'auto') {
				this.src.main.align.val('center');
			}
		}

		this.src.main.border.val(this.table);
		//this.src.main.frame.val(this.table.attr('frame'));
		this.src.main.rules.val(this.rte.dom.attr(this.table.get(0), 'rules'));

		this.src.main.bg.val(this.table.css('background-color'));
		var bgimg = (this.table.css('background-image')||'').replace(/url\(([^\)]+)\)/i, "$1");
		this.src.main.bgimg.val(bgimg!='none' ? bgimg : '');

		var opts = {
			rtl : this.rte.rtl,
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			dialog : {
				width : 530,
				title : this.rte.i18n('Table')
			}
		}
		var d = new elDialogForm(opts);
		
		for (var tab in this.src) {
			d.tab(tab, this.rte.i18n(this.labels[tab]));
			if (tab == 'main') {
				var t1 = $('<table />')
					.append($('<tr />').append('<td>'+this.rte.i18n('Rows')+'</td>').append($('<td />').append(this.src.main.rows)))
					.append($('<tr />').append('<td>'+this.rte.i18n('Columns')+'</td>').append($('<td />').append(this.src.main.cols)));
				var t2 = $('<table />')
					.append($('<tr />').append('<td>'+this.rte.i18n('Width')+'</td>').append($('<td />').append(this.src.main.width).append(this.src.main.wunit)))
					.append($('<tr />').append('<td>'+this.rte.i18n('Height')+'</td>').append($('<td />').append(this.src.main.height).append(this.src.main.hunit)));
				var t3 = $('<table />')
					.append($('<tr />').append('<td>'+this.rte.i18n('Spacing')+'</td>').append($('<td />').append(this.src.main.spacing.val(this.table.attr('cellspacing')||''))))
					.append($('<tr />').append('<td>'+this.rte.i18n('Padding')+'</td>').append($('<td />').append(this.src.main.padding.val(this.table.attr('cellpadding')||''))));
				
				d.append([this.rte.i18n('Caption'), this.src.main.caption.val(this.table.find('caption').eq(0).text() || '')], 'main', true)
					.separator('main')
					.append([t1, t2, t3], 'main', true)
					.separator('main')
					.append([this.rte.i18n('Border'),        this.src.main.border], 'main', true)
					//.append([this.rte.i18n('Frame'),       this.src.main.frame], 'main', true)
					.append([this.rte.i18n('Inner borders'), this.src.main.rules], 'main', true)
					.append([this.rte.i18n('Alignment'),     this.src.main.align], 'main', true)
					.append([this.rte.i18n('Margins'),       this.src.main.margin], 'main', true)
					.append([this.rte.i18n('Background'),    $('<span />').append($('<span />').css({'float' : 'left', 'margin-right' : '3px'}).append(this.src.main.bg)).append(this.src.main.bgimg)], 'main', true)
			} else {
				for (var name in this.src[tab]) {
					var v = this.rte.dom.attr(this.table, name);
					if (tab == 'events') {
						v = this.rte.utils.trimEventCallback(v);
					} 
					d.append([this.rte.i18n(this.labels[name] ? this.labels[name] : name), this.src[tab][name].val(v)], tab, true);
				}
			}
		}
		
		d.open();
	}
	
	this.set = function() {
		
		if (!this.table.parents().length) {
			var r = parseInt(this.src.main.rows.val()) || 0;
			var c = parseInt(this.src.main.cols.val()) || 0;
			if (r<=0 || c<=0) {
				return;
			}
			this.rte.history.add(); 
			var b = $(this.rte.doc.createElement('tbody')).appendTo(this.table);
			
			for (var i=0; i < r; i++) {
				var tr = '<tr>';
				for (var j=0; j < c; j++) {
					tr += '<td>&nbsp;</td>';
				}
				b.append(tr+'</tr>');
			};
			
			// var tr = $(this.rte.doc.createElement('tr'));
			// 
			// for (var i=0; i < c; i++) {
			// 	tr.append($(this.rte.doc.createElement('td')).html('&nbsp;'));
			// };
			// 
			// for (var i=0; i<r; i++) {
			// 	b.append(tr.clone(true));
			// };
			
			// this.rte.selection.insertNode(this.table.get(0), true);
		} else {
			this.table
				.removeAttr('width')
				.removeAttr('height')
				.removeAttr('border')
				.removeAttr('align')
				.removeAttr('bordercolor')
				.removeAttr('bgcolor')
				.removeAttr('cellspacing')
				.removeAttr('cellpadding')
				.removeAttr('frame')
				.removeAttr('rules')
				.removeAttr('style');
		}
		
		var cap = $.trim(this.src.main.caption.val());
		if (cap) {
			if (!this.table.children('caption').length) {
				this.table.prepend('<caption />' );
			}
			this.table.children('caption').text(cap);
		} else {
			this.table.children('caption').remove();
		}
		
		
		for (var tab in this.src) {
			if (tab != 'main') {
				for (var n in this.src[tab]) {
					var v = $.trim(this.src[tab][n].val());
					if (v) {
						this.table.attr(n, v);
					} else {
						this.table.removeAttr(n);
					}
				}
			}
		}
		var spacing, padding, rules;

		if ((spacing = parseInt(this.src.main.spacing.val())) && spacing>=0) {
			this.table.attr('cellspacing', spacing);
		} 

		if ((padding = parseInt(this.src.main.padding.val())) && padding>=0) {
			this.table.attr('cellpadding', padding);
		} 
		
		if ((rules = this.src.main.rules.val())) {
			this.table.attr('rules', rules);
		}
		
		var
			w = parseInt(this.src.main.width.val()) || '',
			h = parseInt(this.src.main.height.val()) || '',
			i = $.trim(this.src.main.bgimg.val()),
			b = this.src.main.border.val(),
			m = this.src.main.margin.val(),
			f = this.src.main.align.val();
		this.table.css({
			width              : w ? w+this.src.main.wunit.val() : '',
			height             : h ? h+this.src.main.hunit.val() : '',
			border             : $.trim(b.width+' '+b.style+' '+b.color),
			'background-color' : this.src.main.bg.val(),
			'background-image' : i ? 'url('+i+')' : ''
		});	
		if (m.css) {
			this.table.css('margin', m.css);
		} else {
			this.table.css({
				'margin-top'    : m.top,
				'margin-right'  : m.right,
				'margin-bottom' : m.bottom,
				'margin-left'   : m.left
			});
		}
		if ((f=='left' || f=='right') && this.table.css('margin-left')!='auto'  && this.table.css('margin-right')!='auto') {
			this.table.css('float', f);
		}
		if (!this.table.attr('style')) {
			this.table.removeAttr('style');
		}
		if (!this.table.parents().length) {
			this.rte.selection.insertNode(this.table.get(0), true);
		}
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		if (this.name == 'tableprops' && !this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^TABLE$/)) {
			this.domElem.addClass('disabled').removeClass('active');
		}
	}
	
}

elRTE.prototype.ui.prototype.buttons.tableprops = elRTE.prototype.ui.prototype.buttons.table;

})(jQuery);

/**
 * @class button - remove table
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.tablerm = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var t = this.rte.dom.parent(this.rte.selection.getNode(), /^TABLE$/);
		// t && $(t).remove();
		if (t) {
			this.rte.history.add();
			$(t).remove();
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		if (this.rte.dom.parent(this.rte.selection.getNode(), /^TABLE$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}
})(jQuery);
/**
 * @class button - table cell properties
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.tbcellprops = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.src = null;
	this.labels = null;
	
	function init() {
		self.labels = {
			main    : 'Properies',
			adv     : 'Advanced',
			events  : 'Events',
			id      : 'ID',
			'class' : 'Css class',
			style   : 'Css style',
			dir     : 'Script direction',
			lang    : 'Language'
		}
		
		self.src = {
			main : {
				type    : $('<select />').addClass('span1').attr('size','1').css('font-size','12px')
							.append($('<option />').val('td').text(self.rte.i18n('Data')))
							.append($('<option />').val('th').text(self.rte.i18n('Header'))),
				width   : $('<input type="text" />').attr('size', 4).addClass('span1'),
				wunit   : $('<select />').addClass('span1').attr('size','1').css('font-size','12px')
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),				
				height  : $('<input type="text" />').attr('size', 4).addClass('span1'),
				hunit   : $('<select />').addClass('span1').attr('size','1').css('font-size','12px')
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),	
				align   : $('<select />').addClass('span1').attr('size','1').css('font-size','12px')
							.append($('<option />').val('').text(self.rte.i18n('Not set')))
							.append($('<option />').val('left').text(self.rte.i18n('Left')))
							.append($('<option />').val('center').text(self.rte.i18n('Center')))	
							.append($('<option />').val('right').text(self.rte.i18n('Right')))
							.append($('<option />').val('justify').text(self.rte.i18n('Justify'))),	
				border  : $('<div />').addClass('span1'),
				padding  : $('<div />').addClass('span1'),
				bg      : $('<div />').addClass('span1'),
				bgimg   : $('<input type="text" />').addClass('span4'),
				apply   : $('<select />').addClass('span1').attr('size','1').css('font-size','12px')
							.append($('<option />').val('').text(self.rte.i18n('Current cell')))
							.append($('<option />').val('row').text(self.rte.i18n('All cells in row')))
							.append($('<option />').val('column').text(self.rte.i18n('All cells in column')))	
							.append($('<option />').val('table').text(self.rte.i18n('All cells in table')))
			},
			
			adv : {
				id        : $('<input type="text" />').addClass('span4').attr('size','10'),
				'class'   : $('<input type="text" />').addClass('span4').attr('size','10'),
				style     : $('<input type="text" />').addClass('span4').attr('size','10'),
				dir       : $('<select />').css('width', '100%').css('font-size','12px')
								.append($('<option />').text(self.rte.i18n('Not set')).val(''))
								.append($('<option />').text(self.rte.i18n('Left to right')).val('ltr'))
								.append($('<option />').text(self.rte.i18n('Right to left')).val('rtl')),
				lang      : $('<input type="text" />').addClass('span4').attr('size','10')
			},
			
			events : {}
		}
		
		$.each(self.src, function() {
			for (var n in this) {
				this[n].attr('name', n);
				if (this[n].attr('type') == 'text' && !this[n].attr('size') && n!='bgimg') {
					this[n].css('width', '100%')
				}
			}
		});
		
		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />').addClass('span4');
		});
		
	}
	
	this.command = function() {
		!this.src && init();
		this.cell = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/);
		if (!this.cell) {
			return;
		}
		this.src.main.type.val(this.cell.nodeName.toLowerCase());
		this.cell = $(this.cell);
		this.src.main.border.elBorderSelect({styleHeight : 117, value : this.cell});
		this.src.main.bg.elColorPicker({palettePosition : 'outer', 'class' : 'el-colorpicker ui-icon ui-icon-pencil'});
		this.src.main.padding.elPaddingInput({ value : this.cell});
		
		var w = this.cell.css('width') || this.cell.attr('width');
		this.src.main.width.val(parseInt(w)||'');
		this.src.main.wunit.val(w.indexOf('px') != -1 ? 'px' : '%');
		
		var h = this.cell.css('height') || this.cell.attr('height');	
		this.src.main.height.val(parseInt(h)||'');
		this.src.main.hunit.val(h.indexOf('px') != -1 ? 'px' : '%');
		
		this.src.main.align.val(this.cell.attr('align') || this.cell.css('text-align'));
		this.src.main.bg.val(this.cell.css('background-color'));
		var bgimg = this.cell.css('background-image');
		this.src.main.bgimg.val(bgimg && bgimg!='none' ? bgimg.replace(/url\(([^\)]+)\)/i, "$1") : '');
		this.src.main.apply.val('');
		
		var opts = {
			rtl : this.rte.rtl,
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			dialog : {
				width : 520,
				title : this.rte.i18n('Table cell properties')
			}
		}
		var d = new elDialogForm(opts);
		for (var tab in this.src) {
			d.tab(tab, this.rte.i18n(this.labels[tab]));
			
			if (tab == 'main') {
				d.append([this.rte.i18n('Width'),              $('<span />').append(this.src.main.width).append(this.src.main.wunit)],  'main', true)
					.append([this.rte.i18n('Height'),          $('<span />').append(this.src.main.height).append(this.src.main.hunit)], 'main', true)
					.append([this.rte.i18n('Table cell type'), this.src.main.type],    'main', true)
					.append([this.rte.i18n('Border'),          this.src.main.border],  'main', true)
					.append([this.rte.i18n('Alignment'),       this.src.main.align],   'main', true)
					.append([this.rte.i18n('Paddings'),        this.src.main.padding], 'main', true)
					.append([this.rte.i18n('Background'),      $('<span />').append($('<span />').css({'float' : 'left', 'margin-right' : '3px'}).append(this.src.main.bg)).append(this.src.main.bgimg)],  'main', true)
					.append([this.rte.i18n('Apply to'),        this.src.main.apply],   'main', true);
			} else {
				for (var name in this.src[tab]) {
					var v = this.cell.attr(name) || '';
					if (tab == 'events') {
						v = this.rte.utils.trimEventCallback(v);
					} 
					d.append([this.rte.i18n(this.labels[name] ? this.labels[name] : name), this.src[tab][name].val(v)], tab, true);
				}
			}
		}
		d.open()
	}
	
	this.set = function() {
		// $(t).remove();
		var target = this.cell,
			apply  = this.src.main.apply.val();
		switch (this.src.main.apply.val()) {
			case 'row':
				target = this.cell.parent('tr').children('td,th');
				break;
				
			case 'column':
				target = $(this.rte.dom.tableColumn(this.cell.get(0)));
				break;
				
			case 'table':
				target = this.cell.parents('table').find('td,th');
				break;
		}

		for (var tab in this.src) {
			if (tab != 'main') {
				for (var n in this.src[tab]) {
					var v = $.trim(this.src[tab][n].val());
					if (v) {
						target.attr(n, v);
					} else {
						target.removeAttr(n);
					}
				}
			}
		}
		
		target.removeAttr('width')
			.removeAttr('height')
			.removeAttr('border')
			.removeAttr('align')
			.removeAttr('bordercolor')
			.removeAttr('bgcolor');
			
		var t = this.src.main.type.val();
		var w = parseInt(this.src.main.width.val()) || '';
		var h = parseInt(this.src.main.height.val()) || '';
		var i = $.trim(this.src.main.bgimg.val());
		var b = this.src.main.border.val();
		var css = {
			'width'            : w ? w+this.src.main.wunit.val() : '',
			'height'           : h ? h+this.src.main.hunit.val() : '',
			'background-color' : this.src.main.bg.val(),
			'background-image' : i ? 'url('+i+')' : '',
			'border'           : $.trim(b.width+' '+b.style+' '+b.color),
			'text-align'       : this.src.main.align.val() || ''
		};
		var p = this.src.main.padding.val();
		if (p.css) {
			css.padding = p.css;
		} else {
			css['padding-top']    = p.top;
			css['padding-right']  = p.right;
			css['padding-bottom'] = p.bottom;
			css['padding-left']   = p.left;
		}
		
		target = target.get();

		$.each(target, function() {
			var type = this.nodeName.toLowerCase();
			var $this = $(this);
			if (type != t) {
				
				var attr = {}
				for (var i in self.src.adv) {
					var v = $this.attr(i)
					if (v) {
						attr[i] = v.toString();
					}
				}
				for (var i in self.src.events) {
					var v = $this.attr(i)
					if (v) {
						attr[i] = v.toString();
					}
				}
				var colspan = $this.attr('colspan')||1;
				var rowspan = $this.attr('rowspan')||1;
				if (colspan>1) {
					attr.colspan = colspan;
				}
				if (rowspan>1) {
					attr.rowspan = rowspan;
				}
				
				$this.replaceWith($('<'+t+' />').html($this.html()).attr(attr).css(css) );
				
			} else {
				$this.css(css);
			}
		});

		this.rte.ui.update();
	}
	
	this.update = function() {
		if (this.rte.dom.parent(this.rte.selection.getNode(), /^TABLE$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
	
}

})(jQuery);/**
 * @class button - table cells merge
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.tbcellsmerge = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	function selectedCells() {
		var c1 = self.rte.dom.selfOrParent(self.rte.selection.getStart(), /^(TD|TH)$/);
		var c2 = self.rte.dom.selfOrParent(self.rte.selection.getEnd(), /^(TD|TH)$/);		
		if (c1 && c2 && c1!=c2 && $(c1).parents('table').get(0) == $(c2).parents('table').get(0)) {
			return [c1, c2];
		}
		return null;
	}
	
	this.command = function() {
		var cells = selectedCells();

		if (cells) {
			
			var _s  = this.rte.dom.indexOf($(cells[0]).parent('tr').get(0));
			var _e  = this.rte.dom.indexOf($(cells[1]).parent('tr').get(0));
			var ro  = Math.min(_s, _e); // row offset
			var rl  = Math.max(_s, _e) - ro + 1; // row length
			var _c1 = this.rte.dom.tableColumn(cells[0], true, true); 
			var _c2 = this.rte.dom.tableColumn(cells[1], true);
			var _i1 = $.inArray(cells[0], _c1.column); 
			var _i2 = $.inArray(cells[1], _c2.column);
			
			var colBegin = _c1.info.offset[_i1] < _c2.info.offset[_i2]  ? _c1 : _c2;
			var colEnd   = _c1.info.offset[_i1] >= _c2.info.offset[_i2] ? _c1 : _c2;
			var length   = 0;
			var target   = null;
			var html     = '';
			this.rte.history.add();
			var rows = $($(cells[0]).parents('table').eq(0).find('tr').get().slice(ro, ro+rl))
				.each( function(i) {
					var _l = html.length;
					var accept = false;
					$(this).children('td,th').each(function() {
						var $this   = $(this);
						var inBegin = $.inArray(this, colBegin.column);
						var inEnd   = $.inArray(this, colEnd.column);
						
						if (inBegin!=-1 || inEnd!=-1) {
							accept = inBegin!=-1 && inEnd==-1;
							var len = parseInt($this.attr('colspan')||1)
							if (i == 0) {
								length += len;
							}
							
							if (inBegin!=-1 && i>0) {
								var delta = colBegin.info.delta[inBegin];
								if (delta>0) {
									if ($this.css('text-align') == 'left') {
										var cell = $this.clone(true);
										$this.html('&nbsp;');
									} else {
										var cell = $this.clone().html('&nbsp;');
									}
									cell.removeAttr('colspan').removeAttr('id').insertBefore(this);
									if (delta>1) {
										cell.attr('colspan', delta);
									}
								}
							}
							
							if (inEnd!=-1) {
								var delta = colEnd.info.delta[inEnd];
								if (len-delta>1) {
									var cp = len-delta-1;
									if ($this.css('text-align') == 'right') {
										var cell = $this.clone(true);
										$this.html('&nbsp;');
									} else {
										var cell = $this.clone().html('&nbsp;');
									}
									cell.removeAttr('colspan').removeAttr('id').insertAfter(this);
									if (cp>1) {
										cell.attr('colspan', cp);
									}
								}
							}
							if (!target) {
								target = $this;
							} else {
								html += $this.html();
								$this.remove();
							}
						} else if (accept) {
							if (i == 0) {
								length += parseInt($this.attr('colspan')||1);
							}
							html += $this.html();
							$this.remove();
							

						}
					})
					html += _l!=html.length ? '<br />' : '';
				});

			target.removeAttr('colspan').removeAttr('rowspan').html(target.html()+html)
			if (length>1) {
				target.attr('colspan', length);
			}
			if (rl>1) {
				target.attr('rowspan', rl);
			}
			// sometimes when merge cells with different rowspans we get "lost" cells in rows 
			// this add cells if needed
			this.rte.dom.fixTable($(cells[0]).parents('table').get(0));
		}
	}
	
	this.update = function() {
		if (selectedCells()) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}
})(jQuery);
/**
 * @class button - split merged cell
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки
 * @todo split not merged cell 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru 
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.tbcellsplit = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/);
		if (n) {
			this.rte.history.add();
			var colspan = parseInt(this.rte.dom.attr(n, 'colspan'));
			var rowspan = parseInt(this.rte.dom.attr(n, 'rowspan'));
			if (colspan>1 || rowspan>1) {
				var cnum = colspan-1;
				var rnum = rowspan-1;
				var tb   = this.rte.dom.parent(n, /^TABLE$/);
				var tbm  = this.rte.dom.tableMatrix(tb);
				
				// ячейки в текущем ряду
				if (cnum) {
					for (var i=0; i<cnum; i++) {
						$(this.rte.dom.create(n.nodeName)).html('&nbsp;').insertAfter(n);
					}
				}
				if (rnum) {
					var ndx  = this.rte.dom.indexesOfCell(n, tbm)
					var rndx = ndx[0];
					var cndx = ndx[1];
					// ячейки в следущих рядах
					for (var r=rndx+1; r < rndx+rnum+1; r++) {
						var cell;
						
						if (!tbm[r][cndx].nodeName) {
							if (tbm[r][cndx-1].nodeName) {
								cell = tbm[r][cndx-1];
							} else {
								for (var i=cndx-1; i>=0; i--) {
									if (tbm[r][i].nodeName) {
										cell =tbm[r][i];
										break;
									}
								}
							}
							if (cell) {
								for (var i=0; i<= cnum; i++) {
									$(this.rte.dom.create(cell.nodeName)).html('&nbsp;').insertAfter(cell);
								}
							}
						}
					};
				}
				$(n).removeAttr('colspan').removeAttr('rowspan');
				this.rte.dom.fixTable(tb);
			}
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/);
		if (n && (parseInt(this.rte.dom.attr(n, 'colspan'))>1 || parseInt(this.rte.dom.attr(n, 'rowspan'))>1)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

})(jQuery);
/**
 * @class button - Insert new column in table(before or after current)
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.tbcolbefore = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	this.command = function() {
		var self = this;
		var cells = this.rte.dom.tableColumn(this.rte.selection.getNode(), false, true);
		if (cells.length) {
			this.rte.history.add();
			$.each(cells, function() {
				var $this = $(this);
				var cp = parseInt($this.attr('colspan')||1)
				if (cp >1) {
					$this.attr('colspan', cp+1);
				} else {
					var c = $(self.rte.dom.create(this.nodeName)).html('&nbsp;');
					if (self.name == 'tbcolbefore') {
						c.insertBefore(this);
					} else {
						c.insertAfter(this);
					}
				}
			});
			this.rte.ui.update();
		}
	}
	
	this.update = function() {
		if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

elRTE.prototype.ui.prototype.buttons.tbcolafter = elRTE.prototype.ui.prototype.buttons.tbcolbefore;

})(jQuery);
/**
 * @class button - remove table colunm
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.tbcolrm = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	this.command = function() {
		var n     = this.rte.selection.getNode();
		var c     = this.rte.dom.selfOrParent(n, /^(TD|TH)$/);
		var prev  = $(c).prev('td,th').get(0);
		var next  = $(c).next('td,th').get(0);			
		var tb    = this.rte.dom.parent(n, /^TABLE$/);
		var cells = this.rte.dom.tableColumn(n, false, true);

		if (cells.length) {
			this.rte.history.add();
			$.each(cells, function() {
				var $this = $(this);
				var cp    = parseInt($this.attr('colspan')||1);
				if ( cp>1 ) {
					$this.attr('colspan', cp-1);
				} else {
					$this.remove();
				}
			});
			this.rte.dom.fixTable(tb);
			if (prev || next) {
				this.rte.selection.selectContents(prev ? prev : next).collapse(true);
			}
			this.rte.ui.update(true);
		}
	}
	
	this.update = function() {
		if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

})(jQuery);
/**
 * @class меню - Новый ряд в таблице
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 **/
elRTE.prototype.ui.prototype.buttons.tbrowbefore = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var n  = this.rte.selection.getNode();
		var c  = this.rte.dom.selfOrParent(n, /^(TD|TH)$/);
		var r  = this.rte.dom.selfOrParent(c, /^TR$/);
		var mx = this.rte.dom.tableMatrix(this.rte.dom.selfOrParent(c, /^TABLE$/));

		if (c && r && mx) {
			this.rte.history.add();
			var before = this.name == 'tbrowbefore';
			var ro     = $(r).prevAll('tr').length;
			var cnt    = 0;
			var mdf    = [];
			
			function _find(x, y) {
				while (y>0) {
					y--;
					if (mx[y] && mx[y][x] && mx[y][x].nodeName) {
						return mx[y][x];
					}
				}
			}
			
			for (var i=0; i<mx[ro].length; i++) {
				if (mx[ro][i] && mx[ro][i].nodeName) {
					var cell    = $(mx[ro][i]);
					var colspan = parseInt(cell.attr('colspan')||1);
					if (parseInt(cell.attr('rowspan')||1) > 1) {
						if (before) {
							cnt += colspan;
						} else {
							mdf.push(cell);
						}
					} else {
						cnt += colspan;
					}
				} else if (mx[ro][i] == '-') {
					cell = _find(i, ro);
					cell && mdf.push($(cell));
				}
			}
			var row = $(this.rte.dom.create('tr'));
			for (var i=0; i<cnt; i++) {
				row.append('<td>&nbsp;</td>');
			}
			if (before) {
				row.insertBefore(r);
			} else {
				row.insertAfter(r);
			}
			$.each(mdf, function() {
				$(this).attr('rowspan', parseInt($(this).attr('rowspan')||1)+1);
			});
			this.rte.ui.update();
		}
	}
	
	this.update = function() {
		if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^TR$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

elRTE.prototype.ui.prototype.buttons.tbrowafter = elRTE.prototype.ui.prototype.buttons.tbrowbefore;
/**
 * @class button - remove table row
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
elRTE.prototype.ui.prototype.buttons.tbrowrm = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.command = function() {
		
		var n  = this.rte.selection.getNode(),
			c  = this.rte.dom.selfOrParent(n, /^(TD|TH)$/),
			r  = this.rte.dom.selfOrParent(c, /^TR$/),
			tb = this.rte.dom.selfOrParent(c, /^TABLE$/),
			mx = this.rte.dom.tableMatrix(tb);
		
		if (c && r && mx.length) {
			this.rte.history.add();
			if (mx.length==1) {
				$(tb).remove();
				return this.rte.ui.update();
			}
			var mdf = [];
			var ro  = $(r).prevAll('tr').length;
			
			function _find(x, y) {
				while (y>0) {
					y--;
					if (mx[y] && mx[y][x] && mx[y][x].nodeName) {
						return mx[y][x];
					}
				}
			}
			
			// move cell with rowspan>1 to next row
			function _move(cell, x) {
				y = ro+1;
				var sibling= null;
				if (mx[y]) {
					for (var _x=0; _x<x; _x++) {
						if (mx[y][_x] && mx[y][_x].nodeName) {
							sibling = mx[y][_x];
						}
					};
					
					cell = cell.remove();
					if (sibling) {
						cell.insertAfter(sibling);
					} else {
						cell.prependTo($(r).next('tr').eq(0));
					}
				}
			}
			
			function _cursorPos(column) {
				for (var i = 0; i<column.length; i++) {
					if (column[i] == c) {
						return i<column.length-1 ? column[i+1] : column[i-1];
					}
				}
			}
			
			for (var i=0; i<mx[ro].length; i++) {
				var cell = null;
				var move = false;
				if (mx[ro][i] && mx[ro][i].nodeName) {
					cell = mx[ro][i];
					move = true;
				} else if (mx[ro][i] == '-' && (cell = _find(i, ro))) {
					move = false;
				}
				if (cell) {
					cell = $(cell);
					var rowspan = parseInt(cell.attr('rowspan')||1);
					if (rowspan>1) {
						cell.attr('rowspan', rowspan-1);
						move && _move(cell, i, ro);
					} 
				}
			};
			
			var _c = _cursorPos(this.rte.dom.tableColumn(c));
			if (_c) {
				this.rte.selection.selectContents(_c).collapse(true);
			}

			$(r).remove();
		}
		this.rte.ui.update();
	}
	
	this.update = function() {
		if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^TR$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

})(jQuery);/**
 * @class кнопка - отмена повтор действий
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки 
 * 
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
(function($) {
	elRTE.prototype.ui.prototype.buttons.undo = function(rte, name) {
		this.constructor.prototype.constructor.call(this, rte, name);
	
		this.command = function() {
			if (this.name == 'undo' && this.rte.history.canBack()) {
				this.rte.history.back();
				this.rte.ui.update();
			} else if (this.name == 'redo' && this.rte.history.canFwd()) {
				this.rte.history.fwd();
				this.rte.ui.update();
			}
		}
	
		this.update = function() {
			this.domElem.toggleClass('disabled', this.name == 'undo' ? !this.rte.history.canBack() : !this.rte.history.canFwd());
		}
	}

	elRTE.prototype.ui.prototype.buttons.redo = elRTE.prototype.ui.prototype.buttons.undo;

})(jQuery);/**
 * @class button - remove link
 *
 * @param  elRTE  rte   объект-редактор
 * @param  String name  название кнопки
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru 
 **/
(function($) {

	elRTE.prototype.ui.prototype.buttons.unlink = function(rte, name) {
		this.constructor.prototype.constructor.call(this, rte, name);

		this.command = function() {

			var n = this.rte.selection.getNode(), 
				l = this.rte.dom.selfOrParentLink(n);

			function isLink(n) { return n.nodeName == 'A' && n.href; }

			if (!l) {

				var sel = $.browser.msie ? this.rte.selection.selected() : this.rte.selection.selected({wrap : false});
				if (sel.length) {
					for (var i=0; i < sel.length; i++) {
						if (isLink(sel[i])) {
							l = sel[i];
							break;
						}
					};
					if (!l) {
						l = this.rte.dom.parent(sel[0], isLink) || this.rte.dom.parent(sel[sel.length-1], isLink);
					}
				}
			}

			if (l) {
				this.rte.history.add();
				this.rte.selection.select(l);
				this.rte.doc.execCommand('unlink', false, null);
				this.rte.ui.update(true);
			}
		
		}
	
		this.update = function() {
			var n = this.rte.selection.getNode();
			if (this.rte.dom.selfOrParentLink(n)) {
				this.domElem.removeClass('disabled').addClass('active');
			} else if (this.rte.dom.selectionHas(function(n) { return n.nodeName == 'A' && n.href; })) {
				this.domElem.removeClass('disabled').addClass('active');
			} else {
				this.domElem.addClass('disabled').removeClass('active');
			}
		}
	}

})(jQuery);

// SyntaxHighligher code button
(function($) {
elRTE.prototype.ui.prototype.buttons.code = function(rte, name) {
    this.constructor.prototype.constructor.call(this, rte, name);
    var self = this;
    var opts = {
        tpl      : '<pre class="brush:%val">%label</span>',
        select   : function(v) { self.set(v); },
        src      : {
            'code':     'Code',
            'as3':      'ActionScript 3',
            'bash':     'Bash',
            'cpp':      'C++',
            'csharp':   'C#',
            'css':      'CSS',
            'delphi':   'Delphi',
            'diff':     'Diff',
            'erlang':   'Erlang',
            'groovy':   'Groovy',
            'java':     'Java',
            'javafx':   'JavaFX',
            'jscript':  'JavaScript',
            'perl':     'Perl',
            'php':      'PHP',
            'plain':    'Plain text',
            'python':   'Python',
            'ruby':     'Ruby',
            'scala':    'Scala',
            'vb':       'VB',
            'xml':      'XML'
        }
    }

    this.select = this.domElem.elSelect(opts);

    this.command = function() {
    }

    this.set = function(brush) {
        this.rte.history.add();
        var nodes = this.rte.selection.selected({filter : 'textContainsNodes'});
        //nodes.wrapAll('<pre class="brush:'+brush+'" />');
        //nodes.wrap('<pre class="brush:'+brush+'" />');
        $.each(nodes, function() {
            //$this = /^(THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(this.nodeName) ? $(this).find('td,th') : $(this);
            if (brush == 'code') {
                if ($(this).parent().is('pre')) {
                    $(this).unwrap();
                }
            } else {
                $(this).wrap('<pre class="brush:'+brush+'"/>');
            }
        });
        this.rte.ui.update();
    }

    this.update = function() {
        this.domElem.removeClass('disabled');
        var n = this.rte.selection.getNode();
        if (n.nodeType != 1) {
            n = n.parentNode;
        }
        var v = $(n).attr('class');
        var m = /brush:(\w+)/gi.match(v);
        v = m[1];
        this.select.val(opts.src[v] ? v : '');
    }
}
})(jQuery);
elRTE.prototype.options.buttons.code = 'Code';