Commit RELEASE-NOTES line for the wgCategories js variable I added some time ago.
[lhc/web/wiklou.git] / js2 / mwEmbed / jquery / plugins / jquery.textSelection.js
1 /**
2 * These plugins provide extra functionality for interaction with textareas.
3 */
4 ( function( $ ) { $.fn.extend( {
5 /**
6 * Get the currently selected text in this textarea. Will focus the textarea
7 * in some browsers (IE/Opera)
8 */
9 textSelection: function() {
10 var e = this.jquery ? this[0] : this;
11 var retval = '';
12 if ( e.style.display == 'none' ) {
13 // Do nothing
14 } else if ( document.selection && document.selection.createRange ) {
15 e.focus();
16 var range = document.selection.createRange();
17 retval = range.text;
18 } else if ( e.selectionStart || e.selectionStart == '0' ) {
19 retval = e.value.substring( e.selectionStart, e.selectionEnd );
20 }
21 return retval;
22 },
23 /**
24 * Ported from skins/common/edit.js by Trevor Parscal
25 * (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org
26 *
27 * Inserts text at the begining and end of a text selection, optionally
28 * inserting text at the caret when selection is empty.
29 *
30 * @param pre Text to insert before selection
31 * @param peri Text to insert at caret if selection is empty
32 * @param post Text to insert after selection
33 * @param ownline If true, put the inserted text is on its own line
34 * @param replace If true, replaces any selected text with peri; if false, peri is ignored and selected text is left alone
35 */
36 encapsulateSelection: function( pre, peri, post, ownline, replace ) {
37 return this.each( function() {
38 /**
39 * Check if the selected text is the same as the insert text
40 */
41 function checkSelectedText() {
42 if ( !selText ) {
43 selText = peri;
44 isSample = true;
45 } else if ( replace ) {
46 selText = peri;
47 } else if ( selText.charAt( selText.length - 1 ) == ' ' ) {
48 // Exclude ending space char
49 selText = selText.substring(0, selText.length - 1);
50 post += ' ';
51 }
52 }
53 var selText = $(this).getSelection();
54 var isSample = false;
55 if ( this.style.display == 'none' ) {
56 // Do nothing
57 } else if ( this.selectionStart || this.selectionStart == '0' ) {
58 // Mozilla/Opera
59 $(this).focus();
60 var startPos = this.selectionStart;
61 var endPos = this.selectionEnd;
62 checkSelectedText();
63 if ( ownline ) {
64 if ( startPos != 0 && this.value.charAt( startPos - 1 ) != "\n" ) {
65 pre = "\n" + pre;
66 }
67 if ( this.value.charAt( endPos ) != "\n" ) {
68 post += "\n";
69 }
70 }
71 this.value = this.value.substring( 0, startPos ) + pre + selText + post + this.value.substring( endPos, this.value.length );
72 if ( window.opera ) {
73 pre = pre.replace( /\r?\n/g, "\r\n" );
74 selText = selText.replace( /\r?\n/g, "\r\n" );
75 post = post.replace( /\r?\n/g, "\r\n" );
76 }
77 if ( isSample ) {
78 this.selectionStart = startPos + pre.length;
79 this.selectionEnd = startPos + pre.length + selText.length;
80 } else {
81 this.selectionStart = startPos + pre.length + selText.length + post.length;
82 this.selectionEnd = this.selectionStart;
83 }
84 } else if ( document.selection && document.selection.createRange ) {
85 // IE
86 $(this).focus();
87 var range = document.selection.createRange();
88 if ( ownline && range.moveStart ) {
89 var range2 = document.selection.createRange();
90 range2.collapse();
91 range2.moveStart( 'character', -1 );
92 // FIXME: Which check is correct?
93 if ( range2.text != "\r" && range2.text != "\n" && range2.text != "" ) {
94 pre = "\n" + pre;
95 }
96 var range3 = document.selection.createRange();
97 range3.collapse( false );
98 range3.moveEnd( 'character', 1 );
99 if ( range3.text != "\r" && range3.text != "\n" && range3.text != "" ) {
100 post += "\n";
101 }
102 }
103 checkSelectedText();
104 range.text = pre + selText + post;
105 if ( isSample && range.moveStart ) {
106 range.moveStart( 'character', - post.length - selText.length );
107 range.moveEnd( 'character', - post.length );
108 }
109 range.select();
110 }
111 // Scroll the textarea to the inserted text
112 $(this).scrollToCaretPosition();
113 $(this).trigger( 'encapsulateSelection', [ pre, peri, post, ownline, replace ] );
114 });
115 },
116 /**
117 * Ported from Wikia's LinkSuggest extension
118 * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
119 * Some code copied from
120 * http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
121 *
122 * Get the position (in resolution of bytes not nessecarily characters)
123 * in a textarea
124 */
125 getCaretPosition: function( startAndEnd ) {
126 function getCaret( e ) {
127 var caretPos = 0, endPos = 0;
128 if ( $.browser.msie ) {
129 // IE Support
130 var postFinished = false;
131 var periFinished = false;
132 var postFinished = false;
133 var preText, rawPreText, periText;
134 var rawPeriText, postText, rawPostText;
135 // Create range containing text in the selection
136 var periRange = document.selection.createRange().duplicate();
137 // Create range containing text before the selection
138 var preRange = document.body.createTextRange();
139 // Select all the text
140 preRange.moveToElementText(e);
141 // Move the end where we need it
142 preRange.setEndPoint("EndToStart", periRange);
143 // Create range containing text after the selection
144 var postRange = document.body.createTextRange();
145 // Select all the text
146 postRange.moveToElementText(e);
147 // Move the start where we need it
148 postRange.setEndPoint("StartToEnd", periRange);
149 // Load the text values we need to compare
150 preText = rawPreText = preRange.text;
151 periText = rawPeriText = periRange.text;
152 postText = rawPostText = postRange.text;
153 /*
154 * Check each range for trimmed newlines by shrinking the range by 1
155 * character and seeing if the text property has changed. If it has
156 * not changed then we know that IE has trimmed a \r\n from the end.
157 */
158 do {
159 if ( !postFinished ) {
160 if ( preRange.compareEndPoints( "StartToEnd", preRange ) == 0 ) {
161 postFinished = true;
162 } else {
163 preRange.moveEnd( "character", -1 )
164 if ( preRange.text == preText ) {
165 rawPreText += "\r\n";
166 } else {
167 postFinished = true;
168 }
169 }
170 }
171 if ( !periFinished ) {
172 if ( periRange.compareEndPoints( "StartToEnd", periRange ) == 0 ) {
173 periFinished = true;
174 } else {
175 periRange.moveEnd( "character", -1 )
176 if ( periRange.text == periText ) {
177 rawPeriText += "\r\n";
178 } else {
179 periFinished = true;
180 }
181 }
182 }
183 if ( !postFinished ) {
184 if ( postRange.compareEndPoints("StartToEnd", postRange) == 0 ) {
185 postFinished = true;
186 } else {
187 postRange.moveEnd( "character", -1 )
188 if ( postRange.text == postText ) {
189 rawPostText += "\r\n";
190 } else {
191 postFinished = true;
192 }
193 }
194 }
195 } while ( ( !postFinished || !periFinished || !postFinished ) );
196 caretPos = rawPreText.replace( /\r\n/g, "\n" ).length;
197 endPos = caretPos + rawPeriText.replace( /\r\n/g, "\n" ).length;
198 } else if ( e.selectionStart || e.selectionStart == '0' ) {
199 // Firefox support
200 caretPos = e.selectionStart;
201 endPos = e.selectionEnd;
202 }
203 return startAndEnd ? [ caretPos, endPos ] : caretPos;
204 }
205 return getCaret( this.get( 0 ) );
206 },
207 setSelection: function( start, end ) {
208 if ( typeof end == 'undefined' )
209 end = start;
210 return this.each( function() {
211 if ( this.selectionStart || this.selectionStart == '0' ) {
212 // Opera 9.0 doesn't allow setting selectionStart past
213 // selectionEnd; any attempts to do that will be ignored
214 // Make sure to set them in the right order
215 if ( start > this.selectionEnd ) {
216 this.selectionEnd = end;
217 this.selectionStart = start;
218 } else {
219 this.selectionStart = start;
220 this.selectionEnd = end;
221 }
222 } else if ( document.body.createTextRange ) {
223 var selection = document.body.createTextRange();
224 selection.moveToElementText( this );
225 var length = selection.text.length;
226 selection.moveStart( 'character', start );
227 selection.moveEnd( 'character', -length + end );
228 selection.select();
229 }
230 });
231 },
232 /**
233 * Ported from Wikia's LinkSuggest extension
234 * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
235 *
236 * Scroll a textarea to the current cursor position. You can set the cursor
237 * position with setSelection()
238 * @param force boolean Whether to force a scroll even if the caret position
239 * is already visible. Defaults to false
240 */
241 scrollToCaretPosition: function( force ) {
242 function getLineLength( e ) {
243 return Math.floor( e.scrollWidth / ( $.os.name == 'linux' ? 7 : 8 ) );
244 }
245 function getCaretScrollPosition( e ) {
246 // FIXME: This functions sucks and is off by a few lines most
247 // of the time. It should be replaced by something decent.
248 var text = e.value.replace( /\r/g, "" );
249 var caret = $( e ).getCaretPosition();
250 var lineLength = getLineLength( e );
251 var row = 0;
252 var charInLine = 0;
253 var lastSpaceInLine = 0;
254 for ( i = 0; i < caret; i++ ) {
255 charInLine++;
256 if ( text.charAt( i ) == " " ) {
257 lastSpaceInLine = charInLine;
258 } else if ( text.charAt( i ) == "\n" ) {
259 lastSpaceInLine = 0;
260 charInLine = 0;
261 row++;
262 }
263 if ( charInLine > lineLength ) {
264 if ( lastSpaceInLine > 0 ) {
265 charInLine = charInLine - lastSpaceInLine;
266 lastSpaceInLine = 0;
267 row++;
268 }
269 }
270 }
271 var nextSpace = 0;
272 for ( j = caret; j < caret + lineLength; j++ ) {
273 if (
274 text.charAt( j ) == " " ||
275 text.charAt( j ) == "\n" ||
276 caret == text.length
277 ) {
278 nextSpace = j;
279 break;
280 }
281 }
282 if ( nextSpace > lineLength && caret <= lineLength ) {
283 charInLine = caret - lastSpaceInLine;
284 row++;
285 }
286 return ( $.os.name == 'mac' ? 13 : ( $.os.name == 'linux' ? 15 : 16 ) ) * row;
287 }
288 return this.each(function() {
289 if ( this.selectionStart || this.selectionStart == '0' ) {
290 // Mozilla
291 var scroll = getCaretScrollPosition( this );
292 if ( force || scroll < $(this).scrollTop() ||
293 scroll > $(this).scrollTop() + $(this).height() )
294 $(this).scrollTop( scroll );
295 } else if ( document.selection && document.selection.createRange ) {
296 // IE / Opera
297 /*
298 * IE automatically scrolls the selected text to the
299 * bottom of the textarea at range.select() time, except
300 * if it was already in view and the cursor position
301 * wasn't changed, in which case it does nothing. To
302 * cover that case, we'll force it to act by moving one
303 * character back and forth.
304 */
305 var range = document.selection.createRange();
306 var pos = $(this).getCaretPosition();
307 var oldScrollTop = this.scrollTop;
308 range.moveToElementText( this );
309 range.collapse();
310 range.move( 'character', pos + 1);
311 range.select();
312 if ( this.scrollTop != oldScrollTop )
313 this.scrollTop += range.offsetTop;
314 else if ( force ) {
315 range.move( 'character', -1 );
316 range.select();
317 }
318 }
319 $(this).trigger( 'scrollToPosition' );
320 } );
321 }
322
323 } ); } )( jQuery );