openrat-cms

# OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs

jquery.markitup.min.js (18300B)


      1 // ----------------------------------------------------------------------------
      2 // markItUp! Universal MarkUp Engine, JQuery plugin
      3 // v 1.1.x
      4 // Dual licensed under the MIT and GPL licenses.
      5 // ----------------------------------------------------------------------------
      6 // Copyright (C) 2007-2011 Jay Salvat
      7 // http://markitup.jaysalvat.com/
      8 // ----------------------------------------------------------------------------
      9 // Permission is hereby granted, free of charge, to any person obtaining a copy
     10 // of this software and associated documentation files (the "Software"), to deal
     11 // in the Software without restriction, including without limitation the rights
     12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     13 // copies of the Software, and to permit persons to whom the Software is
     14 // furnished to do so, subject to the following conditions:
     15 // 
     16 // The above copyright notice and this permission notice shall be included in
     17 // all copies or substantial portions of the Software.
     18 // 
     19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     25 // THE SOFTWARE.
     26 // ----------------------------------------------------------------------------
     27 (function($) {
     28 	$.fn.markItUp = function(settings, extraSettings) {
     29 		var options, ctrlKey, shiftKey, altKey;
     30 		ctrlKey = shiftKey = altKey = false;
     31 	
     32 		options = {	id:						'',
     33 					nameSpace:				'',
     34 					root:					'',
     35 					previewInWindow:		'', // 'width=800, height=600, resizable=yes, scrollbars=yes'
     36 					previewAutoRefresh:		true,
     37 					previewPosition:		'after',
     38 					previewTemplatePath:	'~/templates/preview.html',
     39 					previewParser:			false,
     40 					previewParserPath:		'',
     41 					previewParserVar:		'data',
     42 					resizeHandle:			true,
     43 					beforeInsert:			'',
     44 					afterInsert:			'',
     45 					onEnter:				{},
     46 					onShiftEnter:			{},
     47 					onCtrlEnter:			{},
     48 					onTab:					{},
     49 					markupSet:			[	{ /* set */ } ]
     50 				};
     51 		$.extend(options, settings, extraSettings);
     52 
     53 		// compute markItUp! path
     54 		if (!options.root) {
     55 			$('script').each(function(a, tag) {
     56 				miuScript = $(tag).get(0).src.match(/(.*)jquery\.markitup(\.pack)?\.js$/);
     57 				if (miuScript !== null) {
     58 					options.root = miuScript[1];
     59 				}
     60 			});
     61 		}
     62 
     63 		return this.each(function() {
     64 			var $$, textarea, levels, scrollPosition, caretPosition, caretOffset,
     65 				clicked, hash, header, footer, previewWindow, template, iFrame, abort;
     66 			$$ = $(this);
     67 			textarea = this;
     68 			levels = [];
     69 			abort = false;
     70 			scrollPosition = caretPosition = 0;
     71 			caretOffset = -1;
     72 
     73 			options.previewParserPath = localize(options.previewParserPath);
     74 			options.previewTemplatePath = localize(options.previewTemplatePath);
     75 
     76 			// apply the computed path to ~/
     77 			function localize(data, inText) {
     78 				if (inText) {
     79 					return 	data.replace(/("|')~\//g, "$1"+options.root);
     80 				}
     81 				return 	data.replace(/^~\//, options.root);
     82 			}
     83 
     84 			// init and build editor
     85 			function init() {
     86 				id = ''; nameSpace = '';
     87 				if (options.id) {
     88 					id = 'id="'+options.id+'"';
     89 				} else if ($$.attr("id")) {
     90 					id = 'id="markItUp'+($$.attr("id").substr(0, 1).toUpperCase())+($$.attr("id").substr(1))+'"';
     91 
     92 				}
     93 				if (options.nameSpace) {
     94 					nameSpace = 'class="'+options.nameSpace+'"';
     95 				}
     96 				$$.wrap('<div '+nameSpace+'></div>');
     97 				$$.wrap('<div '+id+' class="markItUp"></div>');
     98 				$$.wrap('<div class="markItUpContainer"></div>');
     99 				$$.addClass("markItUpEditor");
    100 
    101 				// add the header before the textarea
    102 				header = $('<div class="markItUpHeader"></div>').insertBefore($$);
    103 				$(dropMenus(options.markupSet)).appendTo(header);
    104 
    105 				// add the footer after the textarea
    106 				footer = $('<div class="markItUpFooter"></div>').insertAfter($$);
    107 
    108 				// add the resize handle after textarea
    109 				if (options.resizeHandle === true && $.browser.safari !== true) {
    110 					resizeHandle = $('<div class="markItUpResizeHandle"></div>')
    111 						.insertAfter($$)
    112 						.bind("mousedown", function(e) {
    113 							var h = $$.height(), y = e.clientY, mouseMove, mouseUp;
    114 							mouseMove = function(e) {
    115 								$$.css("height", Math.max(20, e.clientY+h-y)+"px");
    116 								return false;
    117 							};
    118 							mouseUp = function(e) {
    119 								$("html").unbind("mousemove", mouseMove).unbind("mouseup", mouseUp);
    120 								return false;
    121 							};
    122 							$("html").bind("mousemove", mouseMove).bind("mouseup", mouseUp);
    123 					});
    124 					footer.append(resizeHandle);
    125 				}
    126 
    127 				// listen key events
    128 				$$.keydown(keyPressed).keyup(keyPressed);
    129 				
    130 				// bind an event to catch external calls
    131 				$$.bind("insertion", function(e, settings) {
    132 					if (settings.target !== false) {
    133 						get();
    134 					}
    135 					if (textarea === $.markItUp.focused) {
    136 						markup(settings);
    137 					}
    138 				});
    139 
    140 				// remember the last focus
    141 				$$.focus(function() {
    142 					$.markItUp.focused = this;
    143 				});
    144 			}
    145 
    146 			// recursively build header with dropMenus from markupset
    147 			function dropMenus(markupSet) {
    148 				var ul = $('<ul></ul>'), i = 0;
    149 				$('li:hover > ul', ul).css('display', 'block');
    150 				$.each(markupSet, function() {
    151 					var button = this, t = '', title, li, j;
    152 					title = (button.key) ? (button.name||'')+' [Ctrl+'+button.key+']' : (button.name||'');
    153 					key   = (button.key) ? 'accesskey="'+button.key+'"' : '';
    154 					if (button.separator) {
    155 						li = $('<li class="markItUpSeparator">'+(button.separator||'')+'</li>').appendTo(ul);
    156 					} else {
    157 						i++;
    158 						for (j = levels.length -1; j >= 0; j--) {
    159 							t += levels[j]+"-";
    160 						}
    161 						li = $('<li class="markItUpButton markItUpButton'+t+(i)+' '+(button.className||'')+'"><a href="" '+key+' title="'+title+'">'+(button.name||'')+'</a></li>')
    162 						.bind("contextmenu", function() { // prevent contextmenu on mac and allow ctrl+click
    163 							return false;
    164 						}).click(function() {
    165 							return false;
    166 						}).bind("focusin", function(){
    167                             $$.focus();
    168 						}).mouseup(function() {
    169 							if (button.call) {
    170 								eval(button.call)();
    171 							}
    172 							setTimeout(function() { markup(button) },1);
    173 							return false;
    174 						}).hover(function() {
    175 								$('> ul', this).show();
    176 								$(document).one('click', function() { // close dropmenu if click outside
    177 										$('ul ul', header).hide();
    178 									}
    179 								);
    180 							}, function() {
    181 								$('> ul', this).hide();
    182 							}
    183 						).appendTo(ul);
    184 						if (button.dropMenu) {
    185 							levels.push(i);
    186 							$(li).addClass('markItUpDropMenu').append(dropMenus(button.dropMenu));
    187 						}
    188 					}
    189 				}); 
    190 				levels.pop();
    191 				return ul;
    192 			}
    193 
    194 			// markItUp! markups
    195 			function magicMarkups(string) {
    196 				if (string) {
    197 					string = string.toString();
    198 					string = string.replace(/\(\!\(([\s\S]*?)\)\!\)/g,
    199 						function(x, a) {
    200 							var b = a.split('|!|');
    201 							if (altKey === true) {
    202 								return (b[1] !== undefined) ? b[1] : b[0];
    203 							} else {
    204 								return (b[1] === undefined) ? "" : b[0];
    205 							}
    206 						}
    207 					);
    208 					// [![prompt]!], [![prompt:!:value]!]
    209 					string = string.replace(/\[\!\[([\s\S]*?)\]\!\]/g,
    210 						function(x, a) {
    211 							var b = a.split(':!:');
    212 							if (abort === true) {
    213 								return false;
    214 							}
    215 							value = prompt(b[0], (b[1]) ? b[1] : '');
    216 							if (value === null) {
    217 								abort = true;
    218 							}
    219 							return value;
    220 						}
    221 					);
    222 					return string;
    223 				}
    224 				return "";
    225 			}
    226 
    227 			// prepare action
    228 			function prepare(action) {
    229 				if ($.isFunction(action)) {
    230 					action = action(hash);
    231 				}
    232 				return magicMarkups(action);
    233 			}
    234 
    235 			// build block to insert
    236 			function build(string) {
    237 				var openWith 			= prepare(clicked.openWith);
    238 				var placeHolder 		= prepare(clicked.placeHolder);
    239 				var replaceWith 		= prepare(clicked.replaceWith);
    240 				var closeWith 			= prepare(clicked.closeWith);
    241 				var openBlockWith 		= prepare(clicked.openBlockWith);
    242 				var closeBlockWith 		= prepare(clicked.closeBlockWith);
    243 				var multiline 			= clicked.multiline;
    244 				
    245 				if (replaceWith !== "") {
    246 					block = openWith + replaceWith + closeWith;
    247 				} else if (selection === '' && placeHolder !== '') {
    248 					block = openWith + placeHolder + closeWith;
    249 				} else {
    250 					string = string || selection;
    251 
    252 					var lines = selection.split(/\r?\n/), blocks = [];
    253 					
    254 					for (var l=0; l < lines.length; l++) {
    255 						line = lines[l];
    256 						var trailingSpaces;
    257 						if (trailingSpaces = line.match(/ *$/)) {
    258 							blocks.push(openWith + line.replace(/ *$/g, '') + closeWith + trailingSpaces);
    259 						} else {
    260 							blocks.push(openWith + line + closeWith);
    261 						}
    262 					}
    263 					
    264 					block = blocks.join("\n");
    265 				}
    266 
    267 				block = openBlockWith + block + closeBlockWith;
    268 
    269 				return {	block:block, 
    270 							openWith:openWith, 
    271 							replaceWith:replaceWith, 
    272 							placeHolder:placeHolder,
    273 							closeWith:closeWith
    274 					};
    275 			}
    276 
    277 			// define markup to insert
    278 			function markup(button) {
    279 				var len, j, n, i;
    280 				hash = clicked = button;
    281 				get();
    282 				$.extend(hash, {	line:"", 
    283 						 			root:options.root,
    284 									textarea:textarea, 
    285 									selection:(selection||''), 
    286 									caretPosition:caretPosition,
    287 									ctrlKey:ctrlKey, 
    288 									shiftKey:shiftKey, 
    289 									altKey:altKey
    290 								}
    291 							);
    292 				// callbacks before insertion
    293 				prepare(options.beforeInsert);
    294 				prepare(clicked.beforeInsert);
    295 				if ((ctrlKey === true && shiftKey === true) || button.multiline === true) {
    296 					prepare(clicked.beforeMultiInsert);
    297 				}			
    298 				$.extend(hash, { line:1 });
    299 
    300 				if ((ctrlKey === true && shiftKey === true)) {
    301 					lines = selection.split(/\r?\n/);
    302 					for (j = 0, n = lines.length, i = 0; i < n; i++) {
    303 						if ($.trim(lines[i]) !== '') {
    304 							$.extend(hash, { line:++j, selection:lines[i] } );
    305 							lines[i] = build(lines[i]).block;
    306 						} else {
    307 							lines[i] = "";
    308 						}
    309 					}
    310 					string = { block:lines.join('\n')};
    311 					start = caretPosition;
    312 					len = string.block.length + (($.browser.opera) ? n-1 : 0);
    313 				} else if (ctrlKey === true) {
    314 					string = build(selection);
    315 					start = caretPosition + string.openWith.length;
    316 					len = string.block.length - string.openWith.length - string.closeWith.length;
    317 					len = len - (string.block.match(/ $/) ? 1 : 0);
    318 					len -= fixIeBug(string.block);
    319 				} else if (shiftKey === true) {
    320 					string = build(selection);
    321 					start = caretPosition;
    322 					len = string.block.length;
    323 					len -= fixIeBug(string.block);
    324 				} else {
    325 					string = build(selection);
    326 					start = caretPosition + string.block.length ;
    327 					len = 0;
    328 					start -= fixIeBug(string.block);
    329 				}
    330 				if ((selection === '' && string.replaceWith === '')) {
    331 					caretOffset += fixOperaBug(string.block);
    332 					
    333 					start = caretPosition + string.openWith.length;
    334 					len = string.block.length - string.openWith.length - string.closeWith.length;
    335 
    336 					caretOffset = $$.val().substring(caretPosition,  $$.val().length).length;
    337 					caretOffset -= fixOperaBug($$.val().substring(0, caretPosition));
    338 				}
    339 				$.extend(hash, { caretPosition:caretPosition, scrollPosition:scrollPosition } );
    340 
    341 				if (string.block !== selection && abort === false) {
    342 					insert(string.block);
    343 					set(start, len);
    344 				} else {
    345 					caretOffset = -1;
    346 				}
    347 				get();
    348 
    349 				$.extend(hash, { line:'', selection:selection });
    350 
    351 				// callbacks after insertion
    352 				if ((ctrlKey === true && shiftKey === true) || button.multiline === true) {
    353 					prepare(clicked.afterMultiInsert);
    354 				}
    355 				prepare(clicked.afterInsert);
    356 				prepare(options.afterInsert);
    357 
    358 				// refresh preview if opened
    359 				if (previewWindow && options.previewAutoRefresh) {
    360 					refreshPreview(); 
    361 				}
    362 																									
    363 				// reinit keyevent
    364 				shiftKey = altKey = ctrlKey = abort = false;
    365 			}
    366 
    367 			// Substract linefeed in Opera
    368 			function fixOperaBug(string) {
    369 				if ($.browser.opera) {
    370 					return string.length - string.replace(/\n*/g, '').length;
    371 				}
    372 				return 0;
    373 			}
    374 			// Substract linefeed in IE
    375 			function fixIeBug(string) {
    376 				if ($.browser.msie) {
    377 					return string.length - string.replace(/\r*/g, '').length;
    378 				}
    379 				return 0;
    380 			}
    381 				
    382 			// add markup
    383 			function insert(block) {	
    384 				if (document.selection) {
    385 					var newSelection = document.selection.createRange();
    386 					newSelection.text = block;
    387 				} else {
    388 					textarea.value =  textarea.value.substring(0, caretPosition)  + block + textarea.value.substring(caretPosition + selection.length, textarea.value.length);
    389 				}
    390 			}
    391 
    392 			// set a selection
    393 			function set(start, len) {
    394 				if (textarea.createTextRange){
    395 					// quick fix to make it work on Opera 9.5
    396 					if ($.browser.opera && $.browser.version >= 9.5 && len == 0) {
    397 						return false;
    398 					}
    399 					range = textarea.createTextRange();
    400 					range.collapse(true);
    401 					range.moveStart('character', start); 
    402 					range.moveEnd('character', len); 
    403 					range.select();
    404 				} else if (textarea.setSelectionRange ){
    405 					textarea.setSelectionRange(start, start + len);
    406 				}
    407 				textarea.scrollTop = scrollPosition;
    408 				textarea.focus();
    409 			}
    410 
    411 			// get the selection
    412 			function get() {
    413 				textarea.focus();
    414 
    415 				scrollPosition = textarea.scrollTop;
    416 				if (document.selection) {
    417 					selection = document.selection.createRange().text;
    418 					if ($.browser.msie) { // ie
    419 						var range = document.selection.createRange(), rangeCopy = range.duplicate();
    420 						rangeCopy.moveToElementText(textarea);
    421 						caretPosition = -1;
    422 						while(rangeCopy.inRange(range)) {
    423 							rangeCopy.moveStart('character');
    424 							caretPosition ++;
    425 						}
    426 					} else { // opera
    427 						caretPosition = textarea.selectionStart;
    428 					}
    429 				} else { // gecko & webkit
    430 					caretPosition = textarea.selectionStart;
    431 
    432 					selection = textarea.value.substring(caretPosition, textarea.selectionEnd);
    433 				} 
    434 				return selection;
    435 			}
    436 
    437 			// open preview window
    438 			function preview() {
    439 				if (!previewWindow || previewWindow.closed) {
    440 					if (options.previewInWindow) {
    441 						previewWindow = window.open('', 'preview', options.previewInWindow);
    442 						$(window).unload(function() {
    443 							previewWindow.close();
    444 						});
    445 					} else {
    446 						iFrame = $('<iframe class="markItUpPreviewFrame"></iframe>');
    447 						if (options.previewPosition == 'after') {
    448 							iFrame.insertAfter(footer);
    449 						} else {
    450 							iFrame.insertBefore(header);
    451 						}	
    452 						previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
    453 					}
    454 				} else if (altKey === true) {
    455 					if (iFrame) {
    456 						iFrame.remove();
    457 					} else {
    458 						previewWindow.close();
    459 					}
    460 					previewWindow = iFrame = false;
    461 				}
    462 				if (!options.previewAutoRefresh) {
    463 					refreshPreview(); 
    464 				}
    465 				if (options.previewInWindow) {
    466 					previewWindow.focus();
    467 				}
    468 			}
    469 
    470 			// refresh Preview window
    471 			function refreshPreview() {
    472  				renderPreview();
    473 			}
    474 
    475 			function renderPreview() {		
    476 				var phtml;
    477 				if (options.previewParser && typeof options.previewParser === 'function') {
    478 					var data = options.previewParser( $$.val() );
    479 					writeInPreview( localize(data, 1) ); 
    480 				} else if (options.previewParserPath !== '') {
    481 					$.ajax({
    482 						type: 'POST',
    483 						dataType: 'text',
    484 						global: false,
    485 						url: options.previewParserPath,
    486 						data: options.previewParserVar+'='+encodeURIComponent($$.val()),
    487 						success: function(data) {
    488 							writeInPreview( localize(data, 1) ); 
    489 						}
    490 					});
    491 				} else {
    492 					if (!template) {
    493 						$.ajax({
    494 							url: options.previewTemplatePath,
    495 							dataType: 'text',
    496 							global: false,
    497 							success: function(data) {
    498 								writeInPreview( localize(data, 1).replace(/<!-- content -->/g, $$.val()) );
    499 							}
    500 						});
    501 					}
    502 				}
    503 				return false;
    504 			}
    505 			
    506 			function writeInPreview(data) {
    507 				if (previewWindow.document) {			
    508 					try {
    509 						sp = previewWindow.document.documentElement.scrollTop
    510 					} catch(e) {
    511 						sp = 0;
    512 					}	
    513 					previewWindow.document.open();
    514 					previewWindow.document.write(data);
    515 					previewWindow.document.close();
    516 					previewWindow.document.documentElement.scrollTop = sp;
    517 				}
    518 			}
    519 			
    520 			// set keys pressed
    521 			function keyPressed(e) { 
    522 				shiftKey = e.shiftKey;
    523 				altKey = e.altKey;
    524 				ctrlKey = (!(e.altKey && e.ctrlKey)) ? (e.ctrlKey || e.metaKey) : false;
    525 
    526 				if (e.type === 'keydown') {
    527 					if (ctrlKey === true) {
    528 						li = $('a[accesskey="'+String.fromCharCode(e.keyCode)+'"]', header).parent('li');
    529 						if (li.length !== 0) {
    530 							ctrlKey = false;
    531 							setTimeout(function() {
    532 								li.triggerHandler('mouseup');
    533 							},1);
    534 							return false;
    535 						}
    536 					}
    537 					if (e.keyCode === 13 || e.keyCode === 10) { // Enter key
    538 						if (ctrlKey === true) {  // Enter + Ctrl
    539 							ctrlKey = false;
    540 							markup(options.onCtrlEnter);
    541 							return options.onCtrlEnter.keepDefault;
    542 						} else if (shiftKey === true) { // Enter + Shift
    543 							shiftKey = false;
    544 							markup(options.onShiftEnter);
    545 							return options.onShiftEnter.keepDefault;
    546 						} else { // only Enter
    547 							markup(options.onEnter);
    548 							return options.onEnter.keepDefault;
    549 						}
    550 					}
    551 					if (e.keyCode === 9) { // Tab key
    552 						if (shiftKey == true || ctrlKey == true || altKey == true) {
    553 							return false; 
    554 						}
    555 						if (caretOffset !== -1) {
    556 							get();
    557 							caretOffset = $$.val().length - caretOffset;
    558 							set(caretOffset, 0);
    559 							caretOffset = -1;
    560 							return false;
    561 						} else {
    562 							markup(options.onTab);
    563 							return options.onTab.keepDefault;
    564 						}
    565 					}
    566 				}
    567 			}
    568 
    569 			init();
    570 		});
    571 	};
    572 
    573 	$.fn.markItUpRemove = function() {
    574 		return this.each(function() {
    575 				var $$ = $(this).unbind().removeClass('markItUpEditor');
    576 				$$.parent('div').parent('div.markItUp').parent('div').replaceWith($$);
    577 			}
    578 		);
    579 	};
    580 
    581 	$.markItUp = function(settings) {
    582 		var options = { target:false };
    583 		$.extend(options, settings);
    584 		if (options.target) {
    585 			return $(options.target).each(function() {
    586 				$(this).focus();
    587 				$(this).trigger('insertion', [options]);
    588 			});
    589 		} else {
    590 			$('textarea').trigger('insertion', [options]);
    591 		}
    592 	};
    593 })(jQuery);