Index: trunk/extensions/UsabilityInitiative/images/wikiEditor/toolbar/insert-link-notexists.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/images/wikiEditor/toolbar/insert-link-notexists.png |
___________________________________________________________________ |
Name: svn:mime-type |
1 | 1 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/images/wikiEditor/toolbar/insert-link-exists.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/images/wikiEditor/toolbar/insert-link-exists.png |
___________________________________________________________________ |
Name: svn:mime-type |
2 | 2 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/images/wikiEditor/toolbar/insert-link-invalid.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UsabilityInitiative/images/wikiEditor/toolbar/insert-link-invalid.png |
___________________________________________________________________ |
Name: svn:mime-type |
3 | 3 | + application/octet-stream |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.hooks.php |
— | — | @@ -67,6 +67,10 @@ |
68 | 68 | 'edittoolbar-tool-link-ext-text', |
69 | 69 | 'edittoolbar-tool-link-insert', |
70 | 70 | 'edittoolbar-tool-link-cancel', |
| 71 | + 'edittoolbar-link-int-target-status-exists', |
| 72 | + 'edittoolbar-link-int-target-status-notexists', |
| 73 | + 'edittoolbar-link-int-target-status-invalid', |
| 74 | + 'edittoolbar-link-int-target-status-loading', |
71 | 75 | 'edittoolbar-tool-file', |
72 | 76 | 'edittoolbar-tool-file-pre', |
73 | 77 | 'edittoolbar-tool-file-example', |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.js |
— | — | @@ -69,12 +69,16 @@ |
70 | 70 | titleMsg: 'edittoolbar-tool-link-title', |
71 | 71 | id: 'edittoolbar-link-dialog', |
72 | 72 | // TODO: break this line |
73 | | - html: '<div id="edittoolbar-link-tabs"><ul><li><a href="#edittoolbar-link-dialog-tab-int" rel="edittoolbar-tool-link-int"></a></li><li><a href="#edittoolbar-link-dialog-tab-ext" rel="edittoolbar-tool-link-ext"></a></li></ul><div id="edittoolbar-link-dialog-tab-int"><form><label for="edittoolbar-link-int-target" rel="edittoolbar-tool-link-int-target"></label><input type="text" id="edittoolbar-link-int-target" style="display:block;" /><label for="edittoolbar-link-int-text" rel="edittoolbar-tool-link-int-text"></label><input type="text" id="edittoolbar-link-int-text" style="display:block;" /></form></div><div id="edittoolbar-link-dialog-tab-ext"><form><label for="edittoolbar-link-ext-target" rel="edittoolbar-tool-link-ext-target"></label><input type="text" id="edittoolbar-link-ext-target" style="display:block;" /><label for="edittoolbar-link-ext-text" rel="edittoolbar-tool-link-ext-text"></label><input type="text" id="edittoolbar-link-ext-text" style="display:block;" /></form></div></div>', |
| 73 | + html: '<div id="edittoolbar-link-tabs"><ul><li><a href="#edittoolbar-link-dialog-tab-int" rel="edittoolbar-tool-link-int"></a></li><li><a href="#edittoolbar-link-dialog-tab-ext" rel="edittoolbar-tool-link-ext"></a></li></ul><div id="edittoolbar-link-dialog-tab-int"><form><label for="edittoolbar-link-int-target" rel="edittoolbar-tool-link-int-target"></label> <input type="text" id="edittoolbar-link-int-target" /> <div id="edittoolbar-link-int-target-status" style="display: inline;"></div><br /><label for="edittoolbar-link-int-text" rel="edittoolbar-tool-link-int-text"></label> <input type="text" id="edittoolbar-link-int-text" /></form></div><div id="edittoolbar-link-dialog-tab-ext"><form><label for="edittoolbar-link-ext-target" rel="edittoolbar-tool-link-ext-target"></label> <input type="text" id="edittoolbar-link-ext-target" /><br /><label for="edittoolbar-link-ext-text" rel="edittoolbar-tool-link-ext-text"></label> <input type="text" id="edittoolbar-link-ext-text" /></form></div></div>', |
74 | 74 | init: function() { |
75 | 75 | $j(this).find( '[rel]' ).each( function() { |
76 | 76 | $j(this).html( gM( $j(this).attr( 'rel' ) ) ); |
77 | 77 | }); |
78 | 78 | $j( '#edittoolbar-link-tabs' ).tabs(); |
| 79 | + |
| 80 | + // Link int-target and int-text fields |
| 81 | + // This means mirroring the contents of int-target in int-text |
| 82 | + // as long as int-text itself hasn't been changed by the user |
79 | 83 | $j( '#edittoolbar-link-int-target' ).bind( 'keypress paste', function() { |
80 | 84 | // $j(this).val() is the old value, before the keypress |
81 | 85 | if ( $j( '#edittoolbar-link-int-text' ).data( 'untouched' ) ) |
— | — | @@ -84,6 +88,99 @@ |
85 | 89 | $j( '#edittoolbar-link-int-text' ).bind( 'keypress paste', function() { |
86 | 90 | $j(this).data( 'untouched', false ); |
87 | 91 | }); |
| 92 | + |
| 93 | + // Page existence check widget |
| 94 | + var existsImg = $j.wikiEditor.modules.toolbar.imgPath + 'insert-link-exists.png'; |
| 95 | + var notexistsImg = $j.wikiEditor.modules.toolbar.imgPath + 'insert-link-notexists.png'; |
| 96 | + var invalidImg = $j.wikiEditor.modules.toolbar.imgPath + 'insert-link-invalid.png'; |
| 97 | + var loadingImg = $j.wikiEditor.modules.toolbar.imgPath + 'loading.gif'; |
| 98 | + var existsMsg = gM( 'edittoolbar-link-int-target-status-exists' ); |
| 99 | + var notexistsMsg = gM( 'edittoolbar-link-int-target-status-notexists' ); |
| 100 | + var invalidMsg = gM( 'edittoolbar-link-int-target-status-invalid' ); |
| 101 | + var loadingMsg = gM( 'edittoolbar-link-int-target-status-loading' ); |
| 102 | + $j( '#edittoolbar-link-int-target-status' ) |
| 103 | + .html( '<img id="edittoolbar-link-int-target-status-exists" src="' + existsImg + '" alt="' + existsMsg + '" title="' + existsMsg + '" />' + |
| 104 | + '<img id="edittoolbar-link-int-target-status-notexists" src="' + notexistsImg + '" alt="' + notexistsMsg + '" title="' + notexistsMsg + '" />' + |
| 105 | + '<img id="edittoolbar-link-int-target-status-invalid" src="' + invalidImg + '" alt="' + invalidMsg + '" title="' + invalidMsg + '" />' + |
| 106 | + '<img id="edittoolbar-link-int-target-status-loading" src="' + loadingImg + '" alt="' + loadingMsg + '" title="' + loadingMsg + '" />' ) |
| 107 | + .data( 'cache', {} ) |
| 108 | + .children().hide(); |
| 109 | + |
| 110 | + function updateExistence( target ) { |
| 111 | + function updateWidget( status ) { |
| 112 | + $j( '#edittoolbar-link-int-target-status' ).children().hide(); |
| 113 | + $j( '#edittoolbar-link-int-target-status-' + status ).show(); |
| 114 | + } |
| 115 | + |
| 116 | + // Abort previous request |
| 117 | + var request = $j( '#edittoolbar-link-int-target-status' ).data( 'request' ); |
| 118 | + if ( request ) |
| 119 | + request.abort(); |
| 120 | + |
| 121 | + var target = $j( '#edittoolbar-link-int-target' ).val(); |
| 122 | + var cache = $j( '#edittoolbar-link-int-target-status' ).data( 'cache' ); |
| 123 | + if ( cache[target] ) { |
| 124 | + updateWidget( cache[target] ); |
| 125 | + return; |
| 126 | + } |
| 127 | + |
| 128 | + if ( target == '' ) { |
| 129 | + // Hide the widget when the textbox is empty |
| 130 | + $j( '#edittoolbar-link-int-target-status' ).children().hide(); |
| 131 | + return; |
| 132 | + } |
| 133 | + if ( target.indexOf( '|' ) != -1 ) { |
| 134 | + // Title contains | , which means it's invalid |
| 135 | + // but confuses the API. Show invalid and bypass API |
| 136 | + updateWidget( 'invalid' ); |
| 137 | + return; |
| 138 | + } |
| 139 | + |
| 140 | + updateWidget( 'loading' ); |
| 141 | + var request = $j.ajax( { |
| 142 | + url: wgScriptPath + '/api.php', |
| 143 | + dataType: 'json', |
| 144 | + data: { |
| 145 | + 'action': 'query', |
| 146 | + 'indexpageids': '', |
| 147 | + 'titles': target, |
| 148 | + 'format': 'json' |
| 149 | + }, |
| 150 | + success: function( data ) { |
| 151 | + // TODO: What happens if data.query.pageids is undefined? |
| 152 | + var page = data.query.pages[data.query.pageids[0]]; |
| 153 | + var status = 'exists'; |
| 154 | + if ( typeof page.missing != 'undefined' ) |
| 155 | + status = 'notexists'; |
| 156 | + else if ( typeof page.invalid != 'undefined' ) |
| 157 | + status = 'invalid'; |
| 158 | + |
| 159 | + cache[target] = status; |
| 160 | + updateWidget( status ); |
| 161 | + } |
| 162 | + }); |
| 163 | + // Save request object so it can be aborted if necessary |
| 164 | + $j( '#edittoolbar-link-int-target-status' ).data( 'request', request ); |
| 165 | + } |
| 166 | + |
| 167 | + $j( '#edittoolbar-link-int-target' ).bind( 'keypress paste', function() { |
| 168 | + // Cancel the running timer if applicable |
| 169 | + if ( typeof $j(this).data( 'timerID' ) != 'undefined' ) |
| 170 | + clearTimeout( $j(this).data( 'timerID' ) ); |
| 171 | + |
| 172 | + // Delay fetch for a while |
| 173 | + // FIXME: Make 250 configurable elsewhere |
| 174 | + var timerID = setTimeout( updateExistence, 250 ); |
| 175 | + $j(this).data( 'timerID', timerID ); |
| 176 | + }); |
| 177 | + $j( '#edittoolbar-link-int-target' ).change( function() { |
| 178 | + // Cancel the running timer if applicable |
| 179 | + if ( typeof $j(this).data( 'timerID' ) != 'undefined' ) |
| 180 | + clearTimeout( $j(this).data( 'timerID' ) ); |
| 181 | + |
| 182 | + // Fetch right now |
| 183 | + updateExistence(); |
| 184 | + }); |
88 | 185 | }, |
89 | 186 | dialog: { |
90 | 187 | width: 550, // FIXME: autoresize width |
— | — | @@ -105,7 +202,7 @@ |
106 | 203 | switch ( $j( '#edittoolbar-link-tabs' ).tabs( 'option', 'selected' ) ) { |
107 | 204 | case 0: // Internal link |
108 | 205 | // TODO: Escape this stuff |
109 | | - // TODO: Verify internal target validity |
| 206 | + // TODO: Refuse to insert links to invalid titles |
110 | 207 | insertText = '[[' + |
111 | 208 | $j( '#edittoolbar-link-int-target' ).val() + |
112 | 209 | '|' + |
— | — | @@ -138,9 +235,11 @@ |
139 | 236 | }, |
140 | 237 | open: function() { |
141 | 238 | // Pre-fill text fields |
| 239 | + // val() doesn't trigger the change event, so let's do that ourselves |
142 | 240 | $j( '#edittoolbar-link-int-text, #edittoolbar-link-ext-text, #edittoolbar-link-int-target' ) |
143 | | - .val( $j(this).data( 'context' ).$textarea.getSelection() ); |
144 | | - $j( '#edittoolbar-link-ext-target' ).val( 'http://' ); |
| 241 | + .val( $j(this).data( 'context' ).$textarea.getSelection() ) |
| 242 | + .change(); |
| 243 | + $j( '#edittoolbar-link-ext-target' ).val( 'http://' ).change(); |
145 | 244 | $j( '#edittoolbar-link-int-text' ).data( 'untouched', true ); |
146 | 245 | } |
147 | 246 | } |
— | — | @@ -399,13 +498,14 @@ |
400 | 499 | titleMsg: 'edittoolbar-tool-replace-title', |
401 | 500 | id: 'edittoolbar-replace-dialog', |
402 | 501 | // TODO: break this line |
403 | | - html: '<form><fieldset><label for="edittoolbar-replace-search" rel="edittoolbar-tool-replace-search"></label><input type="text" id="edittoolbar-replace-search" style="display:block;" /><label for="edittoolbar-replace-replace" rel="edittoolbar-tool-replace-replace"></label><input type="text" id="edittoolbar-replace-replace" style="display:block;" /><input type="checkbox" id="edittoolbar-replace-case" /><label for="edittoolbar-replace-case" rel="edittoolbar-tool-replace-case"></label><br /><input type="checkbox" id="edittoolbar-replace-regex" /><label for="edittoolbar-replace-regex" rel="edittoolbar-tool-replace-regex"></label><br /><input type="checkbox" id="edittoolbar-replace-all" /><label for="edittoolbar-replace-all" rel="edittoolbar-tool-replace-all"></label></fieldset></form>', |
| 502 | + html: '<form><fieldset><label for="edittoolbar-replace-search" rel="edittoolbar-tool-replace-search"></label> <input type="text" id="edittoolbar-replace-search" /><br /><label for="edittoolbar-replace-replace" rel="edittoolbar-tool-replace-replace"></label> <input type="text" id="edittoolbar-replace-replace" /><br /><input type="checkbox" id="edittoolbar-replace-case" /><label for="edittoolbar-replace-case" rel="edittoolbar-tool-replace-case"></label><br /><input type="checkbox" id="edittoolbar-replace-regex" /><label for="edittoolbar-replace-regex" rel="edittoolbar-tool-replace-regex"></label><br /><input type="checkbox" id="edittoolbar-replace-all" /><label for="edittoolbar-replace-all" rel="edittoolbar-tool-replace-all"></label></fieldset></form>', |
404 | 503 | init: function() { |
405 | 504 | $j(this).find( '[rel]' ).each( function() { |
406 | 505 | $j(this).html( gM( $j(this).attr( 'rel' ) ) ); |
407 | 506 | }); |
408 | 507 | }, |
409 | 508 | dialog: { |
| 509 | + width: 350, // FIXME: autoresize width |
410 | 510 | buttons: { |
411 | 511 | 'edittoolbar-tool-replace-button': function() { |
412 | 512 | var searchStr = $j( '#edittoolbar-replace-search' ).val(); |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.i18n.php |
— | — | @@ -31,6 +31,10 @@ |
32 | 32 | 'edittoolbar-tool-link-ext-text' => 'Link text:', |
33 | 33 | 'edittoolbar-tool-link-insert' => 'Insert link', |
34 | 34 | 'edittoolbar-tool-link-cancel' => 'Cancel', |
| 35 | + 'edittoolbar-link-int-target-status-exists' => 'Page exists', |
| 36 | + 'edittoolbar-link-int-target-status-notexists' => 'Page does not exist', |
| 37 | + 'edittoolbar-link-int-target-status-invalid' => 'Invalid title', |
| 38 | + 'edittoolbar-link-int-target-status-loading' => 'Checking page existence...', |
35 | 39 | 'edittoolbar-tool-file' => 'Embedded file', |
36 | 40 | 'edittoolbar-tool-file-pre' => '$1{{ns:file}}:', |
37 | 41 | 'edittoolbar-tool-file-example' => 'Example.jpg', |
— | — | @@ -188,6 +192,10 @@ |
189 | 193 | 'edittoolbar-tool-bold-example' => '{{Identical|Bold text}}', |
190 | 194 | 'edittoolbar-tool-italic' => '{{Identical|Italic}}', |
191 | 195 | 'edittoolbar-tool-italic-example' => '{{Identical|Italic text}}', |
| 196 | + 'edittoolbar-link-int-target-status-exists' => 'alt text and title text for the image shown when the title the user entered exists', |
| 197 | + 'edittoolbar-link-int-target-status-notexists' => 'alt text and title text for the image shown when the title the user entered does not exist', |
| 198 | + 'edittoolbar-link-int-target-status-invalid' => 'alt text and title text for the image shown when the title the user entered is invalid', |
| 199 | + 'edittoolbar-link-int-target-status-loading' => 'alt text and title text for the image shown while the title the user entered is being checked for existence', |
192 | 200 | 'edittoolbar-tool-reference' => '{{Identical|Reference}}', |
193 | 201 | 'edittoolbar-group-list' => '{{Identical|List}}', |
194 | 202 | 'edittoolbar-group-size' => '{{Identical|Size}}', |
Index: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.php |
— | — | @@ -19,7 +19,7 @@ |
20 | 20 | /* Configuration */ |
21 | 21 | |
22 | 22 | // Bump the version number every time you change any of the .css/.js files |
23 | | -$wgEditToolbarStyleVersion = 21; |
| 23 | +$wgEditToolbarStyleVersion = 22; |
24 | 24 | |
25 | 25 | // Set this to true to simply override the stock toolbar for everyone |
26 | 26 | $wgEditToolbarGlobalEnable = false; |