PageSourceSearch

https://www.vocance.fr/plugins-dist/porte_plume/javascript/jquery.markitup_pour_spip.js

js vocance.fr collected 2026-10-02 14:42:00 UTC 27,877 bytes, 863 lines download raw bytes

1// ----------------------------------------------------------------------------
2// markItUp! Universal MarkUp Engine, JQuery plugin
3// v 1.1.14 ( c014800b - 02/06/2014 )
4// Dual licensed under the MIT and GPL licenses.
5// ----------------------------------------------------------------------------
6// Copyright (C) 2007-2012 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
28/*
29 *   Le code original de markitup 1.1.14
30 *   a ete modifie pour prendre en compte
31 * 
32 *   1) la langue utilisee dans les textarea :
33 * 		- si un textarea possede un attribut lang='xx' alors
34 *   	  markitup n'affichera que les icones qui correspondent a cette langue
35 * 		- on peut passer une valeur de langue par defaut a markitup (le textarea peut ne pas en definir)
36 *   	  .markitup(set_spip,{lang:'fr'});
37 * 		- une option supplementaire optionnelle 'lang' est introduite dans les parametres 
38 *   	  des boutons (markupset), par exemple : lang:['fr','es','en']
39 * 		- si un bouton n'a pas ce parametre, l'icone s'affiche 
40 *   	  quelque soit la langue designee dans le textarea ou les parametres de markitup ;
41 *   	  sinon, il faut que la langue soit contenue dedans pour que l'icone s'affiche.
42
43 *   2) gerer des types de selections differentes : 
44 * 		- normales comme dans markitup (rien a faire)
45 * 		- 'selectionType':'word' : aux mots le plus proche si pas de selection (sinon la selection)
46 * 		- 'selectionType':'line' : aux lignes les plus proches
47 * 		- and 'return' : ugly hack to generate list (and so on) on key 'return' press
48 *
49 *   3) eviter a Opera de gerer les evenements apres tabulation ou entree...
50 *      il ne sait pas gerer (v11.51)
51 * 
52 *   4) ajout d'un <em> supplémentaire sur le html des boutons de la barre d'outil, pour des histoires de sprites
53 */
54;(function($) {
55	$.fn.markItUp = function(settings, extraSettings) {
56		var method, params, options, ctrlKey, shiftKey, altKey; ctrlKey = shiftKey = altKey = false;
57		markitup_prompt = false; // variable volontairement globale
58		
59		if (typeof settings == 'string') {
60			method = settings;
61			params = extraSettings;
62		} 
63
64		options = {	id:						'',
65					nameSpace:				'',
66					root:					'',
67					lang:					'',
68					previewHandler:			false,
69					previewInWindow:		'', // 'width=800, height=600, resizable=yes, scrollbars=yes'
70					previewInElement:		'',
71					previewAutoRefresh:		true,
72					previewPosition:		'after',
73					previewTemplatePath:	'~/templates/preview.html',
74					previewParser:			false,
75					previewParserPath:		'',
76					previewParserVar:		'data',
77					previewParserAjaxType:	'POST',
78					resizeHandle:			true,
79					beforeInsert:			'',
80					afterInsert:			'',
81					onEnter:				{},
82					onShiftEnter:			{},
83					onCtrlEnter:			{},
84					onTab:					{},
85					markupSet:			[	{ /* set */ } ]
86				};
87		$.extend(options, settings, extraSettings);
88
89		// compute markItUp! path
90		if (!options.root) {
91			$('script').each(function(a, tag) {
92				miuScript = $(tag).get(0).src.match(/(.*)jquery\.markitup(\.pack)?\.js$/);
93				if (miuScript !== null) {
94					options.root = miuScript[1];
95				}
96			});
97		}
98
99		// Quick patch to keep compatibility with jQuery 1.9
100		var uaMatch = function(ua) {
101			ua = ua.toLowerCase();
102
103			var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
104				/(webkit)[ \/]([\w.]+)/.exec(ua) ||
105				/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
106				/(msie) ([\w.]+)/.exec(ua) ||
107				ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
108				[];
109
110			return {
111				browser: match[ 1 ] || "",
112				version: match[ 2 ] || "0"
113			};
114		};
115		var matched = uaMatch( navigator.userAgent );
116		var browser = {};
117
118		if (matched.browser) {
119			browser[matched.browser] = true;
120			browser.version = matched.version;
121		}
122		if (browser.chrome) {
123			browser.webkit = true;
124		} else if (browser.webkit) {
125			browser.safari = true;
126		}
127
128		return this.each(function() {
129			var $$, textarea, levels, scrollPosition, caretPosition,
130				clicked, hash, header, footer, previewWindow, template, iFrame, abort,
131				before, after;
132			$$ = $(this);
133			textarea = this;
134			levels = [];
135			abort = false;
136			scrollPosition = caretPosition = 0;
137			caretOffset = -1;
138
139			options.previewParserPath = localize(options.previewParserPath);
140			options.previewTemplatePath = localize(options.previewTemplatePath);
141
142			if (method) {
143				switch(method) {
144					case 'remove':
145						remove();
146					break;
147					case 'insert':
148						markup(params);
149					break;
150					default: 
151						$.error('Method ' +  method + ' does not exist on jQuery.markItUp');
152				}
153				return;
154			}
155
156			// apply the computed path to ~/
157			function localize(data, inText) {
158				if (inText) {
159					return 	data.replace(/("|')~\//g, "$1"+options.root);
160				}
161				return 	data.replace(/^~\//, options.root);
162			}
163
164			// init and build editor
165			function init() {
166				id = ''; nameSpace = '';
167				if (options.id) {
168					id = 'id="'+options.id+'"';
169				} else if ($$.attr("id")) {
170					id = 'id="markItUp'+($$.attr("id").substr(0, 1).toUpperCase())+($$.attr("id").substr(1))+'"';
171
172				}
173				if (options.nameSpace) {
174					nameSpace = 'class="'+options.nameSpace+'"';
175				}
176				currentScrollPosition = $$.scrollTop();
177				$$.wrap('<div '+nameSpace+'></div>');
178				$$.wrap('<div '+id+' class="markItUp"></div>');
179				$$.wrap('<div class="markItUpContainer"></div>');
180				$$.addClass("markItUpEditor");
181				$$.scrollTop(currentScrollPosition);
182
183				// add the header before the textarea
184				header = $('<div class="markItUpHeader"></div>').insertBefore($$);
185				$(dropMenus(options.markupSet)).appendTo(header);
186				// remove empty dropMenu
187				$(header).find("li.markItUpDropMenu ul:empty").parent().remove();
188
189				// add the footer after the textarea
190				footer = $('<div class="markItUpFooter"></div>').insertAfter($$);
191
192				// add the resize handle after textarea
193				if (options.resizeHandle === true && browser.safari !== true) {
194					resizeHandle = $('<div class="markItUpResizeHandle"></div>')
195						.insertAfter($$)
196						.bind("mousedown.markItUp", function(e) {
197							var h = $$.height(), y = e.clientY, mouseMove, mouseUp;
198							mouseMove = function(e) {
199								$$.css("height", Math.max(20, e.clientY+h-y)+"px");
200								return false;
201							};
202							mouseUp = function(e) {
203								$("html").unbind("mousemove.markItUp", mouseMove).unbind("mouseup.markItUp", mouseUp);
204								return false;
205							};
206							$("html").bind("mousemove.markItUp", mouseMove).bind("mouseup.markItUp", mouseUp);
207					});
208					footer.append(resizeHandle);
209				}
210
211				// listen key events
212				$$.bind('keydown.markItUp', keyPressed).bind('keyup', keyPressed);
213				
214				// bind an event to catch external calls
215				$$.bind("insertion.markItUp", function(e, settings) {
216					if (settings.target !== false) {
217						get();
218					}
219					if (textarea === $.markItUp.focused) {
220						markup(settings);
221					}
222				});
223
224				// remember the last focus
225				$$.bind('focus.markItUp', function() {
226					$.markItUp.focused = this;
227				});
228
229				if (options.previewInElement) {
230					refreshPreview();
231				}
232			}
233
234			// recursively build header with dropMenus from markupset
235			function dropMenus(markupSet) {
236				var ul = $('<ul></ul>'), i = 0;
237				var lang = ($$.attr('lang')||options.lang);
238				
239				$('li:hover > ul', ul).css('display', 'block');
240				$.each(markupSet, function() {
241					var button = this, t = '', title, li, j;
242					// pas de langue ou dans la langue ; et uniquement si langue autorisee
243					if ((!lang || !button.lang || ($.inArray(lang, button.lang) != -1))
244						&& (!button.lang_not || ($.inArray(lang, button.lang_not) == -1))) {
245						button.title ? title = (button.key) ? (button.title||'')+' [Ctrl+'+button.key+']' : (button.title||'') : title = (button.key) ? (button.name||'')+' [Ctrl+'+button.key+']' : (button.name||'');
246						key   = (button.key) ? 'accesskey="'+button.key+'"' : '';
247						if (button.separator) {
248							li = $('<li class="markItUpSeparator">'+(button.separator||'')+'</li>').appendTo(ul);
249						} else {
250							i++;
251							for (j = levels.length -1; j >= 0; j--) {
252								t += levels[j]+"-";
253							}
254							li = $('<li class="markItUpButton markItUpButton'+t+(i)+' '+(button.className||'')+'"><a href="#" '+key+' title="'+title+'"><em>'+(button.name||'')+'</em></a></li>')
255							.bind("contextmenu.markItUp", function() { // prevent contextmenu on mac and allow ctrl+click
256								return false;
257							}).bind('click.markItUp', function(e) {
258								e.preventDefault();
259							}).bind("focusin.markItUp", function(){
260								$$.focus();
261							}).bind('mouseup', function(e) {
262								if (button.call) {
263									eval(button.call)(e); // Pass the mouseup event to custom delegate
264								}
265								setTimeout(function() { markup(button) },1);
266								return false;
267							}).bind('mouseenter.markItUp', function() {
268									$('> ul', this).show();
269									$(document).one('click', function() { // close dropmenu if click outside
270											$('ul ul', header).hide();
271										}
272									);
273							}).bind('mouseleave.markItUp', function() {
274									$('> ul', this).hide();
275							}).appendTo(ul);
276							if (button.dropMenu) {
277								levels.push(i);
278								$(li).addClass('markItUpDropMenu').append(dropMenus(button.dropMenu));
279							}
280						}
281					}
282				}); 
283				levels.pop();
284				return ul;
285			}
286
287			// markItUp! markups
288			function magicMarkups(string) {
289				if (string) {
290					string = string.toString();
291					string = string.replace(/\(\!\(([\s\S]*?)\)\!\)/g,
292						function(x, a) {
293							var b = a.split('|!|');
294							if (altKey === true) {
295								return (b[1] !== undefined) ? b[1] : b[0];
296							} else {
297								return (b[1] === undefined) ? "" : b[0];
298							}
299						}
300					);
301					// [![prompt]!], [![prompt:!:value]!]
302					string = string.replace(/\[\!\[([\s\S]*?)\]\!\]/g,
303						function(x, a) {
304							var b = a.split(':!:');
305							if (abort === true) {
306								return false;
307							}
308							
309							// On prévient qu'un prompt s'ouvre
310							markitup_prompt = true;
311							
312							value = prompt(b[0], (b[1]) ? b[1] : '');
313							if (value === null) {
314								abort = true;
315							}
316							
317							// On attend un peu avant de dire que le prompt est fermé
318							// pour ne pas que ça soit pris en compte en même temps que la fermeture du prompt
319							setTimeout(function(){markitup_prompt = false;}, 500);
320							
321							return value;
322						}
323					);
324					return string;
325				}
326				return "";
327			}
328
329			// prepare action
330			function prepare(action) {
331				if ($.isFunction(action)) {
332					action = action(hash);
333				}
334				return magicMarkups(action);
335			}
336
337			// build block to insert
338			function build(string) {
339				var openWith 			= prepare(clicked.openWith);
340				var placeHolder 		= prepare(clicked.placeHolder);
341				var replaceWith 		= prepare(clicked.replaceWith);
342				var closeWith 			= prepare(clicked.closeWith);
343				var openBlockWith 		= prepare(clicked.openBlockWith);
344				var closeBlockWith 		= prepare(clicked.closeBlockWith);
345				var multiline 			= clicked.multiline;
346				
347				if (replaceWith !== "") {
348					block = openWith + replaceWith + closeWith;
349				} else if (selection === '' && placeHolder !== '') {
350					block = openWith + placeHolder + closeWith;
351				} else if (multiline === true) {
352					string = string || selection;
353
354					var lines = [string], blocks = [];
355					
356					if (multiline === true) {
357						lines = string.split(/\r?\n/);
358					}
359					
360					for (var l = 0; l < lines.length; l++) {
361						line = lines[l];
362						var trailingSpaces;
363						if (trailingSpaces = line.match(/ *$/)) {
364							blocks.push(openWith + line.replace(/ *$/g, '') + closeWith + trailingSpaces);
365						} else {
366							blocks.push(openWith + line + closeWith);
367						}
368					}
369					
370					block = blocks.join("\n");
371				} else {
372					block = openWith + (string || selection) + closeWith;
373				}
374
375				block = openBlockWith + block + closeBlockWith;
376
377				return {	block:block, 
378							openBlockWith:openBlockWith,
379							openWith:openWith, 
380							replaceWith:replaceWith, 
381							placeHolder:placeHolder,
382							closeWith:closeWith,
383							closeBlockWith:closeBlockWith
384					};
385			}
386
387
388			function selectWord(){
389				selectionBeforeAfter(/\s|[.,;:!¡?¿()]/);
390				selectionSave();
391			}
392			function selectLine(){
393				selectionBeforeAfter(/\r?\n/);
394				selectionSave();
395			}
396
397			function selectionRemoveLast(pattern){
398					// Remove space by default
399					if (!pattern) pattern = /\s/;
400					last = selection[selection.length-1];
401					if (last && last.match(pattern)) {
402						set(caretPosition, selection.length-1);
403						get();
404						$.extend(hash, { caretPosition:caretPosition, scrollPosition:scrollPosition } );
405					}
406			}
407
408			function selectionBeforeAfter(pattern) {
409				if (!pattern) pattern = /\s/;
410
411				sautAvantIE = sautApresIE = 0;
412				if (browser.msie) {
413					 	// calcul du nombre reel de caracteres pour le substr()
414					 	// IE ne compte pas les sauts de lignes pour definir les selections
415					 	// mais les compte dans la fonction length()
416						lenSelection = selection.length - fixIeBug(selection);
417						// si le caractere avant mon debut est un saut le ligne,
418						// ie ne le prendra pas en compte dans la selection.
419						// il faut pouvoir le connaitre.
420						if (caretPosition) {
421							set(caretPosition - 1, 2);
422							sautAvantIE = fixIeBug(document.selection.createRange().text);
423						}
424						// idem pour le caractere apres la ligne !
425						set(caretPosition, 2);
426						sautApresIE = fixIeBug(document.selection.createRange().text);
427						// selection avant
428 						set(0,caretPosition);
429 						before = document.selection.createRange().text;
430 						// selection apres
431 						set(caretPosition + lenSelection, textarea.value.length);
432 						after = document.selection.createRange().text;
433 						// remettre la veritable selection
434 						set(caretPosition, lenSelection);
435 						selection = document.selection.createRange().text;
436				} else {
437					before = textarea.value.substring(0, caretPosition);
438					after = textarea.value.substring(caretPosition + selection.length - fixIeBug(selection));
439				}
440
441				before = before.split(pattern);
442				after = after.split(pattern);
443				// ajouter ce fichu saut de ligne pour IE
444				if (sautAvantIE) before.push("");
445				if (sautApresIE) after.unshift("");
446
447			}
448
449			function selectionSave(){
450				nb_before = before ? before[before.length-1].length : 0;
451				nb_after = after ? after[0].length : 0;
452
453				nb = nb_before + selection.length + nb_after - fixIeBug(selection);
454				caretPosition =  caretPosition - nb_before;
455
456				set(caretPosition, nb);
457				get();
458				$.extend(hash, { selection:selection, caretPosition:caretPosition, scrollPosition:scrollPosition } );
459			}
460
461			// define markup to insert
462			function markup(button) {
463				var len, j, n, i;
464				hash = clicked = button;
465				get();
466				
467				$.extend(hash, {	line:"", 
468						 			root:options.root,
469									textarea:textarea, 
470									selection:(selection||''), 
471									caretPosition:caretPosition,
472									ctrlKey:ctrlKey, 
473									shiftKey:shiftKey, 
474									altKey:altKey
475								}
476							);
477							
478				// corrections des selections pour que
479				// - soit le curseur ne change pas
480				// - soit on prend le mot complet (si pas de selection)
481				// - soit on prend la ligne (avant, apres la selection)
482				if (button.selectionType) {
483
484					if (button.selectionType == "word") {
485						if (!selection) {
486							selectWord();
487						} else {
488							// win/ff add space on double click ? (hum, seems strange)
489							selectionRemoveLast(/\s/);
490						}
491					}
492					if (button.selectionType == "line") {
493						selectLine();
494					}
495					// horrible chose, mais tellement plus pratique
496					// car on ne peut pas de l'exerieur (json) utiliser
497					// les fonctions internes de markitup
498					if (button.selectionType == "return"){
499						// le calcul de before et after sous IE
500						// necessitant de creer des selections
501						// c'est extremement vilain a chaque saut de ligne
502						// des qu'il y a un texte volumineux.
503						// on dit tant pis pour lui.
504						if (!browser.msie) {
505							selectionBeforeAfter(/\r?\n/);
506							before_last = before[before.length-1];
507							after = '';
508							// gestion des listes -# et -* 
509							if (r = before_last.match(/^-([*#]+) ?(.*)$/)) {
510								if (r[2]) {
511									button.replaceWith = "\n-"+r[1]+' ';
512									before_last = '';
513								} else {
514									// supprime le -* present
515									// (before le fera)
516									button.replaceWith = "\n";
517								}
518							} else {
519								before_last = '';
520								button.replaceWith = "\n";
521							}
522							before[before.length-1] = before_last;
523							selectionSave();
524						}
525					}
526				}
527				// / fin corrections
528				
529				// callbacks before insertion
530				prepare(options.beforeInsert);
531				prepare(clicked.beforeInsert);
532				if ((ctrlKey === true && shiftKey === true) || button.multiline === true) {
533					prepare(clicked.beforeMultiInsert);
534				}
535				$.extend(hash, { line:1 });
536
537				if ((ctrlKey === true && shiftKey === true) || button.forceMultiline === true) {
538					lines = selection.split(/\r?\n/);
539					for (j = 0, n = lines.length, i = 0; i < n; i++) {
540						// si une seule ligne, on se fiche de savoir qu'elle est vide,
541						// c'est volontaire si on clique le bouton
542						if (n == 1 || $.trim(lines[i]) !== '') {
543							$.extend(hash, { line:++j, selection:lines[i] } );
544							lines[i] = build(lines[i]).block;
545						} else {
546							lines[i] = "";
547						}
548					}
549
550					string = { block:lines.join('\n')};
551					start = caretPosition;
552					len = string.block.length + ((browser.opera) ? n-1 : 0);
553				} else if (ctrlKey === true) {
554					string = build(selection);
555					start = caretPosition + string.openWith.length;
556					len = string.block.length - string.openWith.length - string.closeWith.length;
557					len = len - (string.block.match(/ $/) ? 1 : 0);
558					len -= fixIeBug(string.block);
559				} else if (shiftKey === true) {
560					string = build(selection);
561					start = caretPosition;
562					len = string.block.length;
563					len -= fixIeBug(string.block);
564				} else {
565					string = build(selection);
566					start = caretPosition + string.block.length ;
567					len = 0;
568					start -= fixIeBug(string.block);
569				}
570				
571				if ((selection === '' && string.replaceWith === '')) {
572					caretOffset += fixOperaBug(string.block);
573
574					start = caretPosition + string.openBlockWith.length + string.openWith.length;
575					len = string.block.length - string.openBlockWith.length - string.openWith.length - string.closeWith.length - string.closeBlockWith.length;
576
577					caretOffset = $$.val().substring(caretPosition,  $$.val().length).length;
578					caretOffset -= fixOperaBug($$.val().substring(0, caretPosition));
579				}
580				$.extend(hash, { caretPosition:caretPosition, scrollPosition:scrollPosition } );
581
582				if (string.block !== selection && abort === false) {
583					insert(string.block);
584					set(start, len);
585				} else {
586					caretOffset = -1;
587				}
588				get();
589
590				$.extend(hash, { line:'', selection:selection });
591
592				// callbacks after insertion
593				if ((ctrlKey === true && shiftKey === true) || button.multiline === true) {
594					prepare(clicked.afterMultiInsert);
595				}
596
597				prepare(clicked.afterInsert);
598				prepare(options.afterInsert);
599
600				// refresh preview if opened
601				if (previewWindow && options.previewAutoRefresh) {
602					refreshPreview(); 
603				}
604
605				// reinit keyevent
606				shiftKey = altKey = ctrlKey = abort = false;
607			}
608
609			// Substract linefeed in Opera
610			function fixOperaBug(string) {
611				if (browser.opera) {
612					return string.length - string.replace(/\n*/g, '').length;
613				}
614				return 0;
615			}
616			// Substract linefeed in IE
617			function fixIeBug(string) {
618				if (browser.msie) {
619					return string.length - string.replace(/\r*/g, '').length;
620				}
621				return 0;
622			}
623				
624			// add markup
625			function insert(block) {	
626				if (document.selection) {
627					var newSelection = document.selection.createRange();
628					newSelection.text = block;
629				} else {
630					textarea.value =  textarea.value.substring(0, caretPosition)  + block + textarea.value.substring(caretPosition + selection.length, textarea.value.length);
631				}
632			}
633
634			// set a selection
635			function set(start, len) {
636				if (textarea.createTextRange){
637					// quick fix to make it work on Opera 9.5
638					if (browser.opera && browser.version >= 9.5 && len == 0) {
639						return false;
640					}
641					range = textarea.createTextRange();
642					range.collapse(true);
643					range.moveStart('character', start); 
644					range.moveEnd('character', len); 
645					range.select();
646				} else if (textarea.setSelectionRange ){
647					textarea.setSelectionRange(start, start + len);
648				}
649				textarea.scrollTop = scrollPosition;
650				textarea.focus();
651			}
652
653			// get the selection
654			function get() {
655				textarea.focus();
656
657				scrollPosition = textarea.scrollTop;
658				if (document.selection) {
659					selection = document.selection.createRange().text;
660					if (browser.msie) { // ie
661						var range = document.selection.createRange(), rangeCopy = range.duplicate();
662						rangeCopy.moveToElementText(textarea);
663						caretPosition = -1;
664						while(rangeCopy.inRange(range)) {
665							rangeCopy.moveStart('character');
666							caretPosition ++;
667						}
668					} else { // opera
669						caretPosition = textarea.selectionStart;
670					}
671				} else { // gecko & webkit
672					caretPosition = textarea.selectionStart;
673
674					selection = textarea.value.substring(caretPosition, textarea.selectionEnd);
675				} 
676				return selection;
677			}
678
679			// open preview window
680			function preview() {
681				if (typeof options.previewHandler === 'function') {
682					previewWindow = true;
683				} else if (options.previewInElement) {
684					previewWindow = $(options.previewInElement);
685				} else if (!previewWindow || previewWindow.closed) {
686					if (options.previewInWindow) {
687						previewWindow = window.open('', 'preview', options.previewInWindow);
688						$(window).unload(function() {
689							previewWindow.close();
690						});
691					} else {
692						iFrame = $('<iframe class="markItUpPreviewFrame"></iframe>');
693						if (options.previewPosition == 'after') {
694							iFrame.insertAfter(footer);
695						} else {
696							iFrame.insertBefore(header);
697						}	
698						previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
699					}
700				} else if (altKey === true) {
701					if (iFrame) {
702						iFrame.remove();
703					} else {
704						previewWindow.close();
705					}
706					previewWindow = iFrame = false;
707				}
708				if (!options.previewAutoRefresh) {
709					refreshPreview(); 
710				}
711				if (options.previewInWindow) {
712					previewWindow.focus();
713				}
714			}
715
716			// refresh Preview window
717			function refreshPreview() {
718 				renderPreview();
719			}
720
721			function renderPreview() {
722				var phtml;
723				if (options.previewHandler && typeof options.previewHandler === 'function') {
724					options.previewHandler( $$.val() );
725				} else if (options.previewParser && typeof options.previewParser === 'function') {
726					var data = options.previewParser( $$.val() );
727					writeInPreview(localize(data, 1) ); 
728				} else if (options.previewParserPath !== '') {
729					$.ajax({
730						type: options.previewParserAjaxType,
731						dataType: 'text',
732						global: false,
733						url: options.previewParserPath,
734						data: options.previewParserVar+'='+encodeURIComponent($$.val()),
735						success: function(data) {
736							writeInPreview( localize(data, 1) ); 
737						}
738					});
739				} else {
740					if (!template) {
741						$.ajax({
742							url: options.previewTemplatePath,
743							dataType: 'text',
744							global: false,
745							success: function(data) {
746								writeInPreview( localize(data, 1).replace(/<!-- content -->/g, $$.val()) );
747							}
748						});
749					}
750				}
751				return false;
752			}
753			
754			function writeInPreview(data) {
755				if (options.previewInElement) {
756					$(options.previewInElement).html(data);
757				} else if (previewWindow && previewWindow.document) {			
758					try {
759						sp = previewWindow.document.documentElement.scrollTop
760					} catch(e) {
761						sp = 0;
762					}	
763					previewWindow.document.open();
764					previewWindow.document.write(data);
765					previewWindow.document.close();
766					previewWindow.document.documentElement.scrollTop = sp;
767				}
768			}
769			
770			// set keys pressed
771			function keyPressed(e) { 
772				shiftKey = e.shiftKey;
773				altKey = e.altKey;
774				ctrlKey = (!(e.altKey && e.ctrlKey)) ? (e.ctrlKey || e.metaKey) : false;
775
776				if (e.type === 'keydown') {
777					if (ctrlKey === true) {
778						li = $('a[accesskey="'+((e.keyCode == 13) ? '\\n' : String.fromCharCode(e.keyCode))+'"]', header).parent('li');
779						if (li.length !== 0) {
780							ctrlKey = false;
781							setTimeout(function() {
782								li.triggerHandler('mouseup');
783							},1);
784							return false;
785						}
786					}
787					
788 					// si opera, on s'embete pas, il cree plus de problemes qu'autre chose
789 					// car il ne prend pas en compte l'arret de ces evenements
790					if (!browser.opera) {
791						if (e.keyCode === 13 || e.keyCode === 10) { // Enter key
792							if (ctrlKey === true) {  // Enter + Ctrl
793								ctrlKey = false;
794								markup(options.onCtrlEnter);
795								return options.onCtrlEnter.keepDefault;
796							} else if (shiftKey === true) { // Enter + Shift
797								shiftKey = false;
798								markup(options.onShiftEnter);
799								return options.onShiftEnter.keepDefault;
800							} else { // only Enter
801								markup(options.onEnter);
802								return options.onEnter.keepDefault;
803							}
804						}
805						
806						if (e.keyCode === 9) { // Tab key
807							if (shiftKey == true || ctrlKey == true || altKey == true) {
808								// permettre un retour a l'action naturelle
809								// du navigateur via shift+tab 
810								return false; 
811							}
812							if (caretOffset !== -1) {
813								get();
814								caretOffset = $$.val().length - caretOffset;
815								set(caretOffset, 0);
816								caretOffset = -1;
817								return false;
818							} else {
819								markup(options.onTab);
820								return options.onTab.keepDefault;
821							}
822						}
823					}
824				}
825			}
826
827			function remove() {
828				$$.unbind(".markItUp").removeClass('markItUpEditor');
829				$$.parent('div').parent('div.markItUp').parent('div').replaceWith($$);
830
831				var relativeRef = $$.parent('div').parent('div.markItUp').parent('div');
832				if (relativeRef.length) {
833				    relativeRef.replaceWith($$);
834				}
835				
836				$$.data('markItUp', null);
837			}
838
839			init();
840		});
841	};
842
843	$.fn.markItUpRemove = function() {
844		return this.each(function() {
845				$(this).markItUp('remove');
846			}
847		);
848	};
849
850	$.markItUp = function(settings) {
851		var options = { target:false };
852		$.extend(options, settings);
853		if (options.target) {
854			return $(options.target).each(function() {
855				$(this).focus();
856				$(this).trigger('insertion', [options]);
857			});
858		} else {
859			$('textarea').trigger('insertion', [options]);
860		}
861	};
862	
863})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.