Merge "Whitelist the <wbr> element."
[mediawiki.git] / resources / mediawiki.action / mediawiki.action.edit.js
blob38d4bb8d9b57eb5da8a8a53ea4d87cbd07bd0d36
1 /**
2  * Interface for the classic edit toolbar.
3  *
4  * @class mw.toolbar
5  * @singleton
6  */
7 ( function ( mw, $ ) {
8         var toolbar, isReady, $toolbar, queue, slice, $currentFocused;
10         /**
11          * Internal helper that does the actual insertion of the button into the toolbar.
12          *
13          * See #addButton for parameter documentation.
14          *
15          * @private
16          */
17         function insertButton( b, speedTip, tagOpen, tagClose, sampleText, imageId ) {
18                 // Backwards compatibility
19                 if ( typeof b !== 'object' ) {
20                         b = {
21                                 imageFile: b,
22                                 speedTip: speedTip,
23                                 tagOpen: tagOpen,
24                                 tagClose: tagClose,
25                                 sampleText: sampleText,
26                                 imageId: imageId
27                         };
28                 }
29                 var $image = $( '<img>' ).attr( {
30                         width : 23,
31                         height: 22,
32                         src   : b.imageFile,
33                         alt   : b.speedTip,
34                         title : b.speedTip,
35                         id    : b.imageId || undefined,
36                         'class': 'mw-toolbar-editbutton'
37                 } ).click( function () {
38                         toolbar.insertTags( b.tagOpen, b.tagClose, b.sampleText );
39                         return false;
40                 } );
42                 $toolbar.append( $image );
43         }
45         isReady = false;
46         $toolbar = false;
47         /**
48          * @private
49          * @property {Array}
50          * Contains button objects (and for backwards compatibilty, it can
51          * also contains an arguments array for insertButton).
52          */
53         queue = [];
54         slice = queue.slice;
56         toolbar = {
58                 /**
59                  * Add buttons to the toolbar.
60                  *
61                  * Takes care of race conditions and time-based dependencies
62                  * by placing buttons in a queue if this method is called before
63                  * the toolbar is created.
64                  *
65                  * For compatiblity, passing the properties listed below as separate arguments
66                  * (in the listed order) is also supported.
67                  *
68                  * @param {Object} button Object with the following properties:
69                  * @param {string} button.imageFile
70                  * @param {string} button.speedTip
71                  * @param {string} button.tagOpen
72                  * @param {string} button.tagClose
73                  * @param {string} button.sampleText
74                  * @param {string} [button.imageId]
75                  */
76                 addButton: function () {
77                         if ( isReady ) {
78                                 insertButton.apply( toolbar, arguments );
79                         } else {
80                                 // Convert arguments list to array
81                                 queue.push( slice.call( arguments ) );
82                         }
83                 },
84                 /**
85                  * Example usage:
86                  *     addButtons( [ { .. }, { .. }, { .. } ] );
87                  *     addButtons( { .. }, { .. } );
88                  *
89                  * @param {Object|Array} [buttons...] An array of button objects or the first
90                  *  button object in a list of variadic arguments.
91                  */
92                 addButtons: function ( buttons ) {
93                         if ( !$.isArray( buttons ) ) {
94                                 buttons = slice.call( arguments );
95                         }
96                         if ( isReady ) {
97                                 $.each( buttons, function () {
98                                         insertButton( this );
99                                 } );
100                         } else {
101                                 // Push each button into the queue
102                                 queue.push.apply( queue, buttons );
103                         }
104                 },
106                 /**
107                  * Apply tagOpen/tagClose to selection in currently focused textarea.
108                  *
109                  * Uses `sampleText` if selection is empty.
110                  *
111                  * @param {string} tagOpen
112                  * @param {string} tagClose
113                  * @param {string} sampleText
114                  */
115                 insertTags: function ( tagOpen, tagClose, sampleText ) {
116                         if ( $currentFocused && $currentFocused.length ) {
117                                 $currentFocused.textSelection(
118                                         'encapsulateSelection', {
119                                                 pre: tagOpen,
120                                                 peri: sampleText,
121                                                 post: tagClose
122                                         }
123                                 );
124                         }
125                 },
127                 // For backwards compatibility,
128                 // Called from EditPage.php, maybe in other places as well.
129                 init: function () {}
130         };
132         // Legacy (for compatibility with the code previously in skins/common.edit.js)
133         window.addButton = toolbar.addButton;
134         window.insertTags = toolbar.insertTags;
136         // Explose API publicly
137         mw.toolbar = toolbar;
139         $( function () {
140                 var buttons, i, b, $iframe, editBox, scrollTop, $editForm;
142                 // currentFocus is used to determine where to insert tags
143                 $currentFocused = $( '#wpTextbox1' );
145                 // Populate the selector cache for $toolbar
146                 $toolbar = $( '#toolbar' );
148                 // Legacy: Merge buttons from mwCustomEditButtons
149                 buttons = [].concat( queue, window.mwCustomEditButtons );
150                 // Clear queue
151                 queue.length = 0;
153                 for ( i = 0; i < buttons.length; i++ ) {
154                         b = buttons[i];
155                         if ( $.isArray( b ) ) {
156                                 // Forwarded arguments array from mw.toolbar.addButton
157                                 insertButton.apply( toolbar, b );
158                         } else {
159                                 // Raw object from mw.toolbar.addButtons or mwCustomEditButtons
160                                 insertButton( b );
161                         }
162                 }
164                 // This causes further calls to addButton to go to insertion directly
165                 // instead of to the queue.
166                 // It is important that this is after the one and only loop through
167                 // the the queue
168                 isReady = true;
170                 // Make sure edit summary does not exceed byte limit
171                 $( '#wpSummary' ).byteLimit( 255 );
173                 // Restore the edit box scroll state following a preview operation,
174                 // and set up a form submission handler to remember this state.
175                 editBox = document.getElementById( 'wpTextbox1' );
176                 scrollTop = document.getElementById( 'wpScrolltop' );
177                 $editForm = $( '#editform' );
178                 if ( $editForm.length && editBox && scrollTop ) {
179                         if ( scrollTop.value ) {
180                                 editBox.scrollTop = scrollTop.value;
181                         }
182                         $editForm.submit( function () {
183                                 scrollTop.value = editBox.scrollTop;
184                         });
185                 }
187                 // Apply to dynamically created textboxes as well as normal ones
188                 $( document ).on( 'focus', 'textarea, input:text', function () {
189                         $currentFocused = $( this );
190                 } );
192                 // HACK: make $currentFocused work with the usability iframe
193                 // With proper focus detection support (HTML 5!) this'll be much cleaner
194                 // TODO: Get rid of this WikiEditor code from MediaWiki core!
195                 $iframe = $( '.wikiEditor-ui-text iframe' );
196                 if ( $iframe.length > 0 ) {
197                         $( $iframe.get( 0 ).contentWindow.document )
198                                 // for IE
199                                 .add( $iframe.get( 0 ).contentWindow.document.body )
200                                 .focus( function () {
201                                         $currentFocused = $iframe;
202                                 } );
203                 }
204         });
206 }( mediaWiki, jQuery ) );