text: tinymce.PluginManager.add('repeater', function(editor, url) {
  	function getTag(name) {
  		var $e = jq$(editor.selection.getNode());
  		if (typeof name != 'string')
  			return $e;
  		return $e.is(name) ? $e : $e.parents(name).eq(0);
  	}
  	function isSelectionEmpty(result) {
  		return !(result.text = editor.selection.getContent({ 'format': 'html' }));
  	}
  
  	function isRepeaterTag() {
  		return editor.selection.getNode().tagName.toLowerCase() == "repeater";
  	}
  	function addRepeaterTag(text) {
  		var $node = jq$(editor.selection.getNode()),
  			id = new Date().getTime().toString(36).toLowerCase();
  		editor.execCommand('mceReplaceContent', false, '<repeater id=' + id + '>' + text + '</repeater>');
  		select($node.find("#" + id).removeAttr("id")[0]);
  
  		// can be done in this way, but caret position will be incorrect
  		//var repeater = document.createElement("repeater");
  		//editor.selection.getRng().surroundContents(repeater);
  		//select(repeater);
  		//editor.undoManager.add();
  	}
  	function removeRepeaterTag() {
  		select();
  		editor.execCommand('mceRemoveNode', false, editor.selection.getNode());
  	}
  	function insertRepeaterTag() {
  		editor.windowManager.open({
  			title: fdtPleaseInputText,
  			body: [{ type: 'textbox', name: 'description', label: 'Text' }],
  			onsubmit: function(e) { editor.execCommand('mceInsertContent', true, '<repeater>' + e.data.description + '</repeater>'); }
  		});
  	}
  
  	function activate(fn) {
  		return function() {
  			var self = this,
  				fns = function(e) { self.active(fn()); };
  			editor.on('NodeChange', fns);
  			editor.selection && fns();
  		};
  	}
  	function disable(fn) {
  		return function() {
  			var self = this,
  				fns = function(e) { self.disabled(fn()); };
  			editor.on('NodeChange', fns);
  			editor.selection && fns();
  		};
  	}
  
  	function isRepeated(name) {
  		return getTag(name).is(".iz-repeater");
  	}
  	function repeate(name) {
  		getTag(name).addClass("iz-repeater");
  	}
  	function leaveAlone(name) {
  		var tag = getTag(name);
  		tag.removeClass("iz-repeater");
  		if (!tag.attr("class"))
  			tag.removeAttr("class");
  	}
  	function select(element) {
  		editor.execCommand('mceSelectNode', true, utility.isNode(element) ? element : getTag(element)[0]);
  	}
  
  	editor.addButton('repeater-default', {
  		type: 'splitbutton',
  		text: fdtRepeater,
  		tooltip: fdtPlaceRemoveRepeater,
  		onclick: function() {
  			if (!isRepeaterTag()) {
  				var result = {};
  				if (!isSelectionEmpty(result))
  					addRepeaterTag(result.text);
  				else
  					insertRepeaterTag();
  			}
  			else {
  				removeRepeaterTag();
  			}
  		},
  		onPostRender: activate(function() { return isRepeaterTag(); }),
  		menu: [
  			{
  				text: fdtAdd,
  				onclick: function() {
  					var result = {};
  					if (!isSelectionEmpty(result))
  						addRepeaterTag(result.text);
  				},
  				onPostRender: disable(function() { return isSelectionEmpty({}); })
  			},
  			{
  				text: fdtInsert,
  				onclick: insertRepeaterTag
  			},
  			{
  				text: fdtRemove,
  				onclick: function() {
  					if (isRepeaterTag())
  						removeRepeaterTag();
  				},
  				onPostRender: disable(function() { return !isRepeaterTag(); })
  			}
  		]
  	});
  	editor.addButton('repeater-adv', {
  		type: 'splitbutton',
  		icon: 'code',
  		tooltip: fdtUseRemoveRepeater,
  		onclick: function() {
  			if (!isRepeated()) {
  				repeate();
  				select();
  			}
  			else {
  				leaveAlone();
  				select();
  			}
  		},
  		onPostRender: activate(function() { return isRepeated(); }),
  		menu: [
  			{
  				text: fdtAdd,
  				onclick: function() {
  					repeate();
  					select();
  				},
  				onPostRender: disable(function() { return isRepeated(); })
  			},
  			{
  				text: fdtRemove,
  				onclick: function() {
  					if (isRepeated()) {
  						leaveAlone();
  						select();
  					}
  				},
  				onPostRender: disable(function() { return !isRepeated(); })
  			}
  		]
  	});
  });