Index: trunk/extensions/Translate/utils/MessageTable.php |
— | — | @@ -145,6 +145,11 @@ |
146 | 146 | $extra = '<br />' . $optional; |
147 | 147 | } |
148 | 148 | |
| 149 | + $tqeData = array( |
| 150 | + 'data-title' => $title->getPrefixedText(), |
| 151 | + 'data-group' => $this->group->getId(), |
| 152 | + ); |
| 153 | + |
149 | 154 | $leftColumn = $this->getReviewButton( $m ) . $anchor . $tools['edit'] . $extra . $this->getReviewStatus( $m ); |
150 | 155 | |
151 | 156 | if ( $this->reviewMode && $original !== $message ) { |
— | — | @@ -154,11 +159,11 @@ |
155 | 160 | TranslateUtils::convertWhiteSpaceToHTML( $original ) ) |
156 | 161 | ); |
157 | 162 | |
158 | | - $output .= Xml::tags( 'tr', array( 'class' => 'new' ), |
| 163 | + $output .= Xml::tags( 'tr', array( 'class' => 'new tqe-inlineeditable' ) + $tqeData, |
159 | 164 | Xml::tags( 'td', $rclasses, TranslateUtils::convertWhiteSpaceToHTML( $message ) ) |
160 | 165 | ); |
161 | 166 | } else { |
162 | | - $output .= Xml::tags( 'tr', array( 'class' => 'def' ), |
| 167 | + $output .= Xml::tags( 'tr', array( 'class' => 'def tqe-inlineeditable' ) + $tqeData, |
163 | 168 | Xml::tags( 'td', null, $leftColumn ) . |
164 | 169 | Xml::tags( 'td', $rclasses, TranslateUtils::convertWhiteSpaceToHTML( $message ) ) |
165 | 170 | ); |
Index: trunk/extensions/Translate/utils/TranslationEditPage.php |
— | — | @@ -187,15 +187,23 @@ |
188 | 188 | * @return \array |
189 | 189 | */ |
190 | 190 | public static function jsEdit( Title $title, $group = "" ) { |
191 | | - global $wgUser; |
| 191 | + global $wgUser, $wgRequest; |
192 | 192 | |
193 | 193 | if ( !$wgUser->getOption( 'translate-jsedit' ) ) { |
194 | 194 | return array(); |
195 | 195 | } |
196 | 196 | |
| 197 | + if ( $wgRequest->getVal( 'translate-beta' ) ) { |
| 198 | + $onclick = 'jQuery( this ).closest( ".inlineeditable" ).dblclick(); return false;'; |
| 199 | + } else { |
| 200 | + $onclick = Xml::encodeJsCall( |
| 201 | + 'return mw.translate.openDialog', array( $title->getPrefixedDbKey(), $group ) |
| 202 | + ); |
| 203 | + } |
| 204 | + |
| 205 | + |
197 | 206 | return array( |
198 | | - 'onclick' => Xml::encodeJsCall( |
199 | | - 'return mw.translate.openDialog', array( $title->getPrefixedDbKey(), $group ) ), |
| 207 | + 'onclick' => $onclick, |
200 | 208 | 'title' => wfMsg( 'translate-edit-title', $title->getPrefixedText() ) |
201 | 209 | ); |
202 | 210 | } |
Index: trunk/extensions/Translate/resources/ext.translate.quickedit.js |
— | — | @@ -21,7 +21,8 @@ |
22 | 22 | (function ( $, mw, undefined ) { |
23 | 23 | "use strict"; |
24 | 24 | var dialogwidth = false, |
25 | | - translate; |
| 25 | + translate, |
| 26 | + preloads = {}; |
26 | 27 | |
27 | 28 | function MessageCheckUpdater( callback ) { |
28 | 29 | this.act = function() { |
— | — | @@ -68,23 +69,28 @@ |
69 | 70 | } |
70 | 71 | } |
71 | 72 | |
72 | | - function registerFeatures( dialog, form, page, group ) { |
| 73 | + function registerFeatures( callbacks, form, page, group ) { |
73 | 74 | // Enable the collapsible element |
74 | 75 | var $identical = $( '.mw-identical-title' ); |
75 | 76 | if ( $.isFunction( $identical.makeCollapsible ) ) { |
76 | 77 | $identical.makeCollapsible(); |
77 | 78 | } |
78 | 79 | |
79 | | - if ( mw.config.get( 'trlKeys' ) ) { |
80 | | - form.find( '.mw-translate-next' ).click( function() { |
81 | | - mw.translate.openNext( page, group ); |
82 | | - } ); |
83 | | - |
84 | | - form.find( '.mw-translate-skip' ).click( function() { |
85 | | - mw.translate.openNext( page, group ); |
86 | | - dialog.dialog( 'close' ); |
87 | | - return false; |
88 | | - } ); |
| 80 | + if ( mw.config.get( 'trlKeys' ) || $( '.tqe-inlineeditable' ).length ) { |
| 81 | + if ( callbacks.next === undefined ) { |
| 82 | + form.find( '.mw-translate-next, .mw-translate-skip' ).attr( 'disabled', 'disabled' ) |
| 83 | + } else { |
| 84 | + form.find( '.mw-translate-next' ).click( function () { |
| 85 | + callbacks.next && callbacks.next(); |
| 86 | + } ); |
| 87 | + form.find( '.mw-translate-skip,' ).click( function () { |
| 88 | + callbacks.close && callbacks.close(); |
| 89 | + callbacks.next && callbacks.next(); |
| 90 | + } ); |
| 91 | + form.find( '.mw-translate-close' ).click( function () { |
| 92 | + callbacks.close && callbacks.close(); |
| 93 | + } ); |
| 94 | + } |
89 | 95 | } else { |
90 | 96 | form.find( '.mw-translate-next, .mw-translate-skip' ) |
91 | 97 | .attr( 'disabled', 'disabled' ) |
— | — | @@ -92,7 +98,7 @@ |
93 | 99 | } |
94 | 100 | |
95 | 101 | form.find( '.mw-translate-history' ).click( function() { |
96 | | - window.open( mw.config.get( 'wgServer' ) + mw.config.get( 'wgScript' ) + '?action=history&title=' + form.find( 'input[name=title]' ).val() ); |
| 102 | + window.open( mw.util.wikiScript() + '?action=history&title=' + form.find( 'input[name=title]' ).val() ); |
97 | 103 | return false; |
98 | 104 | } ); |
99 | 105 | |
— | — | @@ -101,9 +107,13 @@ |
102 | 108 | window.open( $(this).attr( 'data-load-url' ) ); |
103 | 109 | return false; |
104 | 110 | } ); |
| 111 | + |
| 112 | + form.find( 'input, textarea' ).focus( function() { |
| 113 | + addAccessKeys( form ); |
| 114 | + } ); |
105 | 115 | |
106 | 116 | form.find( 'input#summary' ).focus( function() { |
107 | | - $(this).css( 'width', '85%' ); |
| 117 | + $( this ).css( 'width', '85%' ); |
108 | 118 | } ); |
109 | 119 | |
110 | 120 | var textarea = form.find( '.mw-translate-edit-area' ); |
— | — | @@ -126,18 +136,27 @@ |
127 | 137 | |
128 | 138 | translate = { |
129 | 139 | init: function() { |
130 | | - var height = $( window ).height() * 0.7; |
131 | 140 | dialogwidth = $( window ).width() * 0.8; |
132 | | - mw.util.addCSS( "/* Inserted by ext.translate.quickedit */\n" + |
133 | | - ".mw-sp-translate-edit-fields {\n" + |
134 | | - "\tmax-height: " + height + "px;\n" + |
135 | | - "\toverflow: auto\n}\n" |
136 | | - ); |
| 141 | + var $inlines = $( '.tqe-inlineeditable' ); |
| 142 | + $inlines.dblclick( mw.translate.inlineEditor ); |
| 143 | + |
| 144 | + var $first = $inlines.first(); |
| 145 | + if ( $first.length ) { |
| 146 | + var title = $first.data( 'title' ); |
| 147 | + var group = $first.data( 'group' ); |
| 148 | + mw.translate.loadEditor( title, group, $.noop ); |
| 149 | + } |
| 150 | + |
| 151 | + var prev = null; |
| 152 | + $inlines.each( function() { |
| 153 | + if ( prev ) { |
| 154 | + prev.next = this; |
| 155 | + } |
| 156 | + prev = this; |
| 157 | + } ) |
137 | 158 | }, |
138 | 159 | |
139 | 160 | openDialog: function( page, group ) { |
140 | | - var url = mw.config.get( 'wgScript' ) + '?title=Special:Translate/editpage&suggestions=async&page=$1&loadgroup=$2'; |
141 | | - url = url.replace( '$1', encodeURIComponent( page ) ).replace( '$2', encodeURIComponent( group ) ); |
142 | 161 | var id = 'jsedit' + page.replace( /[^a-zA-Z0-9_]/g, '_' ); |
143 | 162 | |
144 | 163 | var dialog = $( '#' + id ); |
— | — | @@ -146,20 +165,59 @@ |
147 | 166 | dialog.dialog( 'open' ); |
148 | 167 | return false; |
149 | 168 | } |
| 169 | + |
| 170 | + var dialog = $( '<div>' ).attr( 'id', id ).appendTo( $( 'body' ) ); |
| 171 | + |
| 172 | + var callbacks = {} |
| 173 | + callbacks.close = function () { dialog.dialog( 'close' ); }; |
| 174 | + callbacks.next = function () { mw.translate.openNext( page, group ); }; |
| 175 | + mw.translate.openEditor( dialog, page, group, callbacks ); |
150 | 176 | |
151 | | - $( '<div>' ).attr( 'id', id ).appendTo( $( 'body' ) ); |
152 | | - dialog = $( '#' + id ); |
| 177 | + dialog.dialog( { |
| 178 | + bgiframe: true, |
| 179 | + width: dialogwidth, |
| 180 | + title: page, |
| 181 | + position: 'top', |
| 182 | + resize: function() { $( '#' + id + ' textarea' ).width( '100%' ); }, |
| 183 | + resizeStop: function() { dialogwidth = $( '#' + id ).width(); }, |
| 184 | + } ); |
153 | 185 | |
| 186 | + return false; |
| 187 | + }, |
| 188 | + |
| 189 | + loadEditor: function( page, group, callback ) { |
| 190 | + // Try if it has been cached |
| 191 | + var id = 'preload-' + page.replace( /[^a-zA-Z0-9_]/g, '_' ); |
| 192 | + var preload = preloads[id]; |
| 193 | + if ( preload !== undefined ) { |
| 194 | + callback.call( preload ); |
| 195 | + delete preloads[id]; |
| 196 | + return; |
| 197 | + } |
| 198 | + |
| 199 | + var url = mw.util.wikiScript(); |
| 200 | + var params = { |
| 201 | + title: 'Special:Translate/editpage', |
| 202 | + suggestions: 'sync', |
| 203 | + page: page, |
| 204 | + loadgroup: group |
| 205 | + }; |
| 206 | + $.get( url, params, function ( data ) { |
| 207 | + preloads[id] = data; |
| 208 | + callback.call( data ); |
| 209 | + } ); |
| 210 | + }, |
| 211 | + |
| 212 | + openEditor: function( element, page, group, callbacks ) { |
| 213 | + var $target = $( element ); |
154 | 214 | var spinner = $( '<div>' ).attr( 'class', 'mw-ajax-loader' ); |
155 | | - dialog.html( $( '<div>' ).attr( 'class', 'mw-ajax-dialog' ).html( spinner ) ); |
| 215 | + $target.html( $( '<div>' ).attr( 'class', 'mw-ajax-dialog' ).html( spinner ) ); |
156 | 216 | |
157 | | - dialog.load( url, false, function() { |
158 | | - var form = $( '#' + id + ' form' ); |
159 | | - |
160 | | - registerFeatures( dialog, form, page, group ); |
161 | | - addAccessKeys( form ); |
162 | | - form.hide().slideDown(); |
163 | | - |
| 217 | + mw.translate.loadEditor( page, group, function() { |
| 218 | + $target.html( this ); |
| 219 | + callbacks.load && callbacks.load( $target ); |
| 220 | + var form = $target.find( 'form' ); |
| 221 | + registerFeatures( callbacks, form, page, group ); |
164 | 222 | form.ajaxForm( { |
165 | 223 | dataType: 'json', |
166 | 224 | success: function(json) { |
— | — | @@ -172,26 +230,14 @@ |
173 | 231 | } else if ( json.edit.result === 'Failure' ) { |
174 | 232 | alert( mw.msg( 'translate-js-save-failed' ) ); |
175 | 233 | } else if ( json.edit.result === 'Success' ) { |
176 | | - dialog.dialog( 'close' ); |
| 234 | + callbacks.close && callbacks.close(); |
| 235 | + callbacks.success && callbacks.success( form.find( '.mw-translate-edit-area' ).val() ); |
177 | 236 | } else { |
178 | 237 | alert( mw.msg( 'translate-js-save-failed' ) ); |
179 | 238 | } |
180 | 239 | } |
181 | 240 | } ); |
182 | 241 | } ); |
183 | | - |
184 | | - dialog.dialog( { |
185 | | - bgiframe: true, |
186 | | - width: dialogwidth, |
187 | | - title: page, |
188 | | - position: 'top', |
189 | | - resize: function() { $( '#' + id + ' textarea' ).width( '100%' ); }, |
190 | | - resizeStop: function() { dialogwidth = $( '#' + id ).width(); }, |
191 | | - focus: function() { addAccessKeys( dialog ); }, |
192 | | - close: function() { addAccessKeys( $([]) ); } |
193 | | - } ); |
194 | | - |
195 | | - return false; |
196 | 242 | }, |
197 | 243 | |
198 | 244 | openNext: function( title, group ) { |
— | — | @@ -212,6 +258,55 @@ |
213 | 259 | } |
214 | 260 | alert( mw.msg( 'translate-js-nonext' ) ); |
215 | 261 | return; |
| 262 | + }, |
| 263 | + |
| 264 | + inlineEditor: function () { |
| 265 | + var $this = $( this ); |
| 266 | + if ( $this.hasClass( 'tqe-editor-loaded' ) ) { |
| 267 | + // Editor is open, do not replace it |
| 268 | + return; |
| 269 | + } |
| 270 | + |
| 271 | + var current = $this.html(); |
| 272 | + var $target = $( '<td>' ).attr( { colspan: 2 } ); |
| 273 | + $this.html( $target ); |
| 274 | + $this.addClass( 'tqe-editor-loaded' ); |
| 275 | + |
| 276 | + var classes = $this.attr( 'class' ); |
| 277 | + var page = $this.data( 'title' ); |
| 278 | + var group = $this.data( 'group' ); |
| 279 | + var next = $( this.next ); |
| 280 | + var callbacks = {} |
| 281 | + callbacks.success = function ( text ) { |
| 282 | + // Update the cell value with the new translation |
| 283 | + $this.find( 'td' ).last().text( text ); |
| 284 | + }; |
| 285 | + callbacks.close = function () { |
| 286 | + $this.html( current ); |
| 287 | + $this.removeClass( 'tqe-editor-loaded' ); |
| 288 | + }; |
| 289 | + callbacks.load = function ( editor ) { |
| 290 | + var $header = $( '<div class="tqe-fakeheader"></div>' ); |
| 291 | + $header.text( page ); |
| 292 | + $header.append( '<input type=button class="mw-translate-close" value="X" />' ); |
| 293 | + |
| 294 | + $( editor ).find( 'form' ).prepend( $header ); |
| 295 | + }; |
| 296 | + if ( next.length ) { |
| 297 | + callbacks.next = function () { next.dblclick(); }; |
| 298 | + // Preload the next item |
| 299 | + var ntitle = next.data( 'title' ); |
| 300 | + var ngroup = next.data( 'group' ); |
| 301 | + mw.translate.loadEditor( ntitle, ngroup, $.noop ); |
| 302 | + } |
| 303 | + mw.translate.openEditor( $target, page, group, callbacks ); |
| 304 | + |
| 305 | + // Remove any text selection caused by double clicking |
| 306 | + var sel = window.getSelection ? window.getSelection() : document.selection; |
| 307 | + if ( sel ) { |
| 308 | + sel.removeAllRanges && sel.removeAllRanges(); |
| 309 | + sel.empty && sel.empty(); |
| 310 | + } |
216 | 311 | } |
217 | 312 | }; |
218 | 313 | |
Index: trunk/extensions/Translate/resources/ext.translate.quickedit.css |
— | — | @@ -7,6 +7,12 @@ |
8 | 8 | font-size: small; |
9 | 9 | } |
10 | 10 | |
| 11 | +.tqe-inlineeditable .mw-ajax-dialog { |
| 12 | + margin: 5px; |
| 13 | + margin-top: 15px; |
| 14 | + margin-bottom: 15px; |
| 15 | +} |
| 16 | + |
11 | 17 | .mw-sp-translate-edit-fields a { |
12 | 18 | color: blue; |
13 | 19 | } |
— | — | @@ -94,3 +100,14 @@ |
95 | 101 | .mw-sp-translate-latestchange { |
96 | 102 | padding-bottom: 0px; |
97 | 103 | } |
| 104 | + |
| 105 | + |
| 106 | +.tqe-fakeheader { |
| 107 | + border: 1px solid #CCC; |
| 108 | + padding: .75em; |
| 109 | + font-weight: bold; |
| 110 | +} |
| 111 | + |
| 112 | +.mw-translate-close { |
| 113 | + float: right; |
| 114 | +} |