(function($)
{
	$.Redactor.prototype.source = function()
	{
		return {
			init: function()
			{
				var button = this.button.addFirst('html', 'HTML');
				this.button.setIcon(button, '<i class="re-icon-html"></i>');
				this.button.addCallback(button, this.source.toggle);

				var style = {
					'width': '100%',
					'margin': '0',
					'min-height' : '800px',
					'background': '#1d1d1d',
					'box-sizing': 'border-box',
					'color': '#ccc',
					'font-size': '15px',
					'outline': 'none',
					'padding': '20px',
					'font-family': 'Consolas, Menlo, Monaco, "Courier New", monospace'
				};

				this.source.$textarea = $('<textarea />');
				this.source.$textarea.css(style).hide();

				if (this.opts.type === 'textarea')
				{
					this.core.box().append(this.source.$textarea);
				}
				else
				{
					this.core.box().after(this.source.$textarea);
				}

				this.core.element().on('destroy.callback.redactor', $.proxy(function()
				{
					this.source.$textarea.remove();

				}, this));

			},
			toggle: function()
			{
				return (this.source.$textarea.hasClass('open')) ? this.source.hide() : this.source.show();
			},
			setCaretOnShow: function()
			{
				this.source.offset = this.offset.get();
				var scroll = $(window).scrollTop();

				var	width = this.core.editor().innerWidth();
				var height = this.core.editor().innerHeight();

				// caret position sync
				this.source.start = 0;
				this.source.end = 0;
				var $editorDiv = $("<div/>").append($.parseHTML(this.core.editor().html(), document, true));
				var $selectionMarkers = $editorDiv.find("span.redactor-selection-marker");

				if ($selectionMarkers.length > 0)
				{
					var editorHtml = $editorDiv.html().replace(/&amp;/g, '&');

					if ($selectionMarkers.length === 1)
					{
						this.source.start = this.utils.strpos(editorHtml, $editorDiv.find("#selection-marker-1").prop("outerHTML"));
						this.source.end = this.source.start;
					}
					else if ($selectionMarkers.length === 2)
					{
						this.source.start = this.utils.strpos(editorHtml, $editorDiv.find("#selection-marker-1").prop("outerHTML"));
						this.source.end = this.utils.strpos(editorHtml, $editorDiv.find("#selection-marker-2").prop("outerHTML")) - $editorDiv.find("#selection-marker-1").prop("outerHTML").toString().length;
					}
				}
			},
			setCaretOnHide: function(html)
			{
			    this.source.start = this.source.$textarea.get(0).selectionStart;
				this.source.end = this.source.$textarea.get(0).selectionEnd;

				// if selection starts from end
				if (this.source.start > this.source.end && this.source.end > 0)
				{
					var tempStart = this.source.end;
					var tempEnd = this.source.start;

					this.source.start = tempStart;
					this.source.end = tempEnd;
				}

				this.source.start = this.source.enlargeOffset(html, this.source.start);
				this.source.end = this.source.enlargeOffset(html, this.source.end);

				html = html.substr(0, this.source.start) + this.marker.html(1) + html.substr(this.source.start);

				if (this.source.end > this.source.start)
				{
					var markerLength = this.marker.html(1).toString().length;

					html = html.substr(0, this.source.end + markerLength) + this.marker.html(2) + html.substr(this.source.end + markerLength);
				}


				return html;

			},
			hide: function()
			{
				this.source.$textarea.removeClass('open').hide();
				this.source.$textarea.off('.redactor-source');

				var code = this.source.$textarea.val();

				code = this.paragraphize.load(code);
				code = this.source.setCaretOnHide(code);

				this.code.start(code);
				this.button.enableAll();
				this.core.editor().show().focus();
				this.selection.restore();

                this.core.callback('visual');
			},
			show: function()
			{
				this.selection.save();
				this.source.setCaretOnShow();

				var height = this.core.editor().height();
				var code = this.code.get();

                // callback
                code = this.core.callback('source', code);

				this.core.editor().hide();
				this.button.disableAll('html');

				this.source.$textarea.val(code).height(height).addClass('open').show();
				this.source.$textarea.on('keyup.redactor-source', $.proxy(function()
				{
					if (this.opts.type === 'textarea')
					{
						this.core.textarea().val(this.source.$textarea.val());
					}

				}, this));

				this.marker.remove();

				$(window).scrollTop(scroll);

				if (this.source.$textarea[0].setSelectionRange)
				{
					this.source.$textarea[0].setSelectionRange(this.source.start, this.source.end);
				}

				this.source.$textarea[0].scrollTop = 0;

				setTimeout($.proxy(function()
				{
					this.source.$textarea.focus();

				}, this), 0);
			},
			enlargeOffset: function(html, offset)
			{
				var htmlLength = html.length;
				var c = 0;

				if (html[offset] === '>')
				{
					c++;
				}
				else
				{
					for(var i = offset; i <= htmlLength; i++)
					{
						c++;

						if (html[i] === '>')
						{
							break;
						}
						else if (html[i] === '<' || i === htmlLength)
						{
							c = 0;
							break;
						}
					}
				}

				return offset + c;
			}
		};
	};
})(jQuery);