Merging resourceloader branch into trunk. Full documentation is at http://www.mediawi...
[lhc/web/wiklou.git] / resources / mediawiki / legacy / mediawiki.legacy.edit.js
1 /*
2 * Legacy emulation for the now depricated skins/common/edit.js
3 */
4
5 ( function( $, mw ) {
6
7 /* Extension */
8
9 $.extend( true, mw.legacy, {
10
11 /* Global Variables */
12
13 'currentFocused': null,
14
15 /* Functions */
16
17 /**
18 * Generates the actual toolbar buttons with localized text we use it to avoid creating the toolbar
19 * where javascript is not enabled
20 */
21 'addButton': function( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId ) {
22 // Don't generate buttons for browsers which don't fully support it.
23 mw.legacy.mwEditButtons.push( {
24 'imageId': imageId,
25 'imageFile': imageFile,
26 'speedTip': speedTip,
27 'tagOpen': tagOpen,
28 'tagClose': tagClose,
29 'sampleText': sampleText
30 } );
31 },
32 /**
33 * Generates the actual toolbar buttons with localized text we use it to avoid creating the toolbar where JavaScript
34 * is not enabled
35 */
36 'mwInsertEditButton': function( parent, item ) {
37 var $image = $( '<img />' )
38 .attr( {
39 'width': 23,
40 'height': 22,
41 'class': 'mw-toolbar-editbutton',
42 'id': item.imageId ? item.imageId : null,
43 'src': = item.imageFile,
44 'border': 0,
45 'alt': item.speedTip,
46 'title': item.speedTip
47 } )
48 .css( 'cursor', 'pointer' )
49 .click( function() {
50 mw.legacy.insertTags( item.tagOpen, item.tagClose, item.sampleText );
51 // Click tracking
52 if ( typeof $.trackAction != 'undefined' ) {
53 $.trackAction( 'oldedit.' + item.speedTip.replace( / /g, '-' ) );
54 }
55 return false;
56 } )
57 .appendTo( $( parent ) );
58 return true;
59 },
60 /**
61 * Sets up the toolbar
62 */
63 'mwSetupToolbar': function() {
64 var $toolbar = $( '#toolbar' );
65 var $textbox = $( 'textarea' ).get( 0 );
66 if ( !$toolbar.length || !$textbox.length ) {
67 return false;
68 }
69 // Only check for selection capability if the textarea is visible - errors will occur otherwise - just because
70 // the textarea is not visible, doesn't mean we shouldn't build out the toolbar though - it might have been
71 // replaced with some other kind of control
72 if (
73 $textbox.is( ':visible' ) &&
74 !( document.selection && document.selection.createRange ) &&
75 textboxes[0].selectionStart === null
76 ) {
77 return false;
78 }
79 for ( var i = 0; i < mw.legacy.mwEditButtons.length; i++ ) {
80 mw.legacy.mwInsertEditButton( $toolbar, mw.legacy.mwEditButtons[i] );
81 }
82 for ( var i = 0; i < mw.legacy.mwCustomEditButtons.length; i++ ) {
83 mw.legacy.mwInsertEditButton( $toolbar, mw.legacy.mwCustomEditButtons[i] );
84 }
85 return true;
86 },
87 /**
88 * Apply tagOpen/tagClose to selection in textarea, use sampleText instead of selection if there is none
89 */
90 'insertTags': function( tagOpen, tagClose, sampleText ) {
91 function checkSelectedText() {
92 if ( !selText ) {
93 selText = sampleText;
94 isSample = true;
95 } else if ( selText.charAt( selText.length - 1 ) == ' ' ) { // exclude ending space char
96 selText = selText.substring( 0, selText.length - 1 );
97 tagClose += ' ';
98 }
99 }
100 var currentFocused = $( mw.legacy.currentFocused );
101 if (
102 typeof $.fn.textSelection != 'undefined' &&
103 ( $currentFocused.name().toLowerCase() == 'iframe' || $currentFocused.attr( 'id' ) == 'wpTextbox1' )
104 ) {
105 $j( '#wpTextbox1' ).textSelection(
106 'encapsulateSelection', { 'pre': tagOpen, 'peri': sampleText, 'post': tagClose }
107 );
108 return;
109 }
110 var $textarea;
111 if ( $( 'form[name=editform]' ) {
112 $textarea = $currentFocused;
113 } else {
114 // Some alternate form? take the first one we can find
115 $textarea = $( 'textarea' ).get( 0 );
116 }
117 var selText, isSample = false;
118 // Text selection implementation for IE and Opera
119 if ( document.selection && document.selection.createRange ) {
120 // Save window scroll position
121 if ( document.documentElement && document.documentElement.scrollTop ) {
122 var winScroll = document.documentElement.scrollTop
123 } else if ( document.body ) {
124 var winScroll = document.body.scrollTop;
125 }
126 // Get current selection
127 $textarea.focus();
128 var range = document.selection.createRange();
129 selText = range.text;
130 // Insert tags
131 checkSelectedText();
132 range.text = tagOpen + selText + tagClose;
133 // Mark sample text as selected
134 if ( isSample && range.moveStart ) {
135 if ( window.opera ) {
136 tagClose = tagClose.replace( /\n/g,'' );
137 }
138 range.moveStart( 'character', - tagClose.length - selText.length );
139 range.moveEnd( 'character', - tagClose.length );
140 }
141 range.select();
142 // Restore window scroll position
143 if ( document.documentElement && document.documentElement.scrollTop ) {
144 document.documentElement.scrollTop = winScroll;
145 } else if ( document.body ) {
146 document.body.scrollTop = winScroll;
147 }
148 }
149 // Text selection implementation for Mozilla, Chrome and Safari
150 else if ( $textarea[0].selectionStart || $textarea[0].selectionStart == '0' ) {
151 // Save textarea scroll position
152 var textScroll = $textarea.scrollTop;
153 // Get current selection
154 $textarea.focus();
155 var startPos = $textarea[0].selectionStart;
156 var endPos = $textarea[0].selectionEnd;
157 selText = $textarea.value.substring( startPos, endPos );
158 // Insert tags
159 checkSelectedText();
160 $textarea.val(
161 $textarea.val().substring( 0, startPos ) +
162 tagOpen + selText + tagClose +
163 $textarea.val().substring( endPos, $textarea.val().length )
164 );
165 // Set new selection
166 if ( isSample ) {
167 $textarea[0].selectionStart = startPos + tagOpen.length;
168 $textarea[0].selectionEnd = startPos + tagOpen.length + selText.length;
169 } else {
170 $textarea[0].selectionStart = startPos + tagOpen.length + selText.length + tagClose.length;
171 $textarea[0].selectionEnd = $textarea[0].selectionStart;
172 }
173 // Restore textarea scroll position
174 $textarea[0].scrollTop = textScroll;
175 }
176 },
177 /**
178 * Restore the edit box scroll state following a preview operation,
179 * and set up a form submission handler to remember this state
180 */
181 'scrollEditBox': function() {
182 var $textbox = $( '#wpTextbox1' );
183 var $scrollTop = $( '#wpScrolltop' );
184 var $editForm = $( '#editform' );
185 if ( $editForm.length && $textbox.length && $scrollTop.length ) {
186 if ( scrollTop.val() ) {
187 $textbox.scrollTop = $scrollTop.val();
188 }
189 $editForm.submit( function() {
190 $scrollTop.val( $textbox.scrollTop );
191 } );
192 }
193 }
194 } );
195
196 /* Initialization */
197
198 $( document ).ready( function() {
199 mw.legacy.scrollEditBox();
200 mw.legacy.mwSetupToolbar();
201 mw.legacy.currentFocused = $( '#wpTextbox1' ).get( 0 );
202 // http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html focus does not bubble normally, but using a
203 // trick we can do event delegation on the focus event on all text inputs to make the toolbox usable on all of them
204 $( '#editform' ).focus( function() {
205 $(this).each( function( e ) {
206 var elm = e.target || e.srcElement;
207 if ( !elm ) {
208 return;
209 }
210 var tagName = elm.tagName.toLowerCase();
211 var type = elm.type || '';
212 if ( tagName !== 'textarea' && tagName !== 'input' ) {
213 return;
214 }
215 if ( tagName === 'input' && type.toLowerCase() !== 'text' ) {
216 return;
217 }
218 mw.legacy.currentFocused = elm;
219 } );
220 } );
221 // HACK: make currentFocused work with the usability iframe - with proper focus detection support (HTML 5!) this'll
222 // be much cleaner
223 var $iframe = $j( '.wikiEditor-ui-text iframe' );
224 if ( $iframe.length > 0 ) {
225 $j( $iframe.get( 0 ).contentWindow.document )
226 // For IE
227 .add( $iframe.get( 0 ).contentWindow.document.body )
228 .focus( function() { mw.legacy.currentFocused = $iframe.get( 0 ); } );
229 }
230 // Make sure edit summary does not exceed byte limit
231 var $summary = $( '#wpSummary' );
232 if ( !$summary.length ) {
233 return;
234 }
235 // L must be capitalized in length
236 $summary.get( 0 ).maxLength = 250;
237 $summary.keypress( function( e ) {
238 // First check to see if this is actually a character key being pressed. Based on key-event info from
239 // http://unixpapa.com/js/key.html note === sign, if undefined, still could be a real key
240 if ( e.which === 0 || e.charCode === 0 || e.ctrlKey || e.altKey || e.metaKey ) {
241 // A special key (backspace, etc) so don't interefere.
242 return true;
243 }
244 // This basically figures out how many bytes a utf-16 string (which is what js sees) will take in utf-8 by
245 // replacing a 2 byte character with 2 *'s, etc, and counting that. Note, surogate (\uD800-\uDFFF) characters
246 // are counted as 2 bytes, since theres two of them and the actual character takes 4 bytes in utf-8 (2*2=4).
247 // Might not work perfectly in edge cases such as such as illegal sequences, but that should never happen.
248 len = summary.value
249 .replace(/[\u0080-\u07FF\uD800-\uDFFF]/g, '**')
250 .replace(/[\u0800-\uD7FF\uE000-\uFFFF]/g, '***')
251 .length;
252 // 247 as this doesn't count character about to be inserted.
253 if ( len > 247 ) {
254 if ( e.preventDefault ) {
255 e.preventDefault();
256 }
257 // IE
258 e.returnValue = false;
259 return false;
260 }
261 } );
262 } );
263
264 } )( jQuery, mediaWiki );