// ----------------------------------------------------------------------------
// markItUp! Universal MarkUp Engine, JQuery plugin
// v 1.1.5
// Dual licensed under the MIT and GPL licenses.
// ----------------------------------------------------------------------------
// Copyright (C) 2007-2008 Jay Salvat
// http://markitup.jaysalvat.com/
// ----------------------------------------------------------------------------
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ----------------------------------------------------------------------------
(function($) {
	$.fn.markItUp = function(settings, extraSettings) {
		var options, ctrlKey, shiftKey, altKey;
		ctrlKey = shiftKey = altKey = false;

		options = {
			className:		'',
			root:			'',
			previewInWindow:	'', // 'width=800, height=600, resizable=yes, scrollbars=yes'
			previewAutoRefresh:	true,
			previewPosition:	'after',
			previewTemplatePath:	'~/templates/preview.html',
			previewParserPath:	'',
			previewParserVar:	'data',
			resizeHandle:		true,
			beforeInsert:		'',
			afterInsert:		'',
			onEnter:		{},
			onShiftEnter:		{},
			onCtrlEnter:		{},
			onTab:			{},
			markupSet:		[ { /* set */ } ]
		};
		$.extend(options, settings, extraSettings);

		// compute markItUp! path
		if (!options.root) {
			$('script').each(function(a, tag) {
				miuScript = $(tag).get(0).src.match(/(.*)jquery\.markitup(\.pack)?\.js$/);
				if (miuScript !== null) {
					options.root = miuScript[1];
				}
			});
		}

		return this.each(function() {
			var $$, textarea, selectedText, selectionStart, scrollPosition, hash, header, footer, previewWindow, template, iFrame, abort;
			$$ = $(this);
			textarea = this;
			abort = false;
			scrollPosition = selectionStart = 0;

			options.previewParserPath = localize(options.previewParserPath);
			options.previewTemplatePath = localize(options.previewTemplatePath);

			// apply the computed path to ~/
			function localize(data, inText) {
				if (inText) {
					return data.replace(/("|')~\//g, "$1" + options.root);
				}
				return data.replace(/^~\//, options.root);
			}

			// init and build editor
			function init() {
				className = '';
				if (options.className) {
					className = ' class="' + options.className + '"';
				}
				$$.wrap('<div' + className + '></div>');
				$$.wrap('<div class="markItUp"></div>');
				$$.wrap('<div class="markItUpContainer"></div>');
				$$.addClass("markItUpEditor");

				// add the header before the textarea
				header = $('<div class="markItUpHeader"></div>').insertBefore($$);
				$(dropMenus(options.markupSet)).appendTo(header);

				// add the footer after the textarea
				footer = $('<div class="markItUpFooter"></div>').insertAfter($$);

				// add the resize handle after textarea
				if (options.resizeHandle === true && $.browser.safari !== true) {
					resizeHandle = $('<div class="markItUpResizeHandle"></div>')
						.insertAfter($$)
						.bind("mousedown", function(e) {
							var h = $$.height(), y = e.clientY, mouseMove, mouseUp;
							mouseMove = function(e) {
								$$.css("height", Math.max(20, e.clientY + h - y) + "px");
								return false;
							};
							mouseUp = function(e) {
								$("html").unbind("mousemove", mouseMove).unbind("mouseup", mouseUp);
								return false;
							};
							$("html").bind("mousemove", mouseMove).bind("mouseup", mouseUp);
						});
					footer.append(resizeHandle);
				}

				// listen key events
				$$.keydown(keyPressed).keyup(keyPressed);

				// bind an event to catch external calls
				$$.bind("insertion", function(e, settings) {
					if (settings.target !== false) {
						getSelection();
					}
					if (textarea === $.markItUp.focused) {
						markup(settings);
					}
				});

				// remember the last focus
				$$.focus(function() {
					$.markItUp.focused = this;
				});
			}

			// recursively build header with dropMenus from markup set
			function dropMenus(markupSet) {
				var ul = $('<ul></ul>'), i = 0;
				$('li:hover > ul', ul).css('display', 'block');
				$.each(markupSet, function() {
					var button = this, title, li, j;
					title = (button.key) ? (button.name || '') + ' [Ctrl+' + button.key + ']' : (button.name || '');
					key   = (button.key) ? 'accesskey="' + button.key + '"' : '';
					if (button.separator) {
						li = $('<li class="markItUpSeparator">&nbsp;</li>').appendTo(ul);
					} else {
						i++;
						li = $('<li class="markItUpButton ' + (button.className || '') + '"><a href="" ' + key + ' title="' + title + '">' + (button.name || '') + '</a></li>')
						.bind("contextmenu", function() { // prevent contextmenu on mac and allow ctrl+click
							return false;
						}).click(function() {
							return false;
						}).mouseup(function() {
							if (button.call) {
								eval(button.call)();
							}
							markup(button);
							return false;
						}).hover(function() {
							$('> ul', this).show();
							$(document).one('click', function() { // close dropmenu if click outside
								$('ul ul', header).hide();
							});
						}, function() {
							$('> ul', this).hide();
						}).appendTo(ul);
						if (button.dropMenu) {
							$(li).addClass('markItUpDropMenu').append(dropMenus(button.dropMenu));
						}
					}
				});
				return ul;
			}

			// markItUp! markups
			function magicMarkups(string) {
				if (string) {
					string = string.toString();
					string = string.replace(/\(\!\(([\s\S]*?)\)\!\)/g, function(x, a) {
						var b = a.split('|!|');
						if (altKey === true) {
							return (b[1] !== undefined) ? b[1] : b[0];
						} else {
							return (b[1] === undefined) ? "" : b[0];
						}
					});
					// [![prompt]!], [![prompt:!:value]!]
					string = string.replace(/\[\!\[([\s\S]*?)\]\!\]/g, function(x, a) {
						var b = a.split(':!:');
						if (abort === true) {
							return false;
						}
						value = prompt(b[0], (b[1]) ? b[1] : '');
						if (value === null) {
							abort = true;
						}
						return value;
					});
					return string;
				}
				return "";
			}

			// prepare action
			function prepare(action) {
				if ($.isFunction(action)) {
					action = action(hash);
				}
				return magicMarkups(action);
			}

			// define markup to insert
			function markup(button) {
				var start, len, n, i;
				hash = button;
				getSelection();

				$.extend(hash, {
					line: "",
					root: options.root,
					textarea: textarea,
					selectedText: selectedText || '',
					selectionStart: selectionStart,
					select: select,
					ctrlKey: ctrlKey,
					shiftKey: shiftKey,
					altKey: altKey
				});

				// callbacks before insertion
				prepare(options.beforeInsert);
				prepare(button.beforeInsert);
				if (ctrlKey === true && shiftKey === true) {
					prepare(button.beforeMultiInsert);
				}
				$.extend(hash, {
					line: 1
				});

				var openWith = prepare(button.openWith);
				var placeHolder = prepare(button.placeHolder);
				var replaceWith = prepare(button.replaceWith);
				var closeWith = prepare(button.closeWith);
				var string;

				if (button.multiLine === true) {
					lines = selectedText.split(/(\r\n|\r|\n)/);
					for (i = 0; i < lines.length; i++) {
						if ($.trim(lines[i]) !== '') {
							$.extend(hash, {
								line: i + 1,
								selectedText: lines[i]
							});
							lines[i] = openWith + lines[i] + closeWith;
						} else {
							lines[i] = "";
						}
					}

					string = lines.join('\r\n');

					start = selectionStart;
					len = string.replace(/(\r\n|\r|\n)/g, '\n').length;
				} else {
					if (replaceWith !== "") {
						string = replaceWith;
					} else if (selectedText === '' && placeHolder !== '') {
						string = placeHolder;
					} else {
						string = selectedText;
					}

					start = selectionStart + openWith.replace(/(\r\n|\r|\n)/g, '\n').length;
					len = string.replace(/(\r\n|\r|\n)/g, '\n').length;

					string = openWith + string + closeWith;
				}

				if (selectedText === '' && replaceWith === '') {
					start = selectionStart + openWith.replace(/(\r\n|\r|\n)/g, '\n').length;
					len = 0;
				}

				$.extend(hash, {
					selectionStart: selectionStart,
					scrollPosition: scrollPosition
				});

				if (string !== selectedText && abort === false) {
					insert(string);
					select(start, len);
				}
				getSelection();

				$.extend(hash, {
					line: '',
					selectedText: selectedText
				});

				// callbacks after insertion
				if (ctrlKey === true && shiftKey === true) {
					prepare(button.afterMultiInsert);
				}
				prepare(button.afterInsert);
				prepare(options.afterInsert);

				// refresh preview if opened
				if (previewWindow && options.previewAutoRefresh) {
					refreshPreview();
				}

				// reinit keyevent
				shiftKey = altKey = ctrlKey = abort = false;
			}

			// add markup
			function insert(string) {
				if (document.selection) {
					var newSelection = document.selection.createRange();
					newSelection.text = string;
				} else {
					$$.val($$.val().substring(0, selectionStart) + string + $$.val().substring(selectionStart + selectedText.length, $$.val().length));
				}
			}

			// select text
			function select(start, len) {
				if (textarea.createTextRange) {
					// quick fix to make it work on Opera 9.5
					if ($.browser.opera && $.browser.version >= 9.5 && len == 0) {
						return false;
					}
					range = textarea.createTextRange();
					range.collapse(true);
					range.moveStart('character', start);
					range.moveEnd('character', len);
					range.select();
				} else if (textarea.setSelectionRange) {
					textarea.setSelectionRange(start, start + len);
				}
				textarea.scrollTop = scrollPosition;
				textarea.focus();
			}

			// get the selection
			function getSelection() {
				textarea.focus();

				scrollPosition = textarea.scrollTop;
				if (document.selection) {
					selectedText = document.selection.createRange().text;
					if ($.browser.msie) { // ie
						var range = document.selection.createRange(), rangeCopy = range.duplicate();
						rangeCopy.moveToElementText(textarea);
						selectionStart = -1;
						while (rangeCopy.inRange(range)) { // fix most of the ie bugs with returns...
							rangeCopy.moveStart('character');
							selectionStart++;
						}
					} else { // opera
						selectionStart = textarea.selectionStart;
					}
				} else { // gecko
					selectionStart = textarea.selectionStart;
					selectedText = $$.val().substring(selectionStart, textarea.selectionEnd);
				}
				return selectedText;
			}

			// open preview window
			function preview() {
				if (!previewWindow || previewWindow.closed) {
					if (options.previewInWindow) {
						previewWindow = window.open('', 'preview', options.previewInWindow);
					} else {
						iFrame = $('<iframe class="markItUpPreviewFrame"></iframe>');
						if (options.previewPosition == 'after') {
							iFrame.insertAfter(footer);
						} else {
							iFrame.insertBefore(header);
						}
						previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
					}
				} else if (altKey === true) {
					if (iFrame) {
						iFrame.remove();
					}
					previewWindow.close();
					previewWindow = iFrame = false;
				}
				if (!options.previewAutoRefresh) {
					refreshPreview();
				}
			}

			// refresh Preview window
			function refreshPreview() {
				if (previewWindow.document) {
					try {
						sp = previewWindow.document.documentElement.scrollTop
					} catch(e) {
						sp = 0;
					}
					previewWindow.document.open();
					previewWindow.document.write(renderPreview());
					previewWindow.document.close();
					previewWindow.document.documentElement.scrollTop = sp;
				}
				if (options.previewInWindow) {
					previewWindow.focus();
				}
			}

			function renderPreview() {
				if (options.previewParserPath !== '') {
					$.ajax( {
						type: 'POST',
						async: false,
						url: options.previewParserPath,
						data: options.previewParserVar + '=' + encodeURIComponent($$.val()),
						success: function(data) {
							phtml = localize(data, 1);
						}
					} );
				} else {
					if (!template) {
						$.ajax( {
							async: false,
							url: options.previewTemplatePath,
							success: function(data) {
								template = localize(data, 1);
							}
						} );
					}
					phtml = template.replace(/<!-- content -->/g, $$.val());
				}
				return phtml;
			}

			// set keys pressed
			function keyPressed(e) {
				shiftKey = e.shiftKey;
				altKey = e.altKey;
				ctrlKey = e.ctrlKey && !e.altKey;

				if (e.type === 'keydown') {
					if (ctrlKey === true) {
						li = $("a[accesskey=" + String.fromCharCode(e.keyCode) + "]", header).parent('li');
						if (li.length !== 0) {
							ctrlKey = false;
							li.triggerHandler('mouseup');
							return false;
						}
					}
					if (e.keyCode === 13 || e.keyCode === 10) { // Enter key
						if (ctrlKey === true) { // Enter + Ctrl
							ctrlKey = false;
							markup(options.onCtrlEnter);
							return options.onCtrlEnter.keepDefault;
						} else if (shiftKey === true) { // Enter + Shift
							shiftKey = false;
							markup(options.onShiftEnter);
							return options.onShiftEnter.keepDefault;
						} else { // only Enter
							markup(options.onEnter);
							return options.onEnter.keepDefault;
						}
					}
					if (e.keyCode === 9) { // Tab key
						if (shiftKey == true || ctrlKey == true || altKey == true) { // Thx Dr Floob.
							return false;
						}

						markup(options.onTab);
						return options.onTab.keepDefault;
					}
				}
			}

			init();
		});
	};

	$.fn.markItUpRemove = function() {
		return this.each(function() {
			$$ = $(this).unbind().removeClass('markItUpEditor');
			$$.parent('div').parent('div.markItUp').parent('div').replaceWith($$);
		});
	};

	$.markItUp = function(settings) {
		var options = {
			target: false
		};
		$.extend(options, settings);
		if (options.target) {
			return $(options.target).each(function() {
				$(this).focus();
				$(this).trigger('insertion', [options]);
			});
		} else {
			$('textarea').trigger('insertion', [options]);
		}
	};
})(jQuery);
