PageSourceSearch

https://browncard.org/plugins-dist/porte_plume/javascript/jquery.markitup_pour_spip.js?1782257631

js browncard.org collected 2026-10-02 22:41:32 UTC 28,519 bytes, 880 lines download raw bytes

1// ----------------------------------------------------------------------------
2// markItUp! Universal MarkUp Engine, JQuery plugin
3// v 1.1.15 ( 77b48ad - 22/10/2018 )
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.15
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				var 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						.on("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").off("mousemove.markItUp", mouseMove).off("mouseup.markItUp", mouseUp);
204								return false;
205							};
206							$("html").on("mousemove.markItUp", mouseMove).on("mouseup.markItUp", mouseUp);
207					});
208					footer.append(resizeHandle);
209				}
210
211				// listen key events
212				$$.on('keydown.markItUp', keyPressed).on('keyup', keyPressed);
213				
214				// bind an event to catch external calls
215				$$.on("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				$$.on('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							.on("contextmenu.markItUp", function() { // prevent contextmenu on mac and allow ctrl+click
256								return false;
257							}).on('click.markItUp', function(e) {
258								e.preventDefault();
259							}).on("focusin.markItUp", function(){
260								setTimeout(function(){
261									$$.focus();
262								}, 0);
263							}).on('mouseup', function(e) {
264								if (button.call) {
265									eval(button.call)(e); // Pass the mouseup event to custom delegate
266								}
267								setTimeout(function() { markup(button) },1);
268								return false;
269							}).on('mouseenter.markItUp', function() {
270									$('> a', this).addClass('active');
271									$('> ul', this).addClass('expanded');
272									$(document).one('click', function() { // close dropmenu if click outside
273											$('ul a', header).removeClass('active');
274											$('ul ul', header).removeClass('expanded');
275										}
276									);
277							}).on('mouseleave.markItUp', function() {
278									$('> a', this).removeClass('active');
279									$('> ul', this).removeClass('expanded');
280							}).appendTo(ul);
281							if (button.dropMenu) {
282								levels.push(i);
283								$(li).addClass('markItUpDropMenu').append(dropMenus(button.dropMenu));
284							}
285						}
286					}
287				}); 
288				levels.pop();
289				return ul;
290			}
291
292			// markItUp! markups
293			function magicMarkups(string) {
294				if (string) {
295					string = string.toString();
296					string = string.replace(/\(\!\(([\s\S]*?)\)\!\)/g,
297						function(x, a) {
298							var b = a.split('|!|');
299							if (altKey === true) {
300								return (b[1] !== undefined) ? b[1] : b[0];
301							} else {
302								return (b[1] === undefined) ? "" : b[0];
303							}
304						}
305					);
306					// [![prompt]!], [![prompt:!:value]!]
307					string = string.replace(/\[\!\[([\s\S]*?)\]\!\]/g,
308						function(x, a) {
309							var b = a.split(':!:');
310							if (abort === true) {
311								return false;
312							}
313							
314							// On prévient qu'un prompt s'ouvre
315							markitup_prompt = true;
316							
317							value = prompt(b[0], (b[1]) ? b[1] : '');
318							if (value === null) {
319								abort = true;
320							}
321							
322							// On attend un peu avant de dire que le prompt est fermé
323							// pour ne pas que ça soit pris en compte en même temps que la fermeture du prompt
324							setTimeout(function(){markitup_prompt = false;}, 500);
325							
326							return value;
327						}
328					);
329					return string;
330				}
331				return "";
332			}
333
334			// prepare action
335			function prepare(action) {
336				if ($.isFunction(action)) {
337					action = action(hash);
338				}
339				return magicMarkups(action);
340			}
341
342			// build block to insert
343			function build(string) {
344				var openWith 			= prepare(clicked.openWith);
345				var placeHolder 		= prepare(clicked.placeHolder);
346				var replaceWith 		= prepare(clicked.replaceWith);
347				var closeWith 			= prepare(clicked.closeWith);
348				var openBlockWith 		= prepare(clicked.openBlockWith);
349				var closeBlockWith 		= prepare(clicked.closeBlockWith);
350				var multiline 			= clicked.multiline;
351				
352				if (replaceWith !== "") {
353					block = openWith + replaceWith + closeWith;
354				} else if (selection === '' && placeHolder !== '') {
355					block = openWith + placeHolder + closeWith;
356				} else if (multiline === true) {
357					string = string || selection;
358
359					var lines = [string], blocks = [];
360					
361					if (multiline === true) {
362						lines = string.split(/\r?\n/);
363					}
364					
365					for (var l = 0; l < lines.length; l++) {
366						line = lines[l];
367						var trailingSpaces;
368						if (trailingSpaces = line.match(/ *$/)) {
369							blocks.push(openWith + line.replace(/ *$/g, '') + closeWith + trailingSpaces);
370						} else {
371							blocks.push(openWith + line + closeWith);
372						}
373					}
374					
375					block = blocks.join("\n");
376				} else {
377					block = openWith + (string || selection) + closeWith;
378				}
379
380				block = openBlockWith + block + closeBlockWith;
381
382				return {	block:block, 
383							openBlockWith:openBlockWith,
384							openWith:openWith, 
385							replaceWith:replaceWith, 
386							placeHolder:placeHolder,
387							closeWith:closeWith,
388							closeBlockWith:closeBlockWith
389					};
390			}
391
392
393			function selectWord(){
394				selectionBeforeAfter(/\s|[.,;:!¡?¿()]/);
395				selectionSave();
396			}
397			function selectLine(){
398				selectionBeforeAfter(/\r?\n/);
399				selectionSave();
400			}
401
402			function selectionRemoveLast(pattern){
403					// Remove space by default
404					if (!pattern) pattern = /\s/;
405					last = selection[selection.length-1];
406					if (last && last.match(pattern)) {
407						set(caretPosition, selection.length-1);
408						get();
409						$.extend(hash, { caretPosition:caretPosition, scrollPosition:scrollPosition } );
410					}
411			}
412
413			function selectionBeforeAfter(pattern) {
414				if (!pattern) pattern = /\s/;
415
416				sautAvantIE = sautApresIE = 0;
417				if (browser.msie) {
418					 	// calcul du nombre reel de caracteres pour le substr()
419					 	// IE ne compte pas les sauts de lignes pour definir les selections
420					 	// mais les compte dans la fonction length()
421						lenSelection = selection.length - fixIeBug(selection);
422						// si le caractere avant mon debut est un saut le ligne,
423						// ie ne le prendra pas en compte dans la selection.
424						// il faut pouvoir le connaitre.
425						if (caretPosition) {
426							set(caretPosition - 1, 2);
427							sautAvantIE = fixIeBug(document.selection.createRange().text);
428						}
429						// idem pour le caractere apres la ligne !
430						set(caretPosition, 2);
431						sautApresIE = fixIeBug(document.selection.createRange().text);
432						// selection avant
433 						set(0,caretPosition);
434 						before = document.selection.createRange().text;
435 						// selection apres
436 						set(caretPosition + lenSelection, textarea.value.length);
437 						after = document.selection.createRange().text;
438 						// remettre la veritable selection
439 						set(caretPosition, lenSelection);
440 						selection = document.selection.createRange().text;
441				} else {
442					before = textarea.value.substring(0, caretPosition);
443					after = textarea.value.substring(caretPosition + selection.length - fixIeBug(selection));
444				}
445
446				before = before.split(pattern);
447				after = after.split(pattern);
448				// ajouter ce fichu saut de ligne pour IE
449				if (sautAvantIE) before.push("");
450				if (sautApresIE) after.unshift("");
451
452			}
453
454			function selectionSave(){
455				nb_before = before ? before[before.length-1].length : 0;
456				nb_after = after ? after[0].length : 0;
457
458				nb = nb_before + selection.length + nb_after - fixIeBug(selection);
459				caretPosition =  caretPosition - nb_before;
460
461				set(caretPosition, nb);
462				get();
463				$.extend(hash, { selection:selection, caretPosition:caretPosition, scrollPosition:scrollPosition } );
464			}
465
466			// define markup to insert
467			function markup(button) {
468				var len, j, n, i;
469				hash = clicked = button;
470				get();
471				
472				$.extend(hash, {	line:"", 
473						 			root:options.root,
474									textarea:textarea, 
475									selection:(selection||''), 
476									caretPosition:caretPosition,
477									ctrlKey:ctrlKey, 
478									shiftKey:shiftKey, 
479									altKey:altKey
480								}
481							);
482							
483				// corrections des selections pour que
484				// - soit le curseur ne change pas
485				// - soit on prend le mot complet (si pas de selection)
486				// - soit on prend la ligne (avant, apres la selection)
487				if (button.selectionType) {
488
489					if (button.selectionType == "word") {
490						if (!selection) {
491							selectWord();
492						} else {
493							// win/ff add space on double click ? (hum, seems strange)
494							selectionRemoveLast(/\s/);
495						}
496					}
497					if (button.selectionType == "line") {
498						selectLine();
499					}
500					// horrible chose, mais tellement plus pratique
501					// car on ne peut pas de l'exerieur (json) utiliser
502					// les fonctions internes de markitup
503					if (button.selectionType == "return"){
504						// le calcul de before et after sous IE
505						// necessitant de creer des selections
506						// c'est extremement vilain a chaque saut de ligne
507						// des qu'il y a un texte volumineux.
508						// on dit tant pis pour lui.
509						if (!browser.msie) {
510							selectionBeforeAfter(/\r?\n/);
511							before_last = before[before.length-1];
512							after = '';
513							// gestion des listes -# et -* 
514							if (r = before_last.match(/^-([*#]+) ?(.*)$/)) {
515								if (r[2]) {
516									button.replaceWith = "\n-"+r[1]+' ';
517									before_last = '';
518								} else {
519									// supprime le -* present
520									// (before le fera)
521									button.replaceWith = "\n";
522								}
523							} else {
524								before_last = '';
525								button.replaceWith = "\n";
526							}
527							before[before.length-1] = before_last;
528							selectionSave();
529						}
530					}
531				}
532				// / fin corrections
533				
534				// callbacks before insertion
535				prepare(options.beforeInsert);
536				prepare(clicked.beforeInsert);
537				if ((ctrlKey === true && shiftKey === true) || button.multiline === true) {
538					prepare(clicked.beforeMultiInsert);
539				}
540				$.extend(hash, { line:1 });
541
542				if ((ctrlKey === true && shiftKey === true) || button.forceMultiline === true) {
543					lines = selection.split(/\r?\n/);
544					for (j = 0, n = lines.length, i = 0; i < n; i++) {
545						// si une seule ligne, on se fiche de savoir qu'elle est vide,
546						// c'est volontaire si on clique le bouton
547						if (n == 1 || $.trim(lines[i]) !== '') {
548							$.extend(hash, { line:++j, selection:lines[i] } );
549							lines[i] = build(lines[i]).block;
550						} else {
551							lines[i] = "";
552						}
553					}
554
555					string = { block:lines.join('\n')};
556					start = caretPosition;
557					len = string.block.length + ((browser.opera) ? n-1 : 0);
558				} else if (ctrlKey === true) {
559					string = build(selection);
560					start = caretPosition + string.openWith.length;
561					len = string.block.length - string.openWith.length - string.closeWith.length;
562					len = len - (string.block.match(/ $/) ? 1 : 0);
563					len -= fixIeBug(string.block);
564				} else if (shiftKey === true) {
565					string = build(selection);
566					start = caretPosition;
567					len = string.block.length;
568					len -= fixIeBug(string.block);
569				} else {
570					string = build(selection);
571					start = caretPosition + string.block.length ;
572					len = 0;
573					start -= fixIeBug(string.block);
574				}
575				
576				if ((selection === '' && string.replaceWith === '')) {
577					caretOffset += fixOperaBug(string.block);
578
579					start = caretPosition + string.openBlockWith.length + string.openWith.length;
580					len = string.block.length - string.openBlockWith.length - string.openWith.length - string.closeWith.length - string.closeBlockWith.length;
581
582					caretOffset = $$.val().substring(caretPosition,  $$.val().length).length;
583					caretOffset -= fixOperaBug($$.val().substring(0, caretPosition));
584				}
585				$.extend(hash, { caretPosition:caretPosition, scrollPosition:scrollPosition } );
586
587				if (string.block !== selection && abort === false) {
588					insert(string.block);
589					set(start, len);
590				} else {
591					caretOffset = -1;
592				}
593				get();
594
595				$.extend(hash, { line:'', selection:selection });
596
597				// callbacks after insertion
598				if ((ctrlKey === true && shiftKey === true) || button.multiline === true) {
599					prepare(clicked.afterMultiInsert);
600				}
601
602				prepare(clicked.afterInsert);
603				prepare(options.afterInsert);
604
605				// refresh preview if opened
606				if (previewWindow && options.previewAutoRefresh) {
607					refreshPreview(); 
608				}
609
610				// Triggers an input event to allow other scripts to react.
611				textarea.dispatchEvent(new Event('input'));
612
613				// reinit keyevent
614				shiftKey = altKey = ctrlKey = abort = false;
615			}
616
617			// Substract linefeed in Opera
618			function fixOperaBug(string) {
619				if (browser.opera) {
620					return string.length - string.replace(/\n*/g, '').length;
621				}
622				return 0;
623			}
624			// Substract linefeed in IE
625			function fixIeBug(string) {
626				if (browser.msie) {
627					return string.length - string.replace(/\r*/g, '').length;
628				}
629				return 0;
630			}
631				
632			// add markup
633			function insert(block) {	
634				if (document.selection) {
635					var newSelection = document.selection.createRange();
636					newSelection.text = block;
637				} else {
638					textarea.value =  textarea.value.substring(0, caretPosition)  + block + textarea.value.substring(caretPosition + selection.length, textarea.value.length);
639				}
640			}
641
642			// set a selection
643			function set(start, len) {
644				if (textarea.createTextRange){
645					// quick fix to make it work on Opera 9.5
646					if (browser.opera && browser.version >= 9.5 && len == 0) {
647						return false;
648					}
649					range = textarea.createTextRange();
650					range.collapse(true);
651					range.moveStart('character', start); 
652					range.moveEnd('character', len); 
653					range.select();
654				} else if (textarea.setSelectionRange ){
655					textarea.setSelectionRange(start, start + len);
656				}
657				textarea.scrollTop = scrollPosition;
658				textarea.focus();
659			}
660
661			// get the selection
662			function get() {
663				textarea.focus();
664
665				scrollPosition = textarea.scrollTop;
666				if (document.selection) {
667					selection = document.selection.createRange().text;
668					if (browser.msie) { // ie
669						var range = document.selection.createRange(), rangeCopy = range.duplicate();
670						rangeCopy.moveToElementText(textarea);
671						caretPosition = -1;
672						while(rangeCopy.inRange(range)) {
673							rangeCopy.moveStart('character');
674							caretPosition ++;
675						}
676					} else { // opera
677						caretPosition = textarea.selectionStart;
678					}
679				} else { // gecko & webkit
680					caretPosition = textarea.selectionStart;
681
682					selection = textarea.value.substring(caretPosition, textarea.selectionEnd);
683				} 
684				return selection;
685			}
686
687			// open preview window
688			function preview() {
689				if (typeof options.previewHandler === 'function') {
690					previewWindow = true;
691				} else if (options.previewInElement) {
692					previewWindow = $(options.previewInElement);
693				} else if (!previewWindow || previewWindow.closed) {
694					if (options.previewInWindow) {
695						previewWindow = window.open('', 'preview', options.previewInWindow);
696						$(window).unload(function() {
697							previewWindow.close();
698						});
699					} else {
700						iFrame = $('<iframe class="markItUpPreviewFrame"></iframe>');
701						if (options.previewPosition == 'after') {
702							iFrame.insertAfter(footer);
703						} else {
704							iFrame.insertBefore(header);
705						}	
706						previewWindow = iFrame[iFrame.length - 1].contentWindow || frame[iFrame.length - 1];
707					}
708				} else if (altKey === true) {
709					if (iFrame) {
710						iFrame.remove();
711					} else {
712						previewWindow.close();
713					}
714					previewWindow = iFrame = false;
715				}
716				if (!options.previewAutoRefresh) {
717					refreshPreview(); 
718				}
719				if (options.previewInWindow) {
720					previewWindow.focus();
721				}
722			}
723
724			// refresh Preview window
725			function refreshPreview() {
726 				renderPreview();
727			}
728
729			function renderPreview() {
730				var phtml;
731				var parsedData = $$.val();
732				if (options.previewParser && typeof options.previewParser === 'function') {
733					parsedData = options.previewParser(parsedData); 
734				}
735				if (options.previewHandler && typeof options.previewHandler === 'function') {
736					options.previewHandler(parsedData);
737				} else if (options.previewParserPath !== '') {
738					$.ajax({
739						type: options.previewParserAjaxType,
740						dataType: 'text',
741						global: false,
742						url: options.previewParserPath,
743						data: options.previewParserVar+'='+encodeURIComponent(parsedData),
744						success: function(data) {
745							writeInPreview( localize(data, 1) ); 
746						}
747					});
748				} else {
749					if (!template) {
750						$.ajax({
751							url: options.previewTemplatePath,
752							dataType: 'text',
753							global: false,
754							success: function(data) {
755								writeInPreview( localize(data, 1).replace(/<!-- content -->/g, parsedData) );
756							}
757						});
758					}
759				}
760				return false;
761			}
762			
763			function writeInPreview(data) {
764				if (options.previewInElement) {
765					$(options.previewInElement).html(data);
766				} else if (previewWindow && previewWindow.document) {			
767					try {
768						sp = previewWindow.document.documentElement.scrollTop
769					} catch(e) {
770						sp = 0;
771					}	
772					previewWindow.document.open();
773					previewWindow.document.write(data);
774					previewWindow.document.close();
775					previewWindow.document.documentElement.scrollTop = sp;
776				}
777			}
778			
779			// set keys pressed
780			function keyPressed(e) { 
781				shiftKey = e.shiftKey;
782				altKey = e.altKey;
783				ctrlKey = (!(e.altKey && e.ctrlKey)) ? (e.ctrlKey || e.metaKey) : false;
784
785				if (e.type === 'keydown') {
786					if (ctrlKey === true) {
787						li = $('a[accesskey="'+((e.keyCode == 13) ? '\\n' : String.fromCharCode(e.keyCode))+'"]', header).parent('li');
788						if (li.length !== 0) {
789							ctrlKey = false;
790							setTimeout(function() {
791								li.triggerHandler('mouseup');
792							},1);
793							return false;
794						}
795					}
796					
797 					// si opera, on s'embete pas, il cree plus de problemes qu'autre chose
798 					// car il ne prend pas en compte l'arret de ces evenements
799					if (!browser.opera) {
800						if (e.keyCode === 13 || e.keyCode === 10) { // Enter key
801							if (ctrlKey === true) {  // Enter + Ctrl
802								ctrlKey = false;
803								markup(options.onCtrlEnter);
804								if (!options.onCtrlEnter.keepDefault) {
805									textarea.dispatchEvent(new Event('input'));
806								}
807								return options.onCtrlEnter.keepDefault;
808							} else if (shiftKey === true) { // Enter + Shift
809								shiftKey = false;
810								markup(options.onShiftEnter);
811								if (!options.onShiftEnter.keepDefault) {
812									textarea.dispatchEvent(new Event('input'));
813								}
814								return options.onShiftEnter.keepDefault;
815							} else { // only Enter
816								markup(options.onEnter);
817								if (!options.onEnter.keepDefault) {
818									textarea.dispatchEvent(new Event('input'));
819								}
820								return options.onEnter.keepDefault;
821							}
822						}
823						
824						if (e.keyCode === 9) { // Tab key
825							if (shiftKey == true || ctrlKey == true || altKey == true) {
826								// permettre un retour a l'action naturelle
827								// du navigateur via shift+tab 
828								return false; 
829							}
830							if (caretOffset !== -1) {
831								get();
832								caretOffset = $$.val().length - caretOffset;
833								set(caretOffset, 0);
834								caretOffset = -1;
835								return false;
836							} else {
837								markup(options.onTab);
838								return options.onTab.keepDefault;
839							}
840						}
841					}
842				}
843			}
844
845			function remove() {
846				$$.off(".markItUp").removeClass('markItUpEditor');
847				$$.parent('div').parent('div.markItUp').parent('div').replaceWith($$);
848
849				var relativeRef = $$.parent('div').parent('div.markItUp').parent('div');
850				if (relativeRef.length) {
851				    relativeRef.replaceWith($$);
852				}
853				
854				$$.data('markItUp', null);
855			}
856
857			init();
858		});
859	};
860
861	$.fn.markItUpRemove = function() {
862		return this.each(function() {
863				$(this).markItUp('remove');
864			}
865		);
866	};
867
868	$.markItUp = function(settings) {
869		var options = { target:false };
870		$.extend(options, settings);
871		if (options.target) {
872			return $(options.target).each(function() {
873				$(this).focus();
874				$(this).trigger('insertion', [options]);
875			});
876		} else {
877			$('textarea').trigger('insertion', [options]);
878		}
879	};
880})(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.