Index: trunk/extensions/UploadWizard/UploadWizard.i18n.php |
— | — | @@ -12,6 +12,15 @@ |
13 | 13 | * @author Neil Kandalgaonkar |
14 | 14 | */ |
15 | 15 | $messages['en'] = array( |
| 16 | + 'linktest' => '[$1 this is a link]', |
| 17 | + 'pluraltest' => 'This is {{PLURAL:$1|one|many}} things', |
| 18 | + 'magictest' => 'From {{SITENAME}}', |
| 19 | + 'namespacedtest' => '{{ns:project}}:Copyrights', |
| 20 | + 'extremelycomplextest' => 'There is currently no text in this page. |
| 21 | +You can [[Special:Search/{{PAGENAME}}|search for this page title]] in other pages, |
| 22 | +<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs], |
| 23 | +or [{{fullurl:{{FULLPAGENAME}}|action=edit}} edit this page]</span>.', |
| 24 | + 'internallinktest' => '[[Special:SpecialPages|{{int:specialpages}}]]', |
16 | 25 | 'uploadwizard' => 'Upload wizard', |
17 | 26 | 'uploadwizard-desc' => 'Upload wizard, developed for the Multimedia Usability grant', |
18 | 27 | 'mwe-loading-upwiz' => 'Loading upload wizard', |
— | — | @@ -3493,4 +3502,3 @@ |
3494 | 3503 | 'mwe-upwiz-categories-add' => '新增', |
3495 | 3504 | 'mwe-upwiz-category-remove' => '刪除這個類別', |
3496 | 3505 | ); |
3497 | | - |
Index: trunk/extensions/UploadWizard/resources/mw.UtilitiesTime.js |
— | — | @@ -0,0 +1,90 @@ |
| 2 | +/** |
| 3 | + * Given a float number of seconds, returns npt format response. ( ignore |
| 4 | + * days for now ) |
| 5 | + * |
| 6 | + * @param {Float} |
| 7 | + * sec Seconds |
| 8 | + * @param {Boolean} |
| 9 | + * show_ms If milliseconds should be displayed. |
| 10 | + * @return {Float} String npt format |
| 11 | + */ |
| 12 | +mw.seconds2npt = function( sec, show_ms ) { |
| 13 | + if ( isNaN( sec ) ) { |
| 14 | + mw.log("Warning: trying to get npt time on NaN:" + sec); |
| 15 | + return '0:00:00'; |
| 16 | + } |
| 17 | + |
| 18 | + var tm = mw.seconds2Measurements( sec ) |
| 19 | + |
| 20 | + // Round the number of seconds to the required number of significant |
| 21 | + // digits |
| 22 | + if ( show_ms ) { |
| 23 | + tm.seconds = Math.round( tm.seconds * 1000 ) / 1000; |
| 24 | + } else { |
| 25 | + tm.seconds = Math.round( tm.seconds ); |
| 26 | + } |
| 27 | + if ( tm.seconds < 10 ){ |
| 28 | + tm.seconds = '0' + tm.seconds; |
| 29 | + } |
| 30 | + if( tm.hours == 0 ){ |
| 31 | + hoursStr = '' |
| 32 | + } else { |
| 33 | + if ( tm.minutes < 10 ) |
| 34 | + tm.minutes = '0' + tm.minutes; |
| 35 | + |
| 36 | + hoursStr = tm.hours + ":"; |
| 37 | + } |
| 38 | + return hoursStr + tm.minutes + ":" + tm.seconds; |
| 39 | +} |
| 40 | + |
| 41 | +/** |
| 42 | + * Given seconds return array with 'days', 'hours', 'min', 'seconds' |
| 43 | + * |
| 44 | + * @param {float} |
| 45 | + * sec Seconds to be converted into time measurements |
| 46 | + */ |
| 47 | +mw.seconds2Measurements = function ( sec ){ |
| 48 | + var tm = {}; |
| 49 | + tm.days = Math.floor( sec / ( 3600 * 24 ) ) |
| 50 | + tm.hours = Math.floor( sec / 3600 ); |
| 51 | + tm.minutes = Math.floor( ( sec / 60 ) % 60 ); |
| 52 | + tm.seconds = sec % 60; |
| 53 | + return tm; |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * Take hh:mm:ss,ms or hh:mm:ss.ms input, return the number of seconds |
| 58 | + * |
| 59 | + * @param {String} |
| 60 | + * npt_str NPT time string |
| 61 | + * @return {Float} Number of seconds |
| 62 | + */ |
| 63 | +mw.npt2seconds = function ( npt_str ) { |
| 64 | + if ( !npt_str ) { |
| 65 | + // mw.log('npt2seconds:not valid ntp:'+ntp); |
| 66 | + return false; |
| 67 | + } |
| 68 | + // Strip {npt:}01:02:20 or 32{s} from time if present |
| 69 | + npt_str = npt_str.replace( /npt:|s/g, '' ); |
| 70 | + |
| 71 | + var hour = 0; |
| 72 | + var min = 0; |
| 73 | + var sec = 0; |
| 74 | + |
| 75 | + times = npt_str.split( ':' ); |
| 76 | + if ( times.length == 3 ) { |
| 77 | + sec = times[2]; |
| 78 | + min = times[1]; |
| 79 | + hour = times[0]; |
| 80 | + } else if ( times.length == 2 ) { |
| 81 | + sec = times[1]; |
| 82 | + min = times[0]; |
| 83 | + } else { |
| 84 | + sec = times[0]; |
| 85 | + } |
| 86 | + // Sometimes a comma is used instead of period for ms |
| 87 | + sec = sec.replace( /,\s?/, '.' ); |
| 88 | + // Return seconds float |
| 89 | + return parseInt( hour * 3600 ) + parseInt( min * 60 ) + parseFloat( sec ); |
| 90 | +} |
| 91 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.UtilitiesTime.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 92 | + native |
Index: trunk/extensions/UploadWizard/resources/mw.MockUploadHandler.js |
— | — | @@ -0,0 +1,37 @@ |
| 2 | +// TODO copy interface from ApiUploadHandler -- it changed |
| 3 | + |
| 4 | +// Currently this doesn't at all follow the interface that Mdale made in UploadHandler |
| 5 | +// will have to figure this out. |
| 6 | + |
| 7 | +// this should be loaded with test suites when appropriate. separate file. |
| 8 | +mw.MockUploadHandler = function(upload) { |
| 9 | + this.upload = upload; |
| 10 | + this.nextState = null; |
| 11 | + this.progress = 0.0; |
| 12 | + |
| 13 | +}; |
| 14 | + |
| 15 | +mw.MockUploadHandler.prototype = { |
| 16 | + |
| 17 | + start: function () { |
| 18 | + var _this = this; |
| 19 | + _this.beginTime = (new Date()).getTime(); |
| 20 | + _this.nextState = _this.cont; |
| 21 | + _this.nextState(); |
| 22 | + }, |
| 23 | + |
| 24 | + cont: function () { |
| 25 | + var _this = this; |
| 26 | + var delta = 0.0001; // static? |
| 27 | + _this.progress += ( Math.random() * 0.1 ); |
| 28 | + _this.upload.setTransportProgress(_this.progress); |
| 29 | + if (1.0 - _this.progress < delta) { |
| 30 | + _this.upload.setTransported(); |
| 31 | + } else { |
| 32 | + setTimeout( function() { _this.nextState(); }, 200 ); |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | +}; |
| 37 | + |
| 38 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.MockUploadHandler.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 39 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery.tipsy.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/resources/jquery.tipsy.gif |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 40 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/resources/spinner-orange.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/resources/spinner-orange.gif |
___________________________________________________________________ |
Added: svn:mime-type |
3 | 41 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/resources/uploadWizard.css |
— | — | @@ -0,0 +1,558 @@ |
| 2 | + |
| 3 | + |
| 4 | +/* min-width is about 550px, maybe about 52em |
| 5 | +/* max-width is about 875px about 87em? |
| 6 | +#upload-wizard { |
| 7 | + |
| 8 | +} |
| 9 | +*/ |
| 10 | + |
| 11 | + |
| 12 | +form.mwe-upwiz-form { |
| 13 | + display: inline; |
| 14 | +} |
| 15 | + |
| 16 | +#upload-wizard { |
| 17 | + margin-top: 18px; |
| 18 | + min-width: 32em; |
| 19 | + max-width: 64em; |
| 20 | +} |
| 21 | + |
| 22 | +/* |
| 23 | +.upload-section { |
| 24 | + padding: 1em; |
| 25 | + margin-bottom: 0.5em; |
| 26 | + margin-top: 0.5em; |
| 27 | + border: 1px solid #e0e0e0; |
| 28 | +} |
| 29 | +*/ |
| 30 | + |
| 31 | + |
| 32 | +.mwe-upwiz-clearing { |
| 33 | + clear: left; |
| 34 | + width: 100%; |
| 35 | +} |
| 36 | + |
| 37 | + |
| 38 | +#mwe-upwiz-content { |
| 39 | + padding: 1em; |
| 40 | +} |
| 41 | + |
| 42 | +.mwe-upwiz-add-files-0, #mwe-upwiz-files { |
| 43 | + margin-top: 3em; |
| 44 | + margin-bottom: 3em; |
| 45 | +} |
| 46 | + |
| 47 | +.mwe-upwiz-add-files-0 { |
| 48 | + text-align: center; |
| 49 | + font-size: large; |
| 50 | +} |
| 51 | + |
| 52 | +#mwe-upwiz-add-file { |
| 53 | +} |
| 54 | + |
| 55 | +/* NOT a pseudoclass */ |
| 56 | +#mwe-upwiz-add-file.hover { |
| 57 | + text-decoration: underline; |
| 58 | +} |
| 59 | + |
| 60 | +/* perhaps a general class for links that are actually "buttons" */ |
| 61 | +#mwe-upwiz-add-file, .mwe-upwiz-remove-ctrl, .mwe-upwiz-more-options { |
| 62 | + outline: none; |
| 63 | + cursor: pointer; |
| 64 | +} |
| 65 | + |
| 66 | +/* CSS styling hack for file inputs - http://www.quirksmode.org/dom/inputfile.html */ |
| 67 | +.mwe-upwiz-file-ctrl-container { |
| 68 | + position: absolute; |
| 69 | + overflow: hidden; |
| 70 | +} |
| 71 | + |
| 72 | +.mwe-upwiz-file-input, .mwe-upwiz-visible-file { |
| 73 | + cursor: pointer; |
| 74 | +} |
| 75 | + |
| 76 | +/* file inputs are freakishly large to overflow the containing div -- we get a div |
| 77 | + that can act as a more styleable single-click file input */ |
| 78 | +.mwe-upwiz-file-input, .disabler { |
| 79 | + font-size: 100px; |
| 80 | + -moz-opacity: 0.3; |
| 81 | + filter:alpha(opacity: 0); |
| 82 | + opacity: 0; |
| 83 | + z-index: 2; |
| 84 | +} |
| 85 | + |
| 86 | +.mwe-upwiz-file.filled { |
| 87 | + position: relative; |
| 88 | +} |
| 89 | + |
| 90 | +.mwe-upwiz-file.odd .mwe-upwiz-visible-file { |
| 91 | + background: #f5f5f5; |
| 92 | +} |
| 93 | + |
| 94 | + |
| 95 | + |
| 96 | +.mwe-upwiz-remove-ctrl { |
| 97 | +} |
| 98 | + |
| 99 | +/* XXX this is highly specific to our installation |
| 100 | +.mwe-upwiz-remove-ctrl.hover { |
| 101 | +.ui-icon { background-image: url(/w/extensions/UsabilityInitiative/css/vector/images/ui-icons_cd0a0a_256x240.png) } |
| 102 | +} |
| 103 | +*/ |
| 104 | + |
| 105 | +.mwe-upwiz-visible-file .mwe-upwiz-remove-ctrl { |
| 106 | + float: right; |
| 107 | + visibility: hidden; |
| 108 | + margin: 0.25em; |
| 109 | + padding: 0.25em; |
| 110 | +} |
| 111 | + |
| 112 | +.mwe-upwiz-file-indicator, .mwe-upwiz-count { |
| 113 | + float: right; |
| 114 | + margin-left: 0.5em; |
| 115 | + padding: 0.5em 0.5em 0.5em 20px; /* 20px for the icon */ |
| 116 | +} |
| 117 | + |
| 118 | +.mwe-upwiz-visible-file .mwe-upwiz-file-indicator { |
| 119 | + visibility: hidden; |
| 120 | +} |
| 121 | + |
| 122 | +.mwe-upwiz-visible-file { |
| 123 | + display: none; |
| 124 | +} |
| 125 | + |
| 126 | +.mwe-upwiz-file.filled .mwe-upwiz-visible-file { |
| 127 | + display: block; |
| 128 | +} |
| 129 | + |
| 130 | +.mwe-upwiz-visible-file-filename { |
| 131 | + padding: 0.5em; |
| 132 | + margin-right: 40px; |
| 133 | + overflow: hidden; |
| 134 | +} |
| 135 | + |
| 136 | +.mwe-upwiz-progress-bar-etr-container { |
| 137 | + /* needed ? */ |
| 138 | +} |
| 139 | + |
| 140 | + |
| 141 | +.mwe-upwiz-add-files-n { |
| 142 | + float: left; |
| 143 | + margin-top: 5px; |
| 144 | + margin-left: 4px; |
| 145 | +} |
| 146 | + |
| 147 | +#mwe-upwiz-add-file-container.mwe-upwiz-add-files-n, .mwe-upwiz-progress-bar-etr { |
| 148 | + width: 300px; |
| 149 | + padding-left: 5px; |
| 150 | +} |
| 151 | + |
| 152 | + |
| 153 | +#mwe-upwiz-add-file-container.mwe-upwiz-add-files-n { |
| 154 | + float: left; |
| 155 | +} |
| 156 | + |
| 157 | +.mwe-upwiz-visible-file { |
| 158 | + width: 100%; /* of containing mwe-upwiz-file */ |
| 159 | + white-space: nowrap; |
| 160 | + overflow: hidden; |
| 161 | +} |
| 162 | + |
| 163 | +.mwe-upwiz-file.hover .mwe-upwiz-visible-file { |
| 164 | + background: #e0f0ff !important; |
| 165 | +} |
| 166 | + |
| 167 | +.mwe-upwiz-file.hover .mwe-upwiz-remove-ctrl { |
| 168 | + visibility: visible; |
| 169 | +} |
| 170 | + |
| 171 | +/* XXX we probably have a standard for this */ |
| 172 | +.helper { |
| 173 | + color: #cccccc; /* or whatever we do for greyed out text */ |
| 174 | + font-variant: italic, oblique; |
| 175 | + text-align: center; |
| 176 | +} |
| 177 | + |
| 178 | + |
| 179 | +#mwe-upwiz-files { |
| 180 | + margin-right: 8em; |
| 181 | + margin-left: 8em; |
| 182 | +} |
| 183 | + |
| 184 | +.mwe-upwiz-file { |
| 185 | +} |
| 186 | + |
| 187 | +#mwe-upwiz-upload-ctrls { |
| 188 | + margin-top: 1em; |
| 189 | +} |
| 190 | + |
| 191 | +#mwe-upwiz-add-file-container { |
| 192 | + /* empty; this changes a lot */ |
| 193 | +} |
| 194 | + |
| 195 | +.mwe-upwiz-details-descriptions .mwe-upwiz-remove-ctrl { |
| 196 | + vertical-align: top; |
| 197 | + display: inline-block; |
| 198 | +} |
| 199 | + |
| 200 | +a[disabled=true] { |
| 201 | + color: #999999; |
| 202 | + text-decoration: none; |
| 203 | + cursor: default; |
| 204 | +} |
| 205 | + |
| 206 | +a[disabled=true]:hover { |
| 207 | + text-decoration: none; |
| 208 | +} |
| 209 | + |
| 210 | +.mwe-upwiz-status-progress { |
| 211 | + background: url(spinner-orange.gif) no-repeat left center; |
| 212 | + font-weight: bold; |
| 213 | + color: #ff9900; |
| 214 | +} |
| 215 | + |
| 216 | +.mwe-upwiz-status-completed { |
| 217 | + background: url(checkmark.gif) no-repeat left center; |
| 218 | + font-weight: bold; |
| 219 | + color: #009900; |
| 220 | +} |
| 221 | + |
| 222 | +.mwe-upwiz-progress { |
| 223 | + margin-top: 15px; |
| 224 | +} |
| 225 | + |
| 226 | +.mwe-upwiz-progress-bar-etr { |
| 227 | + float: left; |
| 228 | +} |
| 229 | + |
| 230 | +.mwe-upwiz-etr { |
| 231 | + text-align: center; |
| 232 | +} |
| 233 | + |
| 234 | + |
| 235 | +.mwe-upwiz-upload-warning { |
| 236 | + background: #ffffe0; |
| 237 | +} |
| 238 | + |
| 239 | +.mwe-upwiz-details-error { |
| 240 | + display: none; |
| 241 | + background: #ffffe0; |
| 242 | +} |
| 243 | + |
| 244 | +.mwe-upwiz-thumbnail, .mwe-upwiz-thumbnail-small { |
| 245 | + border: 1px solid #cccccc; |
| 246 | + text-align: center; |
| 247 | + background: #ffffff; |
| 248 | +} |
| 249 | + |
| 250 | +.mwe-upwiz-thumbnail { |
| 251 | + padding: 0.5em; |
| 252 | + width: 120px; |
| 253 | +} |
| 254 | + |
| 255 | +.mwe-upwiz-thumbnail-side { |
| 256 | + float: left; |
| 257 | + margin-bottom: 1em; |
| 258 | + margin-right: 1em; |
| 259 | +} |
| 260 | + |
| 261 | +.mwe-upwiz-thumbnail-small { |
| 262 | + padding: 0.25em; |
| 263 | + width: 60px; |
| 264 | +} |
| 265 | + |
| 266 | +#mwe-upwiz-deeds-thumbnails { |
| 267 | + text-align: center; |
| 268 | + margin: 1em 0; |
| 269 | + background: #f0f0f0; |
| 270 | +} |
| 271 | + |
| 272 | +#mwe-upwiz-deeds-thumbnails .mwe-upwiz-thumbnail-small { |
| 273 | + display: inline-block; |
| 274 | + margin: 1em; |
| 275 | + vertical-align: middle; |
| 276 | +} |
| 277 | + |
| 278 | +/* I don't like that this has to have width, to ensure that all the floats work out correctly.*/ |
| 279 | +.mwe-upwiz-data { |
| 280 | + float: left; |
| 281 | + width: 46em; |
| 282 | +} |
| 283 | + |
| 284 | + |
| 285 | +/* XXX add spinner gif instead, maybe */ |
| 286 | +.busy { |
| 287 | + /* background: yellow; */ |
| 288 | +} |
| 289 | + |
| 290 | +.mwe-upwiz-stepdiv { |
| 291 | + height: 0px; |
| 292 | + overflow: hidden; |
| 293 | +} |
| 294 | + |
| 295 | + |
| 296 | + |
| 297 | +.shim { |
| 298 | + float:right; |
| 299 | + width: 1px; |
| 300 | +} |
| 301 | + |
| 302 | +.clearShim { |
| 303 | + clear: both; |
| 304 | + height: 1px; |
| 305 | + overflow: hidden; |
| 306 | +} |
| 307 | + |
| 308 | +.mwe-checkbox-hang-indent { |
| 309 | + float: left; |
| 310 | + width: 24px; |
| 311 | + margin-top: 6px; |
| 312 | +} |
| 313 | + |
| 314 | +.mwe-checkbox-hang-indent-text { |
| 315 | + margin-left: 24px; |
| 316 | +} |
| 317 | + |
| 318 | +.mwe-small-print { |
| 319 | + font-size: x-small; |
| 320 | +} |
| 321 | + |
| 322 | +.mwe-upwiz-deed { |
| 323 | + margin-left: 24px; |
| 324 | +} |
| 325 | + |
| 326 | +.mwe-upwiz-deed.selected .mwe-upwiz-deed-name { |
| 327 | + font-weight: bold; |
| 328 | +} |
| 329 | + |
| 330 | +.mwe-more-options, .mwe-upwiz-macro-deeds-return, .mwe-upwiz-deed-header-link { |
| 331 | + cursor: pointer; |
| 332 | +} |
| 333 | + |
| 334 | +.mwe-more-options { |
| 335 | + padding-bottom: 4px; |
| 336 | +} |
| 337 | + |
| 338 | +.mwe-upwiz-deed-license { |
| 339 | + margin-left: 24px; |
| 340 | +} |
| 341 | + |
| 342 | +#mwe-upwiz-macro-deeds { |
| 343 | + margin-top: 12px; |
| 344 | + margin-bottom: 24px; |
| 345 | +} |
| 346 | + |
| 347 | +#mwe-upwiz-macro-files { |
| 348 | + margin-top: 12px; |
| 349 | +} |
| 350 | + |
| 351 | +.mwe-upwiz-info-file { |
| 352 | + margin-bottom: 1em; |
| 353 | +} |
| 354 | + |
| 355 | +.mwe-upwiz-details-input { |
| 356 | + width: 33em; |
| 357 | + float: left; |
| 358 | +} |
| 359 | + |
| 360 | +.mwe-upwiz-details-fieldname { |
| 361 | + width: 10em; |
| 362 | + padding-top: 0.5em; |
| 363 | + float: left; |
| 364 | +} |
| 365 | + |
| 366 | +.mwe-upwiz-details-input-error { |
| 367 | + padding-left: 10em; |
| 368 | + /* XXX the following rules pop it open at a fixed width so layout doesn't change :( */ |
| 369 | + /* min-height: 3em; |
| 370 | + position: relative; */ |
| 371 | +} |
| 372 | + |
| 373 | +/* XXX the following rule keeps errors on the details page at the bottom of a fixed-height space, see above */ |
| 374 | +/* |
| 375 | +.mwe-upwiz-details-input-error label.mwe-validator-error, |
| 376 | +.mwe-upwiz-details-input-error label.mwe-error { |
| 377 | + position: absolute; |
| 378 | + bottom: 0px; |
| 379 | +} |
| 380 | +*/ |
| 381 | + |
| 382 | + |
| 383 | +.mwe-upwiz-desc-lang-select { |
| 384 | + width: 11em; |
| 385 | + font-family: sans-serif; |
| 386 | + font-size: small; |
| 387 | +} |
| 388 | + |
| 389 | +.mwe-upwiz-desc-lang-text { |
| 390 | + width: 20em; |
| 391 | + overflow: hidden; |
| 392 | + font-family: sans-serif; /* XXX is this right? */ |
| 393 | + font-size: small; |
| 394 | +} |
| 395 | + |
| 396 | +.mwe-upwiz-details-descriptions-add { |
| 397 | + margin-left: 10em; /* width of mwe-upwiz-details-fieldname */ |
| 398 | +} |
| 399 | + |
| 400 | +.mwe-upwiz-details-descriptions-add { |
| 401 | + margin-bottom: 1em; |
| 402 | +} |
| 403 | + |
| 404 | +.mwe-grow-textarea, .mwe-long-textarea { |
| 405 | + overflow: hidden; |
| 406 | + font-family: sans-serif; /* XXX is this right? */ |
| 407 | + font-size: small; |
| 408 | +} |
| 409 | + |
| 410 | +.mwe-long-textarea { |
| 411 | + width: 31em; |
| 412 | +} |
| 413 | + |
| 414 | +fieldset .mwe-long-textarea { |
| 415 | + width: 17em; |
| 416 | +} |
| 417 | + |
| 418 | + |
| 419 | +.mwe-upwiz-details-fieldname-input { |
| 420 | + margin-bottom: 1em; |
| 421 | +} |
| 422 | + |
| 423 | +.mwe-upwiz-details-filename. { |
| 424 | + overflow: hidden; |
| 425 | + width: 350px; |
| 426 | +} |
| 427 | + |
| 428 | +.mwe-upwiz-other-textarea { |
| 429 | + /* width: 40.3em; */ |
| 430 | +} |
| 431 | + |
| 432 | +fieldset.mwe-fieldset { |
| 433 | + width: 450px; |
| 434 | + border: 1px solid #cccccc; |
| 435 | + padding-left: 12px; |
| 436 | + padding-right: 12px; |
| 437 | +} |
| 438 | + |
| 439 | +legend.mwe-legend { |
| 440 | + padding: 0.5em 0.5em 0.5em 0.33em; |
| 441 | + color: #666666; |
| 442 | +} |
| 443 | + |
| 444 | +.masked-hidden { |
| 445 | + visibility: hidden !important; |
| 446 | +} |
| 447 | + |
| 448 | +.mwe-upwiz-thirdparty-fields { |
| 449 | + margin-bottom: .5em; |
| 450 | +} |
| 451 | + |
| 452 | +.mwe-upwiz-thirdparty-fields label { |
| 453 | + width: 9em; |
| 454 | + display: inline-block; |
| 455 | + padding-bottom: .5em; |
| 456 | +} |
| 457 | + |
| 458 | +.mwe-upwiz-thirdparty-fields textarea { |
| 459 | + margin: 0 |
| 460 | +} |
| 461 | + |
| 462 | +.mwe-upwiz-thirdparty-license { |
| 463 | + margin-top: 8px; |
| 464 | +} |
| 465 | + |
| 466 | +.mwe-upwiz-deed-form-internal { |
| 467 | + padding: 0.5em 0 1.5em 3em; |
| 468 | +} |
| 469 | + |
| 470 | + |
| 471 | +.mwe-upwiz-copyright-info label { |
| 472 | + display: inline-block; |
| 473 | + padding-left: 15px; |
| 474 | + text-indent: -15px; |
| 475 | + margin-right: 15px; |
| 476 | +} |
| 477 | + |
| 478 | +/* the hanging indent checkboxes */ |
| 479 | +.mwe-upwiz-copyright-info input.mwe-accept-deed { |
| 480 | + width: 13px; |
| 481 | + height: 13px; |
| 482 | + padding: 0; |
| 483 | + margin: 0px 3px 0px 0px; |
| 484 | + vertical-align: bottom; |
| 485 | + position: relative; |
| 486 | + top: -1px; |
| 487 | +} |
| 488 | + |
| 489 | + |
| 490 | +.mwe-upwiz-custom-deed { |
| 491 | + margin-top: 5px; |
| 492 | +} |
| 493 | + |
| 494 | +.mwe-upwiz-buttons { |
| 495 | + margin-top: 1em; |
| 496 | + padding-top: 10px; |
| 497 | + border-top: 1px solid #e0e0e0; |
| 498 | + text-align: right; /* works for now, only one 'next' button */ |
| 499 | +} |
| 500 | + |
| 501 | +a.mwe-upwiz-tooltip-link { |
| 502 | + cursor: pointer; |
| 503 | +} |
| 504 | + |
| 505 | +.mwe-error, .mwe-validator-error { |
| 506 | + color: #ff0000; |
| 507 | +} |
| 508 | + |
| 509 | +.mwe-upwiz-deed-thirdparty .mwe-upwiz-deed-form-internal label.mwe-error, |
| 510 | +.mwe-upwiz-deed-thirdparty .mwe-upwiz-deed-form-internal label.mwe-validator-error { |
| 511 | + margin-left: 9em; |
| 512 | +} |
| 513 | + |
| 514 | +/* XXX fix this later -- we get non-rounded corners somehow */ |
| 515 | +input[type='text'].mwe-error, textarea.mwe-error, |
| 516 | +input[type='text'].mwe-validator-error, textarea.mwe-validator-error { |
| 517 | + color: black; |
| 518 | + border-color: #ff0000; |
| 519 | +} |
| 520 | + |
| 521 | +.mwe-upwiz-toggler { |
| 522 | + margin-bottom: 0; |
| 523 | + padding: 4px 0 3px 18px; |
| 524 | + background: url('toggle.png') no-repeat left center; |
| 525 | +} |
| 526 | + |
| 527 | +.mwe-upwiz-toggler-open { |
| 528 | + background: url('toggle-open.png') no-repeat left center; |
| 529 | +} |
| 530 | + |
| 531 | +.mwe-upwiz-toggled { |
| 532 | + margin-top: 1em; |
| 533 | +} |
| 534 | + |
| 535 | +.mwe-upwiz-thanks { |
| 536 | + margin-bottom: 2em; |
| 537 | +} |
| 538 | + |
| 539 | +/* may change in RTL */ |
| 540 | +.mwe-upwiz-required-field { |
| 541 | + /* font-weight: bold; */ |
| 542 | +} |
| 543 | + |
| 544 | +.mwe-upwiz-required-marker { |
| 545 | + color: #0099cc; |
| 546 | +} |
| 547 | + |
| 548 | +.mwe-readonly { |
| 549 | + background-color: #ffffff; |
| 550 | +} |
| 551 | + |
| 552 | +.mwe-date-display { |
| 553 | + width: 100%; |
| 554 | + background: #ffffff url('calendar.gif') no-repeat right center; |
| 555 | +} |
| 556 | + |
| 557 | +.ui-datepicker-current-day a.ui-state-active { |
| 558 | + background: #ffff99; |
| 559 | +} |
Property changes on: trunk/extensions/UploadWizard/resources/uploadWizard.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 560 | + native |
Index: trunk/extensions/UploadWizard/resources/checkmark.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/resources/checkmark.gif |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 561 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/resources/mw.units.js |
— | — | @@ -0,0 +1,35 @@ |
| 2 | +/** |
| 3 | + * Format a size in bytes for output, using an appropriate |
| 4 | + * unit (B, KB, MB or GB) according to the magnitude in question |
| 5 | + * |
| 6 | + * @param size Size to format |
| 7 | + * @return string Plain text (not HTML) |
| 8 | + */ |
| 9 | +mw.units.bytes = function ( size ) { |
| 10 | + // For small sizes no decimal places are necessary |
| 11 | + var round = 0; |
| 12 | + var msg = ''; |
| 13 | + if ( size > 1024 ) { |
| 14 | + size = size / 1024; |
| 15 | + if ( size > 1024 ) { |
| 16 | + size = size / 1024; |
| 17 | + // For MB and bigger two decimal places are smarter |
| 18 | + round = 2; |
| 19 | + if ( size > 1024 ) { |
| 20 | + size = size / 1024; |
| 21 | + msg = 'mwe-size-gigabytes'; |
| 22 | + } else { |
| 23 | + msg = 'mwe-size-megabytes'; |
| 24 | + } |
| 25 | + } else { |
| 26 | + msg = 'mwe-size-kilobytes'; |
| 27 | + } |
| 28 | + } else { |
| 29 | + msg = 'mwe-size-bytes'; |
| 30 | + } |
| 31 | + // JavaScript does not let you choose the precision when rounding |
| 32 | + var p = Math.pow( 10, round ); |
| 33 | + size = Math.round( size * p ) / p; |
| 34 | + return gM( msg , size ); |
| 35 | +}; |
| 36 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.units.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 37 | + native |
Index: trunk/extensions/UploadWizard/resources/inactive-arrow-divider.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Property changes on: trunk/extensions/UploadWizard/resources/inactive-arrow-divider.png |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 38 | + image/png |
Index: trunk/extensions/UploadWizard/resources/mw.UploadWizard.js |
— | — | @@ -0,0 +1,3439 @@ |
| 2 | +/** |
| 3 | + * Sort of an abstract class for deeds |
| 4 | + */ |
| 5 | +mw.UploadWizardDeed = function() { |
| 6 | + var _this = this; |
| 7 | + // prevent from instantiating directly? |
| 8 | + return false; |
| 9 | +}; |
| 10 | + |
| 11 | +mw.UploadWizardDeed.prototype = { |
| 12 | + valid: function() { |
| 13 | + return false; |
| 14 | + }, |
| 15 | + |
| 16 | + setFormFields: function() { }, |
| 17 | + |
| 18 | + getSourceWikiText: function() { |
| 19 | + return $j( this.sourceInput ).val(); |
| 20 | + }, |
| 21 | + |
| 22 | + getAuthorWikiText: function() { |
| 23 | + return $j( this.authorInput ).val(); |
| 24 | + }, |
| 25 | + |
| 26 | + /** |
| 27 | + * Get wikitext representing the licenses selected in the license object |
| 28 | + * @return wikitext of all applicable license templates. |
| 29 | + */ |
| 30 | + getLicenseWikiText: function() { |
| 31 | + var _this = this; |
| 32 | + var wikiText = ''; |
| 33 | + $j.each ( _this.licenseInput.getTemplates(), function( i, template ) { |
| 34 | + wikiText += "{{" + template + "}}\n"; |
| 35 | + } ); |
| 36 | + |
| 37 | + return wikiText; |
| 38 | + } |
| 39 | + |
| 40 | +}; |
| 41 | + |
| 42 | + |
| 43 | +/** |
| 44 | + * this is a progress bar for monitoring multiple objects, giving summary view |
| 45 | + */ |
| 46 | +mw.GroupProgressBar = function( selector, text, uploads, endState, progressProperty, weightProperty ) { |
| 47 | + var _this = this; |
| 48 | + |
| 49 | + // XXX need to figure out a way to put text inside bar |
| 50 | + _this.$selector = $j( selector ); |
| 51 | + _this.$selector.html( |
| 52 | + '<div class="mwe-upwiz-progress">' |
| 53 | + + '<div class="mwe-upwiz-progress-bar-etr-container">' |
| 54 | + + '<div class="mwe-upwiz-progress-bar-etr" style="display: none">' |
| 55 | + + '<div class="mwe-upwiz-progress-bar"></div>' |
| 56 | + + '<div class="mwe-upwiz-etr"></div>' |
| 57 | + + '</div>' |
| 58 | + + '</div>' |
| 59 | + + '<div class="mwe-upwiz-count"></div>' |
| 60 | + + '</div>' |
| 61 | + ); |
| 62 | + |
| 63 | + _this.$selector.find( '.mwe-upwiz-progress-bar' ).progressbar( { value : 0 } ); |
| 64 | + |
| 65 | + _this.uploads = uploads; |
| 66 | + _this.endState = endState; |
| 67 | + _this.progressProperty = progressProperty; |
| 68 | + _this.weightProperty = weightProperty; |
| 69 | + _this.beginTime = undefined; |
| 70 | + |
| 71 | +}; |
| 72 | + |
| 73 | +mw.GroupProgressBar.prototype = { |
| 74 | + |
| 75 | + /** |
| 76 | + * Show the progress bar with a slideout motion |
| 77 | + */ |
| 78 | + showBar: function() { |
| 79 | + this.$selector.find( '.mwe-upwiz-progress-bar-etr' ).fadeIn( 200 ); |
| 80 | + }, |
| 81 | + |
| 82 | + /** |
| 83 | + * loop around the uploads, summing certain properties for a weighted total fraction |
| 84 | + */ |
| 85 | + start: function() { |
| 86 | + var _this = this; |
| 87 | + |
| 88 | + var totalWeight = 0.0; |
| 89 | + $j.each( _this.uploads, function( i, upload ) { |
| 90 | + totalWeight += upload[_this.weightProperty]; |
| 91 | + } ); |
| 92 | + |
| 93 | + _this.setBeginTime(); |
| 94 | + var shown = false; |
| 95 | + |
| 96 | + var displayer = function() { |
| 97 | + var fraction = 0.0; |
| 98 | + var endStateCount = 0; |
| 99 | + var hasData = false; |
| 100 | + $j.each( _this.uploads, function( i, upload ) { |
| 101 | + if ( upload.state == _this.endState ) { |
| 102 | + endStateCount++; |
| 103 | + } |
| 104 | + if (upload[_this.progressProperty] !== undefined) { |
| 105 | + fraction += upload[_this.progressProperty] * ( upload[_this.weightProperty] / totalWeight ); |
| 106 | + if (upload[_this.progressProperty] > 0 ) { |
| 107 | + hasData = true; |
| 108 | + } |
| 109 | + } |
| 110 | + } ); |
| 111 | + mw.log( 'hasdata:' + hasData + ' endstatecount:' + endStateCount ); |
| 112 | + // sometimes, the first data we have just tells us that it's over. So only show the bar |
| 113 | + // if we have good data AND the fraction is less than 1. |
| 114 | + if ( hasData && fraction < 1.0 ) { |
| 115 | + if ( ! shown ) { |
| 116 | + _this.showBar(); |
| 117 | + shown = true; |
| 118 | + } |
| 119 | + _this.showProgress( fraction ); |
| 120 | + } |
| 121 | + _this.showCount( endStateCount ); |
| 122 | + |
| 123 | + if ( endStateCount < _this.uploads.length ) { |
| 124 | + setTimeout( displayer, 200 ); |
| 125 | + } else { |
| 126 | + _this.showProgress( 1.0 ); |
| 127 | + // not necessary to hide bar since we're going to the next step. |
| 128 | + /* setTimeout( function() { _this.hideBar(); }, 500 ); */ |
| 129 | + } |
| 130 | + }; |
| 131 | + displayer(); |
| 132 | + }, |
| 133 | + |
| 134 | + |
| 135 | + /** |
| 136 | + * Hide the progress bar with a slideup motion |
| 137 | + */ |
| 138 | + hideBar: function() { |
| 139 | + this.$selector.find( '.mwe-upwiz-progress-bar-etr' ).fadeOut( 200 ); |
| 140 | + }, |
| 141 | + |
| 142 | + /** |
| 143 | + * sets the beginning time (useful for figuring out estimated time remaining) |
| 144 | + * if time parameter omitted, will set beginning time to now |
| 145 | + * |
| 146 | + * @param time optional; the time this bar is presumed to have started (epoch milliseconds) |
| 147 | + */ |
| 148 | + setBeginTime: function( time ) { |
| 149 | + this.beginTime = time ? time : ( new Date() ).getTime(); |
| 150 | + }, |
| 151 | + |
| 152 | + |
| 153 | + /** |
| 154 | + * Show overall progress for the entire UploadWizard |
| 155 | + * The current design doesn't have individual progress bars, just one giant one. |
| 156 | + * We did some tricky calculations in startUploads to try to weight each individual file's progress against |
| 157 | + * the overall progress. |
| 158 | + * @param fraction the amount of whatever it is that's done whatever it's done |
| 159 | + */ |
| 160 | + showProgress: function( fraction ) { |
| 161 | + var _this = this; |
| 162 | + |
| 163 | + _this.$selector.find( '.mwe-upwiz-progress-bar' ).progressbar( 'value', parseInt( fraction * 100, 10 ) ); |
| 164 | + |
| 165 | + var remainingTime = _this.getRemainingTime( fraction ); |
| 166 | + |
| 167 | + if ( remainingTime !== null ) { |
| 168 | + var t = mw.seconds2Measurements( parseInt( remainingTime / 1000, 10 ) ); |
| 169 | + var timeString; |
| 170 | + if (t.hours === 0) { |
| 171 | + if (t.minutes === 0) { |
| 172 | + if (t.seconds === 0) { |
| 173 | + timeString = gM( 'mwe-upwiz-finished' ); |
| 174 | + } else { |
| 175 | + timeString = gM( 'mwe-upwiz-secs-remaining', t.seconds ); |
| 176 | + } |
| 177 | + } else { |
| 178 | + timeString = gM( 'mwe-upwiz-mins-secs-remaining', t.minutes, t.seconds ); |
| 179 | + } |
| 180 | + } else { |
| 181 | + timeString = gM( 'mwe-upwiz-hrs-mins-secs-remaining', t.hours, t.minutes, t.seconds ); |
| 182 | + } |
| 183 | + _this.$selector.find( '.mwe-upwiz-etr' ).html( timeString ); |
| 184 | + } |
| 185 | + }, |
| 186 | + |
| 187 | + /** |
| 188 | + * Calculate remaining time for all uploads to complete. |
| 189 | + * |
| 190 | + * @param fraction fraction of progress to show |
| 191 | + * @return estimated time remaining (in milliseconds) |
| 192 | + */ |
| 193 | + getRemainingTime: function ( fraction ) { |
| 194 | + var _this = this; |
| 195 | + if ( _this.beginTime ) { |
| 196 | + var elapsedTime = ( new Date() ).getTime() - _this.beginTime; |
| 197 | + if ( fraction > 0.0 && elapsedTime > 0 ) { // or some other minimums for good data |
| 198 | + var rate = fraction / elapsedTime; |
| 199 | + return parseInt( ( 1.0 - fraction ) / rate, 10 ); |
| 200 | + } |
| 201 | + } |
| 202 | + return null; |
| 203 | + }, |
| 204 | + |
| 205 | + |
| 206 | + /** |
| 207 | + * Show the overall count as we upload |
| 208 | + * @param count -- the number of items that have done whatever has been done e.g. in "uploaded 2 of 5", this is the 2 |
| 209 | + */ |
| 210 | + showCount: function( count ) { |
| 211 | + var _this = this; |
| 212 | + _this.$selector |
| 213 | + .find( '.mwe-upwiz-count' ) |
| 214 | + .html( gM( 'mwe-upwiz-upload-count', [ count, _this.uploads.length ] ) ); |
| 215 | + } |
| 216 | + |
| 217 | + |
| 218 | +}; |
| 219 | + |
| 220 | + |
| 221 | + |
| 222 | +//mw.setConfig('uploadHandlerClass', mw.MockUploadHandler); // ApiUploadHandler? |
| 223 | + |
| 224 | +// available licenses should be a configuration of the MediaWiki instance, |
| 225 | +// not hardcoded here. |
| 226 | +// but, MediaWiki has no real concept of a License as a first class object -- there are templates and then specially - parsed |
| 227 | +// texts to create menus -- hack on top of hacks -- a bit too much to deal with ATM |
| 228 | +/** |
| 229 | + * Create a group of checkboxes for licenses. N.b. the licenses are named after the templates they invoke. |
| 230 | + * @param div |
| 231 | + * @param values (optional) array of license key names to activate by default |
| 232 | + */ |
| 233 | +mw.UploadWizardLicenseInput = function( selector, values ) { |
| 234 | + var _this = this; |
| 235 | + |
| 236 | + var widgetCount = mw.UploadWizardLicenseInput.prototype.count++; |
| 237 | + |
| 238 | + _this.inputs = []; |
| 239 | + |
| 240 | + // TODO incompatibility check of this license versus others |
| 241 | + |
| 242 | + _this.$selector = $j( selector ); |
| 243 | + _this.$selector.append( $j( '<div class="mwe-error"></div>' ) ); |
| 244 | + |
| 245 | + $j.each( mw.UploadWizard.config[ 'licenses' ], function( i, licenseConfig ) { |
| 246 | + var template = licenseConfig.template; |
| 247 | + var messageKey = licenseConfig.messageKey; |
| 248 | + |
| 249 | + var name = 'license_' + template; |
| 250 | + var id = 'licenseInput' + widgetCount + '_' + name; |
| 251 | + var $input = $j( '<input />' ) |
| 252 | + .attr( { id: id, name: name, type: 'checkbox', value: template } ) |
| 253 | + // we use the selector because events can't be unbound unless they're in the DOM. |
| 254 | + .click( function() { _this.$selector.trigger( 'changeLicenses' ); } ); |
| 255 | + _this.inputs.push( $input ); |
| 256 | + _this.$selector.append( |
| 257 | + $input, |
| 258 | + $j( '<label />' ).attr( { 'for': id } ).html( gM( messageKey ) ), |
| 259 | + $j( '<br/>' ) |
| 260 | + ); |
| 261 | + } ); |
| 262 | + |
| 263 | + if ( values ) { |
| 264 | + _this.setValues( values ); |
| 265 | + } |
| 266 | + |
| 267 | + return _this; |
| 268 | +}; |
| 269 | + |
| 270 | +mw.UploadWizardLicenseInput.prototype = { |
| 271 | + count: 0, |
| 272 | + |
| 273 | + /** |
| 274 | + * Sets the value(s) of a license input. |
| 275 | + * @param object of license-key to boolean values, e.g. { cc_by_sa_30: true, gfdl: true } |
| 276 | + */ |
| 277 | + setValues: function( licenseValues ) { |
| 278 | + var _this = this; |
| 279 | + $j.each( _this.inputs, function( i, $input ) { |
| 280 | + var template = $input.val(); |
| 281 | + $input.attr( 'checked', ~~!!licenseValues[template] ); |
| 282 | + } ); |
| 283 | + // we use the selector because events can't be unbound unless they're in the DOM. |
| 284 | + _this.$selector.trigger( 'changeLicenses' ); |
| 285 | + }, |
| 286 | + |
| 287 | + /** |
| 288 | + * Set the default configured licenses |
| 289 | + */ |
| 290 | + setDefaultValues: function() { |
| 291 | + var _this = this; |
| 292 | + var values = {}; |
| 293 | + $j.each( mw.UploadWizard.config[ 'licenses' ], function( i, licenseConfig ) { |
| 294 | + values[ licenseConfig.template ] = licenseConfig['default']; |
| 295 | + } ); |
| 296 | + _this.setValues( values ); |
| 297 | + }, |
| 298 | + |
| 299 | + /** |
| 300 | + * Gets the templates associated with checked inputs |
| 301 | + * @return array of template names |
| 302 | + */ |
| 303 | + getTemplates: function() { |
| 304 | + return $j( this.inputs ) |
| 305 | + .filter( function() { return this.is( ':checked' ); } ) |
| 306 | + .map( function() { return this.val(); } ); |
| 307 | + }, |
| 308 | + |
| 309 | + /** |
| 310 | + * Check if a valid value is set, also look for incompatible choices. |
| 311 | + * Side effect: if no valid value, add notes to the interface. Add listeners to interface, to revalidate and remove notes. |
| 312 | + * @return boolean; true if a value set, false otherwise |
| 313 | + */ |
| 314 | + valid: function() { |
| 315 | + var _this = this; |
| 316 | + var isValid = true; |
| 317 | + |
| 318 | + if ( ! _this.isSet() ) { |
| 319 | + isValid = false; |
| 320 | + errorHtml = gM( 'mwe-upwiz-deeds-need-license' ); |
| 321 | + } |
| 322 | + |
| 323 | + // XXX something goes here for licenses incompatible with each other |
| 324 | + |
| 325 | + var $errorEl = this.$selector.find( '.mwe-error' ); |
| 326 | + if (isValid) { |
| 327 | + $errorEl.fadeOut(); |
| 328 | + } else { |
| 329 | + // we bind to $selector because unbind() doesn't work on non-DOM objects |
| 330 | + _this.$selector.bind( 'changeLicenses.valid', function() { |
| 331 | + _this.$selector.unbind( 'changeLicenses.valid' ); |
| 332 | + _this.valid(); |
| 333 | + } ); |
| 334 | + $errorEl.html( errorHtml ).show(); |
| 335 | + } |
| 336 | + |
| 337 | + return isValid; |
| 338 | + }, |
| 339 | + |
| 340 | + |
| 341 | + /** |
| 342 | + * Returns true if any license is set |
| 343 | + * @return boolean |
| 344 | + */ |
| 345 | + isSet: function() { |
| 346 | + return this.getTemplates().length > 0; |
| 347 | + } |
| 348 | + |
| 349 | +}; |
| 350 | + |
| 351 | + |
| 352 | +/** |
| 353 | + * Represents the upload -- in its local and remote state. (Possibly those could be separate objects too...) |
| 354 | + * This is our 'model' object if we are thinking MVC. Needs to be better factored, lots of feature envy with the UploadWizard |
| 355 | + * states: |
| 356 | + * 'new' 'transporting' 'transported' 'details' 'submitting-details' 'complete' |
| 357 | + * should fork this into two -- local and remote, e.g. filename |
| 358 | + */ |
| 359 | +mw.UploadWizardUpload = function( filesDiv ) { |
| 360 | + var _this = this; |
| 361 | + _this.state = 'new'; |
| 362 | + _this.transportWeight = 1; // default |
| 363 | + _this.detailsWeight = 1; // default |
| 364 | + _this._thumbnails = {}; |
| 365 | + _this.imageinfo = {}; |
| 366 | + _this.title = undefined; |
| 367 | + _this.filename = undefined; |
| 368 | + _this.originalFilename = undefined; |
| 369 | + _this.mimetype = undefined; |
| 370 | + _this.extension = undefined; |
| 371 | + |
| 372 | + // details |
| 373 | + _this.ui = new mw.UploadWizardUploadInterface( _this, filesDiv ); |
| 374 | + |
| 375 | + // handler -- usually ApiUploadHandler |
| 376 | + // _this.handler = new ( mw.UploadWizard.config[ 'uploadHandlerClass' ] )( _this ); |
| 377 | + // _this.handler = new mw.MockUploadHandler( _this ); |
| 378 | + _this.handler = new mw.ApiUploadHandler( _this ); |
| 379 | +}; |
| 380 | + |
| 381 | +mw.UploadWizardUpload.prototype = { |
| 382 | + |
| 383 | + acceptDeed: function( deed ) { |
| 384 | + var _this = this; |
| 385 | + _this.deed.applyDeed( _this ); |
| 386 | + }, |
| 387 | + |
| 388 | + /** |
| 389 | + * start |
| 390 | + */ |
| 391 | + start: function() { |
| 392 | + var _this = this; |
| 393 | + _this.setTransportProgress(0.0); |
| 394 | + _this.ui.start(); |
| 395 | + _this.handler.start(); |
| 396 | + }, |
| 397 | + |
| 398 | + /** |
| 399 | + * remove this upload. n.b. we trigger a removeUpload this is usually triggered from |
| 400 | + */ |
| 401 | + remove: function() { |
| 402 | + if ( this.details && this.details.div ) { |
| 403 | + this.details.div.remove(); |
| 404 | + } |
| 405 | + if ( this.thanksDiv ) { |
| 406 | + this.thanksDiv.remove(); |
| 407 | + } |
| 408 | + // we signal to the wizard to update itself, which has to delete the final vestige of |
| 409 | + // this upload (the ui.div). We have to do this silly dance because we |
| 410 | + // trigger through the div. Triggering through objects doesn't always work. |
| 411 | + $j( this.ui.div ).trigger( 'removeUploadEvent' ); |
| 412 | + }, |
| 413 | + |
| 414 | + |
| 415 | + /** |
| 416 | + * Wear our current progress, for observing processes to see |
| 417 | + * XXX this is kind of a misnomer; this event is not firing except for the very first time. |
| 418 | + * @param fraction |
| 419 | + */ |
| 420 | + setTransportProgress: function ( fraction ) { |
| 421 | + var _this = this; |
| 422 | + _this.state = 'transporting'; |
| 423 | + _this.transportProgress = fraction; |
| 424 | + $j( _this.ui.div ).trigger( 'transportProgressEvent' ); |
| 425 | + }, |
| 426 | + |
| 427 | + /** |
| 428 | + * To be executed when an individual upload finishes. Processes the result and updates step 2's details |
| 429 | + * @param result the API result in parsed JSON form |
| 430 | + */ |
| 431 | + setTransported: function( result ) { |
| 432 | + var _this = this; |
| 433 | + _this.state = 'transported'; |
| 434 | + _this.transportProgress = 1; |
| 435 | + $j( _this.ui.div ).trigger( 'transportedEvent' ); |
| 436 | + |
| 437 | + if ( result.upload && result.upload.imageinfo && result.upload.imageinfo.descriptionurl ) { |
| 438 | + // success |
| 439 | + _this.extractUploadInfo( result ); |
| 440 | + _this.deedPreview.setup(); |
| 441 | + _this.details.populate(); |
| 442 | + |
| 443 | + } else if ( result.upload && result.upload.sessionkey ) { |
| 444 | + // there was a warning - type error which prevented it from adding the result to the db |
| 445 | + if ( result.upload.warnings.duplicate ) { |
| 446 | + var duplicates = result.upload.warnings.duplicate; |
| 447 | + _this.details.errorDuplicate( result.upload.sessionkey, duplicates ); |
| 448 | + } |
| 449 | + |
| 450 | + // and other errors that result in a stash |
| 451 | + } else { |
| 452 | + alert("failure!"); |
| 453 | + // we may want to tag or otherwise queue it as an upload to retry |
| 454 | + } |
| 455 | + |
| 456 | + |
| 457 | + }, |
| 458 | + |
| 459 | + |
| 460 | + /** |
| 461 | + * call when the file is entered into the file input |
| 462 | + * get as much data as possible -- maybe exif, even thumbnail maybe |
| 463 | + */ |
| 464 | + extractLocalFileInfo: function( localFilename ) { |
| 465 | + var _this = this; |
| 466 | + if (false) { // FileAPI, one day |
| 467 | + _this.transportWeight = getFileSize(); |
| 468 | + } |
| 469 | + _this.extension = mw.UploadWizardUtil.getExtension( localFilename ); |
| 470 | + // XXX add filename, original filename, extension, whatever else is interesting. |
| 471 | + }, |
| 472 | + |
| 473 | + |
| 474 | + /** |
| 475 | + * Accept the result from a successful API upload transport, and fill our own info |
| 476 | + * |
| 477 | + * @param result The JSON object from a successful API upload result. |
| 478 | + */ |
| 479 | + extractUploadInfo: function( result ) { |
| 480 | + var _this = this; |
| 481 | + |
| 482 | + _this.filename = result.upload.filename; |
| 483 | + _this.title = wgFormattedNamespaces[wgNamespaceIds['file']] + ':' + _this.filename; |
| 484 | + |
| 485 | + _this.extractImageInfo( result.upload.imageinfo ); |
| 486 | + |
| 487 | + }, |
| 488 | + |
| 489 | + /** |
| 490 | + * Extract image info into our upload object |
| 491 | + * Image info is obtained from various different API methods |
| 492 | + * @param imageinfo JSON object obtained from API result. |
| 493 | + */ |
| 494 | + extractImageInfo: function( imageinfo ) { |
| 495 | + var _this = this; |
| 496 | + for ( var key in imageinfo ) { |
| 497 | + // we get metadata as list of key-val pairs; convert to object for easier lookup. Assuming that EXIF fields are unique. |
| 498 | + if ( key == 'metadata' ) { |
| 499 | + _this.imageinfo.metadata = {}; |
| 500 | + if ( imageinfo.metadata && imageinfo.metadata.length ) { |
| 501 | + $j.each( imageinfo.metadata, function( i, pair ) { |
| 502 | + if ( pair !== undefined ) { |
| 503 | + _this.imageinfo.metadata[pair['name'].toLowerCase()] = pair['value']; |
| 504 | + } |
| 505 | + } ); |
| 506 | + } |
| 507 | + } else { |
| 508 | + _this.imageinfo[key] = imageinfo[key]; |
| 509 | + } |
| 510 | + } |
| 511 | + |
| 512 | + // we should already have an extension, but if we don't... |
| 513 | + if ( _this.extension === undefined ) { |
| 514 | + var extension = mw.UploadWizardUtil.getExtension( _this.imageinfo.url ); |
| 515 | + if ( !extension ) { |
| 516 | + if ( _this.imageinfo.mimetype ) { |
| 517 | + if ( mw.UploadWizardUtil.mimetypeToExtension[ _this.imageinfo.mimetype ] ) { |
| 518 | + extension = mw.UploadWizardUtil.mimetypeToExtension[ _this.imageinfo.mimetype ]; |
| 519 | + } |
| 520 | + } |
| 521 | + } |
| 522 | + } |
| 523 | + }, |
| 524 | + |
| 525 | + /** |
| 526 | + * Supply information to create a thumbnail for this Upload. Runs async, with a callback. |
| 527 | + * It is assumed you don't call this until it's been transported. |
| 528 | + * |
| 529 | + * XXX should check if we really need this second API call or if we can get MediaWiki to make us a thumbnail URL upon upload |
| 530 | + * |
| 531 | + * @param width - desired width of thumbnail (height will scale to match) |
| 532 | + * @param callback - callback to execute once thumbnail has been obtained -- must accept object with properties of width, height, and url. |
| 533 | + */ |
| 534 | + getThumbnail: function( width, callback ) { |
| 535 | + var _this = this; |
| 536 | + if ( _this._thumbnails[ "width" + width ] !== undefined ) { |
| 537 | + callback( _this._thumbnails[ "width" + width ] ); |
| 538 | + return; |
| 539 | + } |
| 540 | + |
| 541 | + var apiUrl = mw.UploadWizard.config.apiUrl; |
| 542 | + |
| 543 | + var params = { |
| 544 | + 'titles': _this.title, |
| 545 | + 'prop': 'imageinfo', |
| 546 | + 'iiurlwidth': width, |
| 547 | + 'iiprop': 'url' |
| 548 | + }; |
| 549 | + |
| 550 | + mw.getJSON( apiUrl, params, function( data ) { |
| 551 | + if ( !data || !data.query || !data.query.pages ) { |
| 552 | + mw.log(" No data? "); |
| 553 | + // XXX do something about the thumbnail spinner, maybe call the callback with a broken image. |
| 554 | + return; |
| 555 | + } |
| 556 | + |
| 557 | + if ( data.query.pages[-1] ) { |
| 558 | + // XXX do something about the thumbnail spinner, maybe call the callback with a broken image. |
| 559 | + return; |
| 560 | + } |
| 561 | + for ( var page_id in data.query.pages ) { |
| 562 | + var page = data.query.pages[ page_id ]; |
| 563 | + if ( ! page.imageinfo ) { |
| 564 | + alert("imageinfo missing"); |
| 565 | + // not found? error |
| 566 | + } else { |
| 567 | + var imageInfo = page.imageinfo[0]; |
| 568 | + var thumbnail = { |
| 569 | + width: imageInfo.thumbwidth, |
| 570 | + height: imageInfo.thumbheight, |
| 571 | + url: imageInfo.thumburl |
| 572 | + }; |
| 573 | + _this._thumbnails[ "width" + width ] = thumbnail; |
| 574 | + callback( thumbnail ); |
| 575 | + } |
| 576 | + } |
| 577 | + } ); |
| 578 | + |
| 579 | + }, |
| 580 | + |
| 581 | + |
| 582 | + /** |
| 583 | + * look up thumbnail info and set it in HTML, with loading spinner |
| 584 | + * it might be interesting to make this more of a publish/subscribe thing, since we have to do this 3x |
| 585 | + * the callbacks may pile up, getting unnecessary info |
| 586 | + * |
| 587 | + * @param selector |
| 588 | + * @param width |
| 589 | + */ |
| 590 | + setThumbnail: function( selector, width ) { |
| 591 | + var _this = this; |
| 592 | + if ( typeof width === 'undefined' || width === null || width <= 0 ) { |
| 593 | + width = mw.UploadWizard.config[ 'thumbnailWidth' ]; |
| 594 | + } |
| 595 | + width = parseInt( width, 10 ); |
| 596 | + |
| 597 | + var callback = function( thumbnail ) { |
| 598 | + // side effect: will replace thumbnail's loadingSpinner |
| 599 | + $j( selector ).html( |
| 600 | + $j('<a/>') |
| 601 | + .attr( { 'href': _this.imageinfo.descriptionurl, |
| 602 | + 'target' : '_new' } ) |
| 603 | + .append( |
| 604 | + $j( '<img/>' ) |
| 605 | + .attr( 'width', thumbnail.width ) |
| 606 | + .attr( 'height', thumbnail.height ) |
| 607 | + .attr( 'src', thumbnail.url ) ) ); |
| 608 | + }; |
| 609 | + |
| 610 | + $j( selector ).loadingSpinner(); |
| 611 | + _this.getThumbnail( width, callback ); |
| 612 | + } |
| 613 | + |
| 614 | + |
| 615 | + |
| 616 | +}; |
| 617 | + |
| 618 | +/** |
| 619 | + * Create an interface fragment corresponding to a file input, suitable for Upload Wizard. |
| 620 | + * @param upload |
| 621 | + * @param div to insert file interface |
| 622 | + * @param addInterface interface to add a new one (assumed that we start out there) |
| 623 | + */ |
| 624 | +mw.UploadWizardUploadInterface = function( upload, filesDiv ) { |
| 625 | + var _this = this; |
| 626 | + |
| 627 | + _this.upload = upload; |
| 628 | + |
| 629 | + // may need to collaborate with the particular upload type sometimes |
| 630 | + // for the interface, as well as the uploadwizard. OY. |
| 631 | + _this.div = $j('<div class="mwe-upwiz-file"></div>').get(0); |
| 632 | + _this.isFilled = false; |
| 633 | + |
| 634 | + _this.fileInputCtrl = $j('<input size="1" class="mwe-upwiz-file-input" name="file" type="file"/>') |
| 635 | + .change( function() { _this.fileChanged(); } ) |
| 636 | + .get(0); |
| 637 | + |
| 638 | + |
| 639 | + visibleFilenameDiv = $j('<div class="mwe-upwiz-visible-file"></div>') |
| 640 | + .append( |
| 641 | + $j.fn.removeCtrl( 'mwe-upwiz-remove-upload', function() { _this.upload.remove(); } ), |
| 642 | + |
| 643 | + $j( '<div class="mwe-upwiz-file-indicator"></div>' ), |
| 644 | + |
| 645 | + $j( '<div class="mwe-upwiz-visible-file-filename">' ) |
| 646 | + .append( |
| 647 | + $j( '<span class="ui-icon ui-icon-document" style="display: inline-block;" />' ), |
| 648 | + $j( '<span class="mwe-upwiz-visible-file-filename-text"/>' ) |
| 649 | + ) |
| 650 | + |
| 651 | + ); |
| 652 | + |
| 653 | + //_this.errorDiv = $j('<div class="mwe-upwiz-upload-error mwe-upwiz-file-indicator" style="display: none;"></div>').get(0); |
| 654 | + |
| 655 | + _this.filenameCtrl = $j('<input type="hidden" name="filename" value=""/>').get(0); |
| 656 | + |
| 657 | + // this file Ctrl container is placed over other interface elements, intercepts clicks and gives them to the file input control. |
| 658 | + // however, we want to pass hover events to interface elements that we are over, hence the bindings. |
| 659 | + // n.b. not using toggleClass because it often gets this event wrong -- relies on previous state to know what to do |
| 660 | + _this.fileCtrlContainer = $j('<div class="mwe-upwiz-file-ctrl-container">'); |
| 661 | +/* |
| 662 | + .bind( 'mouseenter', function(e) { _this.addFileCtrlHover(e); } ) |
| 663 | + .bind( 'mouseleave', function(e) { _this.removeFileCtrlHover(e); } ); |
| 664 | +*/ |
| 665 | + |
| 666 | + // the css trickery (along with css) |
| 667 | + // here creates a giant size file input control which is contained within a div and then |
| 668 | + // clipped for overflow. The effect is that we have a div (ctrl-container) we can position anywhere |
| 669 | + // which works as a file input. It will be set to opacity:0 and then we can do whatever we want with |
| 670 | + // interface "below". |
| 671 | + // XXX caution -- if the add file input changes size we won't match, unless we add some sort of event to catch this. |
| 672 | + _this.form = $j('<form class="mwe-upwiz-form"></form>') |
| 673 | + .append( visibleFilenameDiv ) |
| 674 | + .append( _this.fileCtrlContainer |
| 675 | + .append( _this.fileInputCtrl ) |
| 676 | + ) |
| 677 | + .append( _this.filenameCtrl ).get( 0 ); |
| 678 | + |
| 679 | + |
| 680 | + $j( _this.div ).append( _this.form ); |
| 681 | + |
| 682 | + // XXX evil hardcoded |
| 683 | + // we don't really need filesdiv if we do it this way? |
| 684 | + $j( _this.div ).insertBefore( '#mwe-upwiz-upload-ctrls' ); // append( _this.div ); |
| 685 | + |
| 686 | + // _this.progressBar = ( no progress bar for individual uploads yet ) |
| 687 | + // we bind to the ui div since unbind doesn't work for non-DOM objects |
| 688 | + $j( _this.div ).bind( 'transportProgressEvent', function(e) { _this.showTransportProgress(); } ); |
| 689 | + $j( _this.div ).bind( 'transportedEvent', function(e) { _this.showTransported(); } ); |
| 690 | + |
| 691 | +}; |
| 692 | + |
| 693 | + |
| 694 | +mw.UploadWizardUploadInterface.prototype = { |
| 695 | + /** |
| 696 | + * Things to do to this interface once we start uploading |
| 697 | + */ |
| 698 | + start: function() { |
| 699 | + var _this = this; |
| 700 | + // remove hovering |
| 701 | + $j( _this.div ) |
| 702 | + .unbind( 'mouseenter mouseover mouseleave mouseout' ); |
| 703 | + |
| 704 | + // remove delete control |
| 705 | + $j( _this.div ) |
| 706 | + .find( '.mwe-upwiz-remove-ctrl' ) |
| 707 | + .unbind( 'mouseenter mouseover mouseleave mouseout' ) |
| 708 | + .remove(); |
| 709 | + }, |
| 710 | + |
| 711 | + busy: function() { |
| 712 | + var _this = this; |
| 713 | + // for now we implement this as looking like "100% progress" |
| 714 | + // e.g. an animated bar that takes up all the space |
| 715 | + // _this.showTransportProgress(); |
| 716 | + }, |
| 717 | + |
| 718 | + /** |
| 719 | + * |
| 720 | + */ |
| 721 | + showIndicatorMessage: function( classToRemove, classToAdd, msgKey ) { |
| 722 | + var _this = this; |
| 723 | + var $indicator = $j( _this.div ).find( '.mwe-upwiz-file-indicator' ); |
| 724 | + if ( classToRemove ) { |
| 725 | + $indicator.removeClass( classToRemove ); |
| 726 | + } |
| 727 | + if ( classToAdd ) { |
| 728 | + $indicator.addClass( classToAdd ); |
| 729 | + } |
| 730 | + $indicator.html( gM( msgKey ) ); |
| 731 | + $j( _this.div ).find( '.mwe-upwiz-visible-file-filename' ) |
| 732 | + .css( 'margin-right', ( $indicator.outerWidth() + 24 ).toString() + 'px' ); |
| 733 | + $indicator.css( 'visibility', 'visible' ); |
| 734 | + }, |
| 735 | + |
| 736 | + /** |
| 737 | + * Put the visual state of an individual upload ito "progress" |
| 738 | + * @param fraction The fraction of progress. Float between 0 and 1 |
| 739 | + */ |
| 740 | + showTransportProgress: function() { |
| 741 | + this.showIndicatorMessage( null, 'mwe-upwiz-status-progress', 'mwe-upwiz-uploading' ); |
| 742 | + // update individual progress bar with fraction? |
| 743 | + }, |
| 744 | + |
| 745 | + /** |
| 746 | + * Execute when this upload is transported; cleans up interface. |
| 747 | + */ |
| 748 | + showTransported: function() { |
| 749 | + this.showIndicatorMessage( 'mwe-upwiz-status-progress', 'mwe-upwiz-status-completed', 'mwe-upwiz-transported' ); |
| 750 | + }, |
| 751 | + |
| 752 | + /** |
| 753 | + * Run this when the value of the file input has changed. Check the file for various forms of goodness. |
| 754 | + * If okay, then update the visible filename (due to CSS trickery the real file input is invisible) |
| 755 | + */ |
| 756 | + fileChanged: function() { |
| 757 | + var _this = this; |
| 758 | + _this.clearErrors(); |
| 759 | + _this.upload.extractLocalFileInfo( $j( _this.fileInputCtrl ).val() ); |
| 760 | + if ( _this.isGoodExtension( _this.upload.extension ) ) { |
| 761 | + _this.updateFilename(); |
| 762 | + } else { |
| 763 | + //_this.error( 'bad-filename-extension', ext ); |
| 764 | + alert("bad extension"); |
| 765 | + } |
| 766 | + }, |
| 767 | + |
| 768 | + /** |
| 769 | + * Move the file input to cover a certain element on the page. |
| 770 | + * We use invisible file inputs because this is the only way to style a file input |
| 771 | + * or otherwise get it to do what you want. |
| 772 | + * It is helpful to sometimes move them to cover certain elements on the page, and |
| 773 | + * even to pass events like hover |
| 774 | + * @param selector jquery-compatible selector, for a single element |
| 775 | + */ |
| 776 | + moveFileInputToCover: function( selector, offset ) { |
| 777 | + |
| 778 | + //mw.log( "moving to cover " + selector ); |
| 779 | + var _this = this; |
| 780 | + var $covered = $j( selector ); |
| 781 | + |
| 782 | + var topOffset, rightOffset, bottomOffset, leftOffset; |
| 783 | + topOffset = rightOffset = bottomOffset = leftOffset = 0; |
| 784 | + if (typeof offset != 'undefined' ) { |
| 785 | + topOffset = offset[0]; |
| 786 | + rightOffset = offset[1]; |
| 787 | + bottomOffset = offset[2]; |
| 788 | + leftOffset = offset[3]; |
| 789 | + } |
| 790 | + var widthOffset = rightOffset - leftOffset; |
| 791 | + var heightOffset = bottomOffset - topOffset; |
| 792 | + //mw.log( "position: " ); |
| 793 | + //mw.log( $covered.position() ); |
| 794 | + var position = $covered.position(); |
| 795 | + _this.fileCtrlContainer |
| 796 | + .css( $covered.position() ) |
| 797 | + .width( $covered.outerWidth() ) |
| 798 | + .height( $covered.outerHeight() ); |
| 799 | + /* |
| 800 | + { |
| 801 | + 'top': (position['top'] + topOffset).toString() + 'px', |
| 802 | + 'left': (position['left'] + leftOffset).toString() + 'px' |
| 803 | + } ) |
| 804 | + .width( ($covered.outerWidth() + widthOffset).toString() + 'px' ) |
| 805 | + .height( ($covered.outerHeight() + heightOffset).toString() + 'px' ); |
| 806 | + */ |
| 807 | + |
| 808 | + // shift the file input over with negative margins, |
| 809 | + // internal to the overflow-containing div, so the div shows all button |
| 810 | + // and none of the textfield-like input |
| 811 | + $j( _this.fileInputCtrl ).css( { |
| 812 | + 'margin-left': '-' + ~~( $j( _this.fileInputCtrl).width() - $covered.outerWidth() - 10 ) + 'px', |
| 813 | + 'margin-top' : '-' + ~~( $j( _this.fileInputCtrl).height() - $covered.outerHeight() - 10 ) + 'px' |
| 814 | + } ); |
| 815 | + |
| 816 | + |
| 817 | + }, |
| 818 | + |
| 819 | + /** |
| 820 | + * this does two things: |
| 821 | + * 1 ) since the file input has been hidden with some clever CSS ( to avoid x-browser styling issues ), |
| 822 | + * update the visible filename |
| 823 | + * |
| 824 | + * 2 ) update the filename desired when added to MediaWiki. This should be RELATED to the filename on the filesystem, |
| 825 | + * but it should be silently fixed so that it does not trigger uniqueness conflicts. i.e. if server has cat.jpg we change ours to cat_2.jpg. |
| 826 | + * This is hard to do in a scalable fashion on the client; we don't want to do 12 api calls to get cat_12.jpg. |
| 827 | + * Ideally we should ask the SERVER for a decently unique filename related to our own. |
| 828 | + * So, at the moment, this is hacked with a guaranteed - unique filename instead. |
| 829 | + */ |
| 830 | + updateFilename: function() { |
| 831 | + var _this = this; |
| 832 | + var path = $j(_this.fileInputCtrl).attr('value'); |
| 833 | + |
| 834 | + |
| 835 | + // visible filename |
| 836 | + $j( _this.form ).find( '.mwe-upwiz-visible-file-filename-text' ).html( path ); |
| 837 | + |
| 838 | + // desired filename |
| 839 | + var filename = _this.convertPathToFilename( path ); |
| 840 | + _this.upload.originalFilename = filename; |
| 841 | + // this is a hack to get a filename guaranteed unique. |
| 842 | + uniqueFilename = mw.UploadWizard.config[ 'userName' ] + "_" + ( new Date() ).getTime() + "_" + filename; |
| 843 | + $j( _this.filenameCtrl ).attr( 'value', uniqueFilename ); |
| 844 | + |
| 845 | + if ( ! _this.isFilled ) { |
| 846 | + var $div = $j( _this.div ); |
| 847 | + _this.isFilled = true; |
| 848 | + $div.addClass( 'filled' ); |
| 849 | + |
| 850 | + |
| 851 | + // cover the div with the file input. |
| 852 | + // we use the visible-file div because it has the same offsetParent as the file input |
| 853 | + // the second argument offsets the fileinput to the right so there's room for the close icon to get mouse events |
| 854 | + _this.moveFileInputToCover( |
| 855 | + $div.find( '.mwe-upwiz-visible-file-filename' ) |
| 856 | + ); |
| 857 | + |
| 858 | + // Highlight the file on mouseover (and also show controls like the remove control). |
| 859 | + // |
| 860 | + // On Firefox there are bugs related to capturing mouse events on inputs, so we seem to miss the |
| 861 | + // mouseenter or mouseleave events randomly. It's only really bad if we miss mouseleave, |
| 862 | + // and have two highlights visible. so we add another call to REALLY make sure that other highlights |
| 863 | + // are deactivated. |
| 864 | + // http://code.google.com/p/fbug/issues/detail?id=2075 |
| 865 | + // |
| 866 | + // ALSO: When file inputs are adjacent, Firefox misses the "mouseenter" and "mouseleave" events. |
| 867 | + // Consequently we have to bind to "mouseover" and "mouseout" as well even though that's not as efficient. |
| 868 | + $div.bind( 'mouseenter mouseover', function() { |
| 869 | + $div.addClass( 'hover' ); |
| 870 | + $j( '#mwe-upwiz-files' ) |
| 871 | + .children() |
| 872 | + .filter( function() { return this !== _this.div; } ) |
| 873 | + .removeClass('hover'); |
| 874 | + }, false ); |
| 875 | + $div.bind( 'mouseleave mouseout', function() { |
| 876 | + $div.removeClass( 'hover' ); |
| 877 | + }, false ); |
| 878 | + $j( _this.div ).trigger( 'filled' ); |
| 879 | + } else { |
| 880 | + $j( _this.div ).trigger( 'filenameAccepted' ); |
| 881 | + } |
| 882 | + }, |
| 883 | + |
| 884 | + /** |
| 885 | + * Remove any complaints we had about errors and such |
| 886 | + * XXX this should be changed to something Theme compatible |
| 887 | + */ |
| 888 | + clearErrors: function() { |
| 889 | + var _this = this; |
| 890 | + $j( _this.div ).removeClass( 'mwe-upwiz-upload-error '); |
| 891 | + $j( _this.errorDiv ).hide().empty(); |
| 892 | + }, |
| 893 | + |
| 894 | + /** |
| 895 | + * Show an error with the upload |
| 896 | + */ |
| 897 | + error: function() { |
| 898 | + var _this = this; |
| 899 | + var args = Array.prototype.slice.call( arguments ); // copies arguments into a real array |
| 900 | + var msg = 'mwe-upwiz-upload-error-' + args[0]; |
| 901 | + $j( _this.errorDiv ).append( $j( '<p class="mwe-upwiz-upload-error">' + gM( msg, args.slice( 1 ) ) + '</p>') ); |
| 902 | + // apply a error style to entire did |
| 903 | + $j( _this.div ).addClass( 'mwe-upwiz-upload-error' ); |
| 904 | + $j( _this.errorDiv ).show(); |
| 905 | + }, |
| 906 | + |
| 907 | + /** |
| 908 | + * Get the extension of the path in fileInputCtrl |
| 909 | + * @return extension as string |
| 910 | + */ |
| 911 | + getExtension: function() { |
| 912 | + var _this = this; |
| 913 | + var path = $j(_this.fileInputCtrl).attr('value'); |
| 914 | + return mw.UploadWizardUtil.getExtension(path); |
| 915 | + }, |
| 916 | + |
| 917 | + /** |
| 918 | + * XXX this is common utility code |
| 919 | + * used when converting contents of a file input and coming up with a suitable "filename" for mediawiki |
| 920 | + * test: what if path is length 0 |
| 921 | + * what if path is all separators |
| 922 | + * what if path ends with a separator character |
| 923 | + * what if it ends with multiple separator characters |
| 924 | + * |
| 925 | + * @param path |
| 926 | + * @return filename suitable for mediawiki as string |
| 927 | + */ |
| 928 | + convertPathToFilename: function( path ) { |
| 929 | + if (path === undefined || path === '') { |
| 930 | + return ''; |
| 931 | + } |
| 932 | + |
| 933 | + var lastFileSeparatorIdx = Math.max(path.lastIndexOf( '/' ), path.lastIndexOf( '\\' )); |
| 934 | + // lastFileSeparatorIdx is now -1 if no separator found, or some index in the string. |
| 935 | + // so, +1, that is either 0 ( beginning of string ) or the character after last separator. |
| 936 | + // caution! could go past end of string... need to be more careful |
| 937 | + var filename = path.substr( lastFileSeparatorIdx + 1 ); |
| 938 | + return mw.UploadWizardUtil.pathToTitle( filename ); |
| 939 | + |
| 940 | + |
| 941 | + |
| 942 | + }, |
| 943 | + |
| 944 | + /** |
| 945 | + * XXX this is common utility code |
| 946 | + * copied because we'll probably need it... stripped from old doDestinationFill |
| 947 | + * this is used when checking for "bad" extensions in a filename. |
| 948 | + * @param ext |
| 949 | + * @return boolean if extension was acceptable |
| 950 | + */ |
| 951 | + isGoodExtension: function( ext ) { |
| 952 | + var _this = this; |
| 953 | + var found = false; |
| 954 | + var extensions = mw.UploadWizard.config[ 'fileExtensions' ]; |
| 955 | + if ( extensions ) { |
| 956 | + for ( var i = 0; i < extensions.length; i++ ) { |
| 957 | + if ( extensions[i].toLowerCase() == ext ) { |
| 958 | + found = true; |
| 959 | + } |
| 960 | + } |
| 961 | + } |
| 962 | + return found; |
| 963 | + } |
| 964 | + |
| 965 | +}; |
| 966 | + |
| 967 | +/** |
| 968 | + * Object that represents an indvidual language description, in the details portion of Upload Wizard |
| 969 | + * @param languageCode -- string |
| 970 | + * @param firstRequired -- boolean -- the first description is required and should be validated and displayed a bit differently |
| 971 | + */ |
| 972 | +mw.UploadWizardDescription = function( languageCode, required ) { |
| 973 | + var _this = this; |
| 974 | + mw.UploadWizardDescription.prototype.count++; |
| 975 | + _this.id = 'description' + mw.UploadWizardDescription.prototype.count; |
| 976 | + |
| 977 | + // XXX for some reason this display:block is not making it into HTML |
| 978 | + var errorLabelDiv = $j( '<div class="mwe-upwiz-details-input-error">' |
| 979 | + + '<label generated="true" class="mwe-validator-error" for="' + _this.id + '" />' |
| 980 | + + '</div>' ); |
| 981 | + |
| 982 | + var fieldnameDiv = $j( '<div class="mwe-upwiz-details-fieldname" />' ); |
| 983 | + if ( required ) { |
| 984 | + fieldnameDiv.append( gM( 'mwe-upwiz-desc' ) ).requiredFieldLabel(); |
| 985 | + } |
| 986 | + |
| 987 | + |
| 988 | + // Logic copied from MediaWiki:UploadForm.js |
| 989 | + // Per request from Portuguese and Brazilian users, treat Brazilian Portuguese as Portuguese. |
| 990 | + if (languageCode == 'pt-br') { |
| 991 | + languageCode = 'pt'; |
| 992 | + // this was also in UploadForm.js, but without the heartwarming justification |
| 993 | + } else if (languageCode == 'en-gb') { |
| 994 | + languageCode = 'en'; |
| 995 | + } |
| 996 | + |
| 997 | + _this.languageMenu = mw.LanguageUpWiz.getMenu( 'lang', languageCode ); |
| 998 | + $j(_this.languageMenu).addClass( 'mwe-upwiz-desc-lang-select' ); |
| 999 | + |
| 1000 | + _this.input = $j( '<textarea name="' + _this.id + '" rows="2" cols="36" class="mwe-upwiz-desc-lang-text"></textarea>' ) |
| 1001 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-description' ) ) |
| 1002 | + .growTextArea() |
| 1003 | + .tipsyPlus( { plus: 'even more stuff' } ); |
| 1004 | + |
| 1005 | + // descriptions |
| 1006 | + _this.div = $j('<div class="mwe-upwiz-details-descriptions-container ui-helper-clearfix"></div>' ) |
| 1007 | + .append( errorLabelDiv, fieldnameDiv, _this.languageMenu, _this.input ); |
| 1008 | + |
| 1009 | +}; |
| 1010 | + |
| 1011 | +mw.UploadWizardDescription.prototype = { |
| 1012 | + |
| 1013 | + /* widget count for auto incrementing */ |
| 1014 | + count: 0, |
| 1015 | + |
| 1016 | + /** |
| 1017 | + * Obtain text of this description, suitable for including into Information template |
| 1018 | + * @return wikitext as a string |
| 1019 | + */ |
| 1020 | + getWikiText: function() { |
| 1021 | + var _this = this; |
| 1022 | + var description = $j( _this.input ).val().trim(); |
| 1023 | + // we assume that form validation has caught this problem if this is a required field |
| 1024 | + // if not, assume the user is trying to blank a description in another language |
| 1025 | + if ( description.length === 0 ) { |
| 1026 | + return ''; |
| 1027 | + } |
| 1028 | + var language = $j( _this.languageMenu ).val().trim(); |
| 1029 | + var fix = mw.UploadWizard.config[ "languageTemplateFixups" ]; |
| 1030 | + if (fix[language]) { |
| 1031 | + language = fix[language]; |
| 1032 | + } |
| 1033 | + return '{{' + language + '|1=' + description + '}}'; |
| 1034 | + }, |
| 1035 | + |
| 1036 | + /** |
| 1037 | + * defer adding rules until it's in a form |
| 1038 | + * @return validator |
| 1039 | + */ |
| 1040 | + addValidationRules: function( required ) { |
| 1041 | + // validator must find a form, so we add rules here |
| 1042 | + return this.input.rules( "add", { |
| 1043 | + minlength: mw.UploadWizard.config[ 'minDescriptionLength' ], |
| 1044 | + maxlength: mw.UploadWizard.config[ 'maxDescriptionLength' ], |
| 1045 | + required: required, |
| 1046 | + messages: { |
| 1047 | + required: gM( 'mwe-upwiz-error-blank' ), |
| 1048 | + minlength: gM( 'mwe-upwiz-error-too-short', mw.UploadWizard.config[ 'minDescriptionLength' ] ), |
| 1049 | + maxlength: gM( 'mwe-upwiz-error-too-long', mw.UploadWizard.config[ 'maxDescriptionLength' ] ) |
| 1050 | + } |
| 1051 | + } ); |
| 1052 | + } |
| 1053 | +}; |
| 1054 | + |
| 1055 | +/** |
| 1056 | + * Object that represents the Details (step 2) portion of the UploadWizard |
| 1057 | + * n.b. each upload gets its own details. |
| 1058 | + * |
| 1059 | + * XXX a lot of this construction is not really the jQuery way. |
| 1060 | + * The correct thing would be to have some hidden static HTML |
| 1061 | + * on the page which we clone and slice up with selectors. Inputs can still be members of the object |
| 1062 | + * but they'll be found by selectors, not by creating them as members and then adding them to a DOM structure. |
| 1063 | + * |
| 1064 | + * XXX this should have styles for what mode we're in |
| 1065 | + * |
| 1066 | + * @param UploadWizardUpload |
| 1067 | + * @param containerDiv The div to put the interface into |
| 1068 | + */ |
| 1069 | +mw.UploadWizardDetails = function( upload, containerDiv ) { |
| 1070 | + |
| 1071 | + var _this = this; |
| 1072 | + _this.upload = upload; |
| 1073 | + |
| 1074 | + _this.descriptions = []; |
| 1075 | + |
| 1076 | + _this.div = $j( '<div class="mwe-upwiz-info-file ui-helper-clearfix"></div>' ); |
| 1077 | + |
| 1078 | + _this.thumbnailDiv = $j( '<div class="mwe-upwiz-thumbnail mwe-upwiz-thumbnail-side"></div>' ); |
| 1079 | + |
| 1080 | + _this.dataDiv = $j( '<div class="mwe-upwiz-data"></div>' ); |
| 1081 | + |
| 1082 | + // descriptions |
| 1083 | + _this.descriptionsDiv = $j( '<div class="mwe-upwiz-details-descriptions"></div>' ); |
| 1084 | + |
| 1085 | + _this.descriptionAdder = $j( '<a class="mwe-upwiz-more-options"/>' ) |
| 1086 | + .html( gM( 'mwe-upwiz-desc-add-0' ) ) |
| 1087 | + .click( function( ) { _this.addDescription(); } ); |
| 1088 | + |
| 1089 | + var descriptionAdderDiv = |
| 1090 | + $j( '<div />' ).append( |
| 1091 | + $j( '<div class="mwe-upwiz-details-fieldname" />' ), |
| 1092 | + $j( '<div class="mwe-upwiz-details-descriptions-add" />' ) |
| 1093 | + .append( _this.descriptionAdder ) |
| 1094 | + ); |
| 1095 | + |
| 1096 | + // Commons specific help for titles |
| 1097 | + // http://commons.wikimedia.org/wiki/Commons:File_naming |
| 1098 | + // http://commons.wikimedia.org/wiki/MediaWiki:Filename-prefix-blacklist |
| 1099 | + // XXX make sure they can't use ctrl characters or returns or any other bad stuff. |
| 1100 | + _this.titleId = "title" + _this.upload.index; |
| 1101 | + _this.titleInput = $j( '<textarea type="text" id="' + _this.titleId + '" name="' + _this.titleId + '" rows="1" class="mwe-title mwe-long-textarea"></textarea>' ) |
| 1102 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-title' ) ) |
| 1103 | + .tipsyPlus() |
| 1104 | + .keyup( function() { |
| 1105 | + _this.setFilenameFromTitle(); |
| 1106 | + } ) |
| 1107 | + .growTextArea() |
| 1108 | + .destinationChecked( { |
| 1109 | + spinner: function(bool) { _this.toggleDestinationBusy(bool); }, |
| 1110 | + preprocess: function( name ) { return _this.getFilenameFromTitle(); }, // XXX this is no longer a pre-process |
| 1111 | + processResult: function( result ) { _this.processDestinationCheck( result ); } |
| 1112 | + } ); |
| 1113 | + |
| 1114 | + _this.titleErrorDiv = $j('<div class="mwe-upwiz-details-input-error"><label class="mwe-error" for="' + _this.titleId + '" generated="true"/></div>'); |
| 1115 | + |
| 1116 | + var titleContainerDiv = $j('<div class="mwe-upwiz-details-fieldname-input ui-helper-clearfix"></div>') |
| 1117 | + .append( |
| 1118 | + _this.titleErrorDiv, |
| 1119 | + $j( '<div class="mwe-upwiz-details-fieldname"></div>' ) |
| 1120 | + .requiredFieldLabel() |
| 1121 | + .append( gM( 'mwe-upwiz-title' ) ), |
| 1122 | + $j( '<div class="mwe-upwiz-details-input"></div>' ).append( _this.titleInput ) |
| 1123 | + ); |
| 1124 | + |
| 1125 | + _this.deedDiv = $j( '<div class="mwe-upwiz-custom-deed" />' ); |
| 1126 | + |
| 1127 | + _this.copyrightInfoFieldset = $j('<fieldset class="mwe-fieldset mwe-upwiz-copyright-info"></fieldset>') |
| 1128 | + .hide() |
| 1129 | + .append( |
| 1130 | + $j( '<legend class="mwe-legend">' ).append( gM( 'mwe-upwiz-copyright-info' ) ), |
| 1131 | + _this.deedDiv |
| 1132 | + ); |
| 1133 | + |
| 1134 | + var $categoriesDiv = $j('<div class="mwe-upwiz-details-fieldname-input ui-helper-clearfix">' |
| 1135 | + + '<div class="mwe-upwiz-details-fieldname"></div>' |
| 1136 | + + '<div class="mwe-upwiz-details-input"></div>' |
| 1137 | + + '</div>' ); |
| 1138 | + $categoriesDiv.find( '.mwe-upwiz-details-fieldname' ).append( gM( 'mwe-upwiz-categories' ) ); |
| 1139 | + var categoriesId = 'categories' + _this.upload.index; |
| 1140 | + $categoriesDiv.find( '.mwe-upwiz-details-input' ) |
| 1141 | + .append( $j( '<input/>' ).attr( { id: categoriesId, |
| 1142 | + name: categoriesId, |
| 1143 | + type: 'text' } ) |
| 1144 | + ); |
| 1145 | + |
| 1146 | + var moreDetailsDiv = $j('<div class="mwe-more-details"></div>'); |
| 1147 | + |
| 1148 | + var moreDetailsCtrlDiv = $j( '<div class="mwe-upwiz-details-more-options"></div>' ); |
| 1149 | + |
| 1150 | + var dateInputId = "dateInput" + ( _this.upload.index ).toString(); |
| 1151 | + var dateDisplayInputId = "dateDisplayInput" + ( _this.upload.index ).toString(); |
| 1152 | + |
| 1153 | + var dateErrorDiv = $j('<div class="mwe-upwiz-details-input-error"><label class="mwe-validator-error" for="' + dateInputId + '" generated="true"/></div>'); |
| 1154 | + |
| 1155 | + /* XXX must localize this by loading jquery.ui.datepicker-XX.js where XX is a language code */ |
| 1156 | + /* jQuery.ui.datepicker also modifies first-day-of-week according to language, which is somewhat wrong. */ |
| 1157 | + /* $.datepicker.setDefaults() for other settings */ |
| 1158 | + _this.dateInput = |
| 1159 | + $j( '<input type="text" id="' + dateInputId + '" name="' + dateInputId + '" type="text" class="mwe-date" size="20"/>' ); |
| 1160 | + _this.dateDisplayInput = |
| 1161 | + $j( '<input type="text" id="' + dateDisplayInputId + '" name="' + dateDisplayInputId + '" type="text" class="mwe-date-display" size="20"/>' ); |
| 1162 | + |
| 1163 | + |
| 1164 | + var dateInputDiv = $j( '<div class="mwe-upwiz-details-fieldname-input ui-helper-clearfix"></div>' ) |
| 1165 | + .append( |
| 1166 | + dateErrorDiv, |
| 1167 | + $j( '<div class="mwe-upwiz-details-fieldname"></div>' ).append( gM( 'mwe-upwiz-date-created' ) ), |
| 1168 | + $j( '<div class="mwe-upwiz-details-input"></div>' ).append( _this.dateInput, _this.dateDisplayInput ) ); |
| 1169 | + |
| 1170 | + var otherInformationId = "otherInformation" + _this.upload.index; |
| 1171 | + _this.otherInformationInput = $j( '<textarea id="' + otherInformationId + '" name="' + otherInformationId + '" class="mwe-upwiz-other-textarea"></textarea>' ) |
| 1172 | + .growTextArea() |
| 1173 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-other' ) ) |
| 1174 | + .tipsyPlus(); |
| 1175 | + |
| 1176 | + var otherInformationDiv = $j('<div></div>') |
| 1177 | + .append( $j( '<div class="mwe-upwiz-details-more-label">' ).append( gM( 'mwe-upwiz-other' ) ) ) |
| 1178 | + .append( _this.otherInformationInput ); |
| 1179 | + |
| 1180 | + |
| 1181 | + $j( moreDetailsDiv ).append( |
| 1182 | + dateInputDiv, |
| 1183 | + // location goes here |
| 1184 | + otherInformationDiv |
| 1185 | + ); |
| 1186 | + |
| 1187 | + _this.$form = $j( '<form></form>' ); |
| 1188 | + _this.$form.append( |
| 1189 | + _this.descriptionsDiv, |
| 1190 | + descriptionAdderDiv, |
| 1191 | + titleContainerDiv, |
| 1192 | + _this.copyrightInfoFieldset, |
| 1193 | + $categoriesDiv, |
| 1194 | + moreDetailsCtrlDiv, |
| 1195 | + moreDetailsDiv |
| 1196 | + ); |
| 1197 | + |
| 1198 | + $j( _this.dataDiv ).append( |
| 1199 | + _this.$form |
| 1200 | + ); |
| 1201 | + |
| 1202 | + $j( _this.div ).append( |
| 1203 | + _this.thumbnailDiv, |
| 1204 | + _this.dataDiv |
| 1205 | + ); |
| 1206 | + |
| 1207 | + _this.$form.validate(); |
| 1208 | + _this.$form.find( '.mwe-date' ).rules( "add", { |
| 1209 | + dateISO: true, |
| 1210 | + messages: { |
| 1211 | + dateISO: gM( 'mwe-upwiz-error-date' ) |
| 1212 | + } |
| 1213 | + } ); |
| 1214 | + |
| 1215 | + // we hide the "real" ISO date, and create another "display" date |
| 1216 | + _this.$form.find( '.mwe-date-display' ) |
| 1217 | + .datepicker( { |
| 1218 | + dateFormat: 'DD, MM d, yy', |
| 1219 | + //buttonImage: mw.getMwEmbedPath() + 'skins/common/images/calendar.gif', |
| 1220 | + showOn: 'focus', |
| 1221 | + /* buttonImage: '???', |
| 1222 | + buttonImageOnly: true, */ |
| 1223 | + changeMonth: true, |
| 1224 | + changeYear: true, |
| 1225 | + showAnim: 'slideDown', |
| 1226 | + altField: '#' + dateInputId, |
| 1227 | + altFormat: 'yy-mm-dd' } ) |
| 1228 | + .click( function() { $j( this ).datepicker( 'show' ); } ) |
| 1229 | + .readonly(); |
| 1230 | + |
| 1231 | + _this.$form.find( '.mwe-date' ) |
| 1232 | + .bind( 'change', function() { $j( this ).valid(); } ) |
| 1233 | + .hide(); |
| 1234 | + |
| 1235 | + /* if the date is not valid, we need to pop open the "more options". How? |
| 1236 | + guess we'll revalidate it with element */ |
| 1237 | + |
| 1238 | + mw.UploadWizardUtil.makeToggler( moreDetailsCtrlDiv, moreDetailsDiv ); |
| 1239 | + |
| 1240 | + _this.addDescription( true, mw.UploadWizard.config[ 'userLanguage' ] ); |
| 1241 | + $j( containerDiv ).append( _this.div ); |
| 1242 | + |
| 1243 | + // make this a category picker |
| 1244 | + $categoriesDiv.find( '.mwe-upwiz-details-input' ) |
| 1245 | + .find( 'input' ) |
| 1246 | + .mwCoolCats( { buttontext: gM( 'mwe-upwiz-categories-add' ) } ); |
| 1247 | + |
| 1248 | +}; |
| 1249 | + |
| 1250 | +mw.UploadWizardDetails.prototype = { |
| 1251 | + |
| 1252 | + /** |
| 1253 | + * check entire form for validity |
| 1254 | + */ |
| 1255 | + // return boolean if we are ready to go. |
| 1256 | + // side effect: add error text to the page for fields in an incorrect state. |
| 1257 | + // we must call EVERY valid() function due to side effects; do not short-circuit. |
| 1258 | + valid: function() { |
| 1259 | + var _this = this; |
| 1260 | + // at least one description -- never mind, we are disallowing removal of first description |
| 1261 | + // all the descriptions -- check min & max length |
| 1262 | + |
| 1263 | + // the title |
| 1264 | + var titleInputValid = $j( _this.titleInput ).data( 'valid' ); |
| 1265 | + if ( typeof titleInputValid == 'undefined' ) { |
| 1266 | + alert( "please wait, still checking the title for uniqueness..." ); |
| 1267 | + return false; |
| 1268 | + } |
| 1269 | + |
| 1270 | + // all other fields validated with validator js |
| 1271 | + var formValid = _this.$form.valid(); |
| 1272 | + return titleInputValid && formValid; |
| 1273 | + |
| 1274 | + // categories are assumed valid |
| 1275 | + |
| 1276 | + // the license, if any |
| 1277 | + |
| 1278 | + // pop open the 'more-options' if the date is bad |
| 1279 | + // the date |
| 1280 | + |
| 1281 | + // location? |
| 1282 | + }, |
| 1283 | + |
| 1284 | + |
| 1285 | + |
| 1286 | + /** |
| 1287 | + * toggles whether we use the 'macro' deed or our own |
| 1288 | + */ |
| 1289 | + useCustomDeedChooser: function() { |
| 1290 | + var _this = this; |
| 1291 | + _this.copyrightInfoFieldset.show(); |
| 1292 | + _this.upload.wizardDeedChooser = _this.upload.deedChooser; |
| 1293 | + _this.upload.deedChooser = new mw.UploadWizardDeedChooser( |
| 1294 | + _this.deedDiv, |
| 1295 | + [ new mw.UploadWizardDeedOwnWork(), |
| 1296 | + new mw.UploadWizardDeedThirdParty() ] |
| 1297 | + ); |
| 1298 | + }, |
| 1299 | + |
| 1300 | + /** |
| 1301 | + * Sets the filename from the title plus this upload's extension. |
| 1302 | + */ |
| 1303 | + setFilenameFromTitle: function() { |
| 1304 | + var _this = this; |
| 1305 | + |
| 1306 | + _this.filename = wgFormattedNamespaces[wgNamespaceIds['file']] + ':' + _this.getFilenameFromTitle(); |
| 1307 | + $j( '#mwe-upwiz-details-filename' ).text( _this.filename ); |
| 1308 | + |
| 1309 | + }, |
| 1310 | + |
| 1311 | + /** |
| 1312 | + * Gets a filename from the human readable title, using upload's extension. |
| 1313 | + * @return Filename |
| 1314 | + */ |
| 1315 | + getFilenameFromTitle: function() { |
| 1316 | + var _this = this; |
| 1317 | + var name = $j( _this.titleInput ).val(); |
| 1318 | + return mw.UploadWizardUtil.pathToTitle( name ) + '.' + _this.upload.extension; |
| 1319 | + }, |
| 1320 | + |
| 1321 | + |
| 1322 | + /** |
| 1323 | + * show file destination field as "busy" while checking |
| 1324 | + * @param busy boolean true = show busy-ness, false = remove |
| 1325 | + */ |
| 1326 | + toggleDestinationBusy: function ( busy ) { |
| 1327 | + var _this = this; |
| 1328 | + if (busy) { |
| 1329 | + _this.titleInput.addClass( "busy" ); |
| 1330 | + $j( _this.titleInput ).data( 'valid', undefined ); |
| 1331 | + } else { |
| 1332 | + _this.titleInput.removeClass( "busy" ); |
| 1333 | + } |
| 1334 | + }, |
| 1335 | + |
| 1336 | + /** |
| 1337 | + * Process the result of a destination filename check. |
| 1338 | + * See mw.DestinationChecker.js for documentation of result format |
| 1339 | + * XXX would be simpler if we created all these divs in the DOM and had a more jquery-friendly way of selecting |
| 1340 | + * attrs. Instead we create & destroy whole interface each time. Won't someone think of the DOM elements? |
| 1341 | + * @param result |
| 1342 | + */ |
| 1343 | + processDestinationCheck: function( result ) { |
| 1344 | + var _this = this; |
| 1345 | + |
| 1346 | + if ( result.isUnique ) { |
| 1347 | + $j( _this.titleInput ).data( 'valid', true ); |
| 1348 | + _this.$form.find( 'label[for=' + _this.titleId + ']' ).hide().empty(); |
| 1349 | + _this.ignoreWarningsInput = undefined; |
| 1350 | + return; |
| 1351 | + } |
| 1352 | + |
| 1353 | + $j( _this.titleInput ).data( 'valid', false ); |
| 1354 | + |
| 1355 | + // result is NOT unique |
| 1356 | + var title = mw.UploadWizardUtil.fileTitleToHumanTitle( result.title ); |
| 1357 | + /* var img = result.img; |
| 1358 | + var href = result.href; */ |
| 1359 | + |
| 1360 | + _this.$form.find( 'label[for=' + _this.titleId + ']' ) |
| 1361 | + .html( gM( 'mwe-upwiz-fileexists-replace', title ) ) |
| 1362 | + .show(); |
| 1363 | + |
| 1364 | + /* temporarily commenting out the full thumbnail etc. thing. For now, we just want the user to change |
| 1365 | + to a different name |
| 1366 | + _this.ignoreWarningsInput = $j("<input />").attr( { type: 'checkbox', name: 'ignorewarnings' } ); |
| 1367 | + var $fileAlreadyExists = $j('<div />') |
| 1368 | + .append( |
| 1369 | + gM( 'mwe-upwiz-fileexists', |
| 1370 | + $j('<a />') |
| 1371 | + .attr( { target: '_new', href: href } ) |
| 1372 | + .text( title ) |
| 1373 | + ), |
| 1374 | + $j('<br />'), |
| 1375 | + _this.ignoreWarningsInput, |
| 1376 | + gM('mwe-upwiz-overwrite') |
| 1377 | + ); |
| 1378 | + |
| 1379 | + var $imageLink = $j('<a />') |
| 1380 | + .addClass( 'image' ) |
| 1381 | + .attr( { target: '_new', href: href } ) |
| 1382 | + .append( |
| 1383 | + $j( '<img />') |
| 1384 | + .addClass( 'thumbimage' ) |
| 1385 | + .attr( { |
| 1386 | + 'width' : img.thumbwidth, |
| 1387 | + 'height' : img.thumbheight, |
| 1388 | + 'border' : 0, |
| 1389 | + 'src' : img.thumburl, |
| 1390 | + 'alt' : title |
| 1391 | + } ) |
| 1392 | + ); |
| 1393 | + |
| 1394 | + var $imageCaption = $j( '<div />' ) |
| 1395 | + .addClass( 'thumbcaption' ) |
| 1396 | + .append( |
| 1397 | + $j('<div />') |
| 1398 | + .addClass( "magnify" ) |
| 1399 | + .append( |
| 1400 | + $j('<a />' ) |
| 1401 | + .addClass( 'internal' ) |
| 1402 | + .attr( { |
| 1403 | + 'title' : gM('mwe-upwiz-thumbnail-more'), |
| 1404 | + 'href' : href |
| 1405 | + } ), |
| 1406 | + |
| 1407 | + $j( '<img />' ) |
| 1408 | + .attr( { |
| 1409 | + 'border' : 0, |
| 1410 | + 'width' : 15, |
| 1411 | + 'height' : 11, |
| 1412 | + 'src' : mw.UploadWizard.config[ 'images_path' ] + 'magnify-clip.png' |
| 1413 | + } ), |
| 1414 | + |
| 1415 | + $j('<span />') |
| 1416 | + .html( gM( 'mwe-fileexists-thumb' ) ) |
| 1417 | + ) |
| 1418 | + ); |
| 1419 | + |
| 1420 | + $j( _this.titleErrorDiv ).html( |
| 1421 | + $j('<span />') // dummy argument since .html() only takes one arg |
| 1422 | + .append( |
| 1423 | + $fileAlreadyExists, |
| 1424 | + $j( '<div />' ) |
| 1425 | + .addClass( 'thumb tright' ) |
| 1426 | + .append( |
| 1427 | + $j( '<div />' ) |
| 1428 | + .addClass( 'thumbinner' ) |
| 1429 | + .css({ |
| 1430 | + 'width' : ( parseInt( img.thumbwidth ) + 2 ) + 'px;' |
| 1431 | + }) |
| 1432 | + .append( |
| 1433 | + $imageLink, |
| 1434 | + $imageCaption |
| 1435 | + ) |
| 1436 | + ) |
| 1437 | + ) |
| 1438 | + ).show(); |
| 1439 | + */ |
| 1440 | + |
| 1441 | + |
| 1442 | + }, |
| 1443 | + |
| 1444 | + /** |
| 1445 | + * Do anything related to a change in the number of descriptions |
| 1446 | + */ |
| 1447 | + recountDescriptions: function() { |
| 1448 | + var _this = this; |
| 1449 | + // if there is some maximum number of descriptions, deal with that here |
| 1450 | + $j( _this.descriptionAdder ).html( gM( 'mwe-upwiz-desc-add-' + ( _this.descriptions.length === 0 ? '0' : 'n' ) ) ); |
| 1451 | + }, |
| 1452 | + |
| 1453 | + |
| 1454 | + /** |
| 1455 | + * Add a new description |
| 1456 | + */ |
| 1457 | + addDescription: function( required, languageCode ) { |
| 1458 | + var _this = this; |
| 1459 | + if ( typeof required === 'undefined' ) { |
| 1460 | + required = false; |
| 1461 | + } |
| 1462 | + |
| 1463 | + if ( typeof languageCode === 'undefined' ) { |
| 1464 | + languageCode = mw.LanguageUpWiz.UNKNOWN; |
| 1465 | + } |
| 1466 | + |
| 1467 | + var description = new mw.UploadWizardDescription( languageCode, required ); |
| 1468 | + |
| 1469 | + if ( ! required ) { |
| 1470 | + $j( description.div ).append( |
| 1471 | + $j.fn.removeCtrl( 'mwe-upwiz-remove-description', function() { _this.removeDescription( description ); } ) |
| 1472 | + ); |
| 1473 | + } |
| 1474 | + |
| 1475 | + $j( _this.descriptionsDiv ).append( description.div ); |
| 1476 | + |
| 1477 | + // must defer adding rules until it's in a form |
| 1478 | + // sigh, this would be simpler if we refactored to be more jquery style, passing DOM element downward |
| 1479 | + description.addValidationRules( required ); |
| 1480 | + |
| 1481 | + _this.descriptions.push( description ); |
| 1482 | + _this.recountDescriptions(); |
| 1483 | + }, |
| 1484 | + |
| 1485 | + /** |
| 1486 | + * Remove a description |
| 1487 | + * @param description |
| 1488 | + */ |
| 1489 | + removeDescription: function( description ) { |
| 1490 | + var _this = this; |
| 1491 | + $j( description.div ).remove(); |
| 1492 | + mw.UploadWizardUtil.removeItem( _this.descriptions, description ); |
| 1493 | + _this.recountDescriptions(); |
| 1494 | + }, |
| 1495 | + |
| 1496 | + /** |
| 1497 | + * Display an error with details |
| 1498 | + * XXX this is a lot like upload ui's error -- should merge |
| 1499 | + */ |
| 1500 | + error: function() { |
| 1501 | + var _this = this; |
| 1502 | + var args = Array.prototype.slice.call( arguments ); // copies arguments into a real array |
| 1503 | + var msg = 'mwe-upwiz-upload-error-' + args[0]; |
| 1504 | + $j( _this.errorDiv ).append( $j( '<p class="mwe-upwiz-upload-error">' + gM( msg, args.slice( 1 ) ) + '</p>' ) ); |
| 1505 | + // apply a error style to entire did |
| 1506 | + $j( _this.div ).addClass( 'mwe-upwiz-upload-error' ); |
| 1507 | + $j( _this.dataDiv ).hide(); |
| 1508 | + $j( _this.errorDiv ).show(); |
| 1509 | + }, |
| 1510 | + |
| 1511 | + /** |
| 1512 | + * Given the API result pull some info into the form ( for instance, extracted from EXIF, desired filename ) |
| 1513 | + * @param result Upload API result object |
| 1514 | + */ |
| 1515 | + populate: function() { |
| 1516 | + var _this = this; |
| 1517 | + mw.log( "populating details from upload" ); |
| 1518 | + _this.upload.setThumbnail( _this.thumbnailDiv ); |
| 1519 | + _this.prefillDate(); |
| 1520 | + _this.prefillSource(); |
| 1521 | + _this.prefillAuthor(); |
| 1522 | + _this.prefillTitle(); |
| 1523 | + _this.prefillFilename(); |
| 1524 | + _this.prefillLocation(); |
| 1525 | + }, |
| 1526 | + |
| 1527 | + /** |
| 1528 | + * Check if we got an EXIF date back; otherwise use today's date; and enter it into the details |
| 1529 | + * XXX We ought to be using date + time here... |
| 1530 | + * EXIF examples tend to be in ISO 8601, but the separators are sometimes things like colons, and they have lots of trailing info |
| 1531 | + * (which we should actually be using, such as time and timezone) |
| 1532 | + */ |
| 1533 | + prefillDate: function() { |
| 1534 | + // XXX surely we have this function somewhere already |
| 1535 | + function pad( n ) { |
| 1536 | + return n < 10 ? "0" + n : n; |
| 1537 | + } |
| 1538 | + |
| 1539 | + var _this = this; |
| 1540 | + var yyyyMmDdRegex = /^(\d\d\d\d)[:\/-](\d\d)[:\/-](\d\d)\D.*/; |
| 1541 | + var dateObj; |
| 1542 | + var metadata = _this.upload.imageinfo.metadata; |
| 1543 | + $j.each([metadata.datetimeoriginal, metadata.datetimedigitized, metadata.datetime, metadata['date']], |
| 1544 | + function( i, imageinfoDate ) { |
| 1545 | + if ( ! mw.isEmpty( imageinfoDate ) ) { |
| 1546 | + var matches = imageinfoDate.trim().match( yyyyMmDdRegex ); |
| 1547 | + if ( ! mw.isEmpty( matches ) ) { |
| 1548 | + dateObj = new Date( parseInt( matches[1], 10 ), |
| 1549 | + parseInt( matches[2], 10 ) - 1, |
| 1550 | + parseInt( matches[3], 10 ) ); |
| 1551 | + return false; // break from $j.each |
| 1552 | + } |
| 1553 | + } |
| 1554 | + } |
| 1555 | + ); |
| 1556 | + |
| 1557 | + // if we don't have EXIF or other metadata, let's use "now" |
| 1558 | + // XXX if we have FileAPI, it might be clever to look at file attrs, saved |
| 1559 | + // in the upload object for use here later, perhaps |
| 1560 | + if (typeof dateObj === 'undefined') { |
| 1561 | + dateObj = new Date(); |
| 1562 | + } |
| 1563 | + dateStr = dateObj.getUTCFullYear() + '-' + pad( dateObj.getUTCMonth() ) + '-' + pad( dateObj.getUTCDate() ); |
| 1564 | + |
| 1565 | + // ok by now we should definitely have a dateObj and a date string |
| 1566 | + $j( _this.dateInput ).val( dateStr ); |
| 1567 | + $j( _this.dateDisplayInput ).datepicker( "setDate", dateObj ); |
| 1568 | + }, |
| 1569 | + |
| 1570 | + /** |
| 1571 | + * Set the title of the thing we just uploaded, visibly |
| 1572 | + * Note: the interface's notion of "filename" versus "title" is the opposite of MediaWiki |
| 1573 | + */ |
| 1574 | + prefillTitle: function() { |
| 1575 | + var _this = this; |
| 1576 | + var titleExt = mw.UploadWizardUtil.titleToPath( _this.upload.originalFilename ); |
| 1577 | + var title = titleExt.replace( /\.\w+$/, '' ); |
| 1578 | + $j( _this.titleInput ).val( title ); |
| 1579 | + }, |
| 1580 | + |
| 1581 | + /** |
| 1582 | + * Set the title of the thing we just uploaded, visibly |
| 1583 | + * Note: the interface's notion of "filename" versus "title" is the opposite of MediaWiki |
| 1584 | + */ |
| 1585 | + prefillFilename: function() { |
| 1586 | + var _this = this; |
| 1587 | + _this.setFilenameFromTitle(); |
| 1588 | + }, |
| 1589 | + |
| 1590 | + /** |
| 1591 | + * Prefill location inputs (and/or scroll to position on map) from image info and metadata |
| 1592 | + * |
| 1593 | + * At least for my test images, the EXIF parser on MediaWiki is not giving back any data for |
| 1594 | + * GPSLatitude, GPSLongitude, or GPSAltitudeRef. It is giving the lat/long Refs, the Altitude, and the MapDatum |
| 1595 | + * So, this is broken until we fix MediaWiki's parser, OR, parse it ourselves somehow |
| 1596 | + * |
| 1597 | + * in Image namespace |
| 1598 | + * GPSTag Long ?? |
| 1599 | + * |
| 1600 | + * in GPSInfo namespace |
| 1601 | + * GPSVersionID byte* 2000 = 2.0.0.0 |
| 1602 | + * GPSLatitude rational |
| 1603 | + * GPSLatitudeRef ascii (N | S) or North | South |
| 1604 | + * GPSLongitude rational |
| 1605 | + * GPSLongitudeRef ascii (E | W) or East | West |
| 1606 | + * GPSAltitude rational |
| 1607 | + * GPSAltitudeRef byte (0 | 1) above or below sea level |
| 1608 | + * GPSImgDirection rational |
| 1609 | + * GPSImgDirectionRef ascii (M | T) magnetic or true north |
| 1610 | + * GPSMapDatum ascii "WGS-84" is the standard |
| 1611 | + * |
| 1612 | + * A 'rational' is a string like this: |
| 1613 | + * "53/1 0/1 201867/4096" --> 53 deg 0 min 49.284 seconds |
| 1614 | + * "2/1 11/1 64639/4096" --> 2 deg 11 min 15.781 seconds |
| 1615 | + * "122/1" -- 122 m (altitude) |
| 1616 | + */ |
| 1617 | + prefillLocation: function() { |
| 1618 | + var _this = this; |
| 1619 | + var metadata = _this.upload.imageinfo.metadata; |
| 1620 | + if (metadata === undefined) { |
| 1621 | + return; |
| 1622 | + } |
| 1623 | + |
| 1624 | + |
| 1625 | + }, |
| 1626 | + |
| 1627 | + /** |
| 1628 | + * Given a decimal latitude and longitude, return filled out {{Location}} template |
| 1629 | + * @param latitude decimal latitude ( -90.0 >= n >= 90.0 ; south = negative ) |
| 1630 | + * @param longitude decimal longitude ( -180.0 >= n >= 180.0 ; west = negative ) |
| 1631 | + * @param scale (optional) how rough the geocoding is. |
| 1632 | + * @param heading (optional) what direction the camera is pointing in. (decimal 0.0-360.0, 0 = north, 90 = E) |
| 1633 | + * @return string with WikiText which will geotag this record |
| 1634 | + */ |
| 1635 | + coordsToWikiText: function(latitude, longitude, scale, heading) { |
| 1636 | + //Wikipedia |
| 1637 | + //http://en.wikipedia.org/wiki/Wikipedia:WikiProject_Geographical_coordinates#Parameters |
| 1638 | + // http://en.wikipedia.org/wiki/Template:Coord |
| 1639 | + //{{coord|61.1631|-149.9721|type:landmark_globe:earth_region:US-AK_scale:150000_source:gnis|name=Kulis Air National Guard Base}} |
| 1640 | + |
| 1641 | + //Wikimedia Commons |
| 1642 | + //{{Coor dms|41|19|20.4|N|19|38|36.7|E}} |
| 1643 | + //{{Location}} |
| 1644 | + |
| 1645 | + }, |
| 1646 | + |
| 1647 | + /** |
| 1648 | + * If there is a way to figure out source from image info, do so here |
| 1649 | + * XXX user pref? |
| 1650 | + */ |
| 1651 | + prefillSource: function() { |
| 1652 | + // we have no way to do this AFAICT |
| 1653 | + }, |
| 1654 | + |
| 1655 | + /** |
| 1656 | + * Prefill author (such as can be determined) from image info and metadata |
| 1657 | + * XXX user pref? |
| 1658 | + */ |
| 1659 | + prefillAuthor: function() { |
| 1660 | + var _this = this; |
| 1661 | + if (_this.upload.imageinfo.metadata.author !== undefined) { |
| 1662 | + $j( _this.authorInput ).val( _this.upload.imageinfo.metadata.author ); |
| 1663 | + } |
| 1664 | + |
| 1665 | + }, |
| 1666 | + |
| 1667 | + /** |
| 1668 | + * Prefill license (such as can be determined) from image info and metadata |
| 1669 | + * XXX user pref? |
| 1670 | + */ |
| 1671 | + prefillLicense: function() { |
| 1672 | + var _this = this; |
| 1673 | + var copyright = _this.upload.imageinfo.metadata.copyright; |
| 1674 | + if (copyright !== undefined) { |
| 1675 | + if (copyright.match(/\bcc-by-sa\b/i)) { |
| 1676 | + alert("unimplemented cc-by-sa in prefillLicense"); |
| 1677 | + // XXX set license to be that CC-BY-SA |
| 1678 | + } else if (copyright.match(/\bcc-by\b/i)) { |
| 1679 | + alert("unimplemented cc-by in prefillLicense"); |
| 1680 | + // XXX set license to be that |
| 1681 | + } else if (copyright.match(/\bcc-zero\b/i)) { |
| 1682 | + alert("unimplemented cc-zero in prefillLicense"); |
| 1683 | + // XXX set license to be that |
| 1684 | + // XXX any other licenses we could guess from copyright statement |
| 1685 | + } else { |
| 1686 | + $j( _this.licenseInput ).val( copyright ); |
| 1687 | + } |
| 1688 | + } |
| 1689 | + // if we still haven't set a copyright use the user's preferences |
| 1690 | + }, |
| 1691 | + |
| 1692 | + |
| 1693 | + /** |
| 1694 | + * |
| 1695 | + showErrors: function() { |
| 1696 | + var _this = this; |
| 1697 | + $j.each( _this.errors, function() { |
| 1698 | + |
| 1699 | + } ); |
| 1700 | + }, |
| 1701 | + */ |
| 1702 | + |
| 1703 | + /** |
| 1704 | + * Convert entire details for this file into wikiText, which will then be posted to the file |
| 1705 | + * XXX there is a WikiText sanitizer in use on UploadForm -- use that here, or port it |
| 1706 | + * @return wikitext representing all details |
| 1707 | + */ |
| 1708 | + getWikiText: function() { |
| 1709 | + var _this = this; |
| 1710 | + |
| 1711 | + // if invalid, should produce side effects in the form |
| 1712 | + // instructing user to fix. |
| 1713 | + if ( ! _this.valid() ) { |
| 1714 | + return null; |
| 1715 | + } |
| 1716 | + |
| 1717 | + wikiText = ''; |
| 1718 | + |
| 1719 | + |
| 1720 | + // http://commons.wikimedia.org / wiki / Template:Information |
| 1721 | + |
| 1722 | + // can we be more slick and do this with maps, applys, joins? |
| 1723 | + var information = { |
| 1724 | + 'description' : '', // {{lang|description in lang}}* required |
| 1725 | + 'date' : '', // YYYY, YYYY-MM, or YYYY-MM-DD required - use jquery but allow editing, then double check for sane date. |
| 1726 | + 'source' : '', // {{own}} or wikitext optional |
| 1727 | + 'author' : '', // any wikitext, but particularly {{Creator:Name Surname}} required |
| 1728 | + 'permission' : '', // leave blank unless OTRS pending; by default will be "see below" optional |
| 1729 | + 'other_versions' : '', // pipe separated list, other versions optional |
| 1730 | + 'other_fields' : '' // ??? additional table fields |
| 1731 | + }; |
| 1732 | + |
| 1733 | + // sanity check the descriptions -- do not have two in the same lang |
| 1734 | + // all should be a known lang |
| 1735 | + if ( _this.descriptions.length === 0 ) { |
| 1736 | + alert("something has gone horribly wrong, unimplemented error check for zero descriptions"); |
| 1737 | + // XXX ruh roh |
| 1738 | + // we should not even allow them to press the button ( ? ) but then what about the queue... |
| 1739 | + } |
| 1740 | + $j.each( _this.descriptions, function( i, desc ) { |
| 1741 | + information['description'] += desc.getWikiText(); |
| 1742 | + } ); |
| 1743 | + |
| 1744 | + // XXX add a sanity check here for good date |
| 1745 | + information['date'] = $j( _this.dateInput ).val().trim(); |
| 1746 | + |
| 1747 | + var deed = _this.upload.deedChooser.deed; |
| 1748 | + |
| 1749 | + information['source'] = deed.getSourceWikiText(); |
| 1750 | + |
| 1751 | + information['author'] = deed.getAuthorWikiText(); |
| 1752 | + |
| 1753 | + var info = ''; |
| 1754 | + for ( var key in information ) { |
| 1755 | + info += '|' + key + '=' + information[key] + "\n"; |
| 1756 | + } |
| 1757 | + |
| 1758 | + wikiText += "=={{int:filedesc}}==\n"; |
| 1759 | + |
| 1760 | + wikiText += '{{Information\n' + info + '}}\n'; |
| 1761 | + |
| 1762 | + // add a location template if possible |
| 1763 | + |
| 1764 | + // add an "anything else" template if needed |
| 1765 | + var otherInfoWikiText = $j( _this.otherInformationInput ).val().trim(); |
| 1766 | + if ( ! mw.isEmpty( otherInfoWikiText ) ) { |
| 1767 | + wikiText += "=={{int:otherinfo}}==\n"; |
| 1768 | + wikiText += otherInfoWikiText; |
| 1769 | + } |
| 1770 | + |
| 1771 | + wikiText += "=={{int:license-header}}==\n"; |
| 1772 | + |
| 1773 | + // in the other implementations, category text follows immediately after license text. This helps |
| 1774 | + // group categories together, maybe? |
| 1775 | + wikiText += deed.getLicenseWikiText() + _this.div.find( '.categoryInput' ).get(0).getWikiText(); |
| 1776 | + |
| 1777 | + |
| 1778 | + return wikiText; |
| 1779 | + }, |
| 1780 | + |
| 1781 | + /** |
| 1782 | + * Check if we are ready to post wikitext |
| 1783 | + deedValid: function() { |
| 1784 | + var _this = this; |
| 1785 | + return _this.upload.deedChooser.deed.valid(); |
| 1786 | + |
| 1787 | + // somehow, all the various issues discovered with this upload should be present in a single place |
| 1788 | + // where we can then check on |
| 1789 | + // perhaps as simple as _this.issues or _this.agenda |
| 1790 | + }, |
| 1791 | + */ |
| 1792 | + |
| 1793 | + /** |
| 1794 | + * Post wikitext as edited here, to the file |
| 1795 | + * XXX This should be split up -- one part should get wikitext from the interface here, and the ajax call |
| 1796 | + * should be be part of upload |
| 1797 | + */ |
| 1798 | + submit: function( endCallback ) { |
| 1799 | + var _this = this; |
| 1800 | + |
| 1801 | + |
| 1802 | + // are we okay to submit? |
| 1803 | + // all necessary fields are ready |
| 1804 | + // check descriptions |
| 1805 | + // the filename is in a sane state |
| 1806 | + var desiredFilename = _this.filename; |
| 1807 | + shouldRename = ( desiredFilename != _this.upload.title ); |
| 1808 | + |
| 1809 | + // XXX check state of details for okayness ( license selected, at least one desc, sane filename ) |
| 1810 | + var wikiText = _this.getWikiText(); |
| 1811 | + mw.log( wikiText ); |
| 1812 | + |
| 1813 | + var params = { |
| 1814 | + action: 'edit', |
| 1815 | + // XXX this is problematic, if the upload wizard is idle for a long time the token expires. |
| 1816 | + // should obtain token just before uploading |
| 1817 | + token: mw.UploadWizard.config[ 'token' ], |
| 1818 | + title: _this.upload.title, |
| 1819 | + // section: 0, ?? causing issues? |
| 1820 | + text: wikiText, |
| 1821 | + summary: "User edited page with " + mw.UploadWizard.userAgent, |
| 1822 | + // notminor: 1, |
| 1823 | + // basetimestamp: _this.upload.imageinfo.timestamp, ( conflicts? ) |
| 1824 | + nocreate: 1 |
| 1825 | + }; |
| 1826 | + |
| 1827 | + var finalCallback = function() { |
| 1828 | + endCallback(); |
| 1829 | + _this.completeDetailsSubmission(); |
| 1830 | + }; |
| 1831 | + |
| 1832 | + mw.log( "editing!" ); |
| 1833 | + mw.log( params ); |
| 1834 | + var callback = function( result ) { |
| 1835 | + mw.log( result ); |
| 1836 | + mw.log( "successful edit" ); |
| 1837 | + if ( shouldRename ) { |
| 1838 | + _this.rename( desiredFilename, finalCallback ); |
| 1839 | + } else { |
| 1840 | + finalCallback(); |
| 1841 | + } |
| 1842 | + }; |
| 1843 | + |
| 1844 | + _this.upload.state = 'submitting-details'; |
| 1845 | + mw.getJSON( params, callback ); |
| 1846 | + }, |
| 1847 | + |
| 1848 | + /** |
| 1849 | + * Rename the file |
| 1850 | + * |
| 1851 | + * THIS MAY NOT WORK ON ALL WIKIS. for instance, on Commons, it may be that only admins can move pages. This is another example of how |
| 1852 | + * we need an "incomplete" upload status |
| 1853 | + * we are presuming this File page is brand new, so let's not bother with the whole redirection deal. ('noredirect') |
| 1854 | + * |
| 1855 | + * use _this.ignoreWarningsInput (if it exists) to check if we can blithely move the file or if we have a problem if there |
| 1856 | + * is a file by that name already there |
| 1857 | + * |
| 1858 | + * @param filename to rename this file to |
| 1859 | + */ |
| 1860 | + rename: function( title, endCallback ) { |
| 1861 | + var _this = this; |
| 1862 | + mw.log("renaming!"); |
| 1863 | + params = { |
| 1864 | + action: 'move', |
| 1865 | + from: _this.upload.title, |
| 1866 | + to: title, |
| 1867 | + reason: "User edited page with " + mw.UploadWizard.userAgent, |
| 1868 | + movetalk: '', |
| 1869 | + noredirect: '', // presume it's too new |
| 1870 | + token: mw.UploadWizard.config[ 'token' ] |
| 1871 | + }; |
| 1872 | + mw.log(params); |
| 1873 | + // despite the name, getJSON magically changes this into a POST request (it has a list of methods and what they require). |
| 1874 | + mw.getJSON( params, function( data ) { |
| 1875 | + // handle errors later |
| 1876 | + // possible error data: { code = 'missingtitle' } -- orig filename not there |
| 1877 | + // and many more |
| 1878 | + |
| 1879 | + // which should match our request. |
| 1880 | + // we should update the current upload filename |
| 1881 | + // then call the uploadwizard with our progress |
| 1882 | + |
| 1883 | + // success is |
| 1884 | + // move = from : ..., reason : ..., redirectcreated : ..., to : .... |
| 1885 | + if (data !== undefined && data.move !== undefined && data.move.to !== undefined) { |
| 1886 | + _this.upload.title = data.move.to; |
| 1887 | + _this.refreshImageInfo( _this.upload, _this.upload.title, endCallback ); |
| 1888 | + } |
| 1889 | + } ); |
| 1890 | + }, |
| 1891 | + |
| 1892 | + /** |
| 1893 | + * Get new image info, for instance, after we renamed an image |
| 1894 | + * |
| 1895 | + * @param upload an UploadWizardUpload object |
| 1896 | + * @param title title to look up remotely |
| 1897 | + * @param endCallback execute upon completion |
| 1898 | + */ |
| 1899 | + refreshImageInfo: function( upload, title, endCallback ) { |
| 1900 | + var params = { |
| 1901 | + 'titles': title, |
| 1902 | + 'prop': 'imageinfo', |
| 1903 | + 'iiprop': 'timestamp|url|user|size|sha1|mime|metadata' |
| 1904 | + }; |
| 1905 | + // XXX timeout callback? |
| 1906 | + mw.getJSON( params, function( data ) { |
| 1907 | + if ( data && data.query && data.query.pages ) { |
| 1908 | + if ( ! data.query.pages[-1] ) { |
| 1909 | + for ( var page_id in data.query.pages ) { |
| 1910 | + var page = data.query.pages[ page_id ]; |
| 1911 | + if ( ! page.imageinfo ) { |
| 1912 | + alert("unimplemented error check, missing imageinfo"); |
| 1913 | + // XXX not found? error |
| 1914 | + } else { |
| 1915 | + upload.extractImageInfo( page.imageinfo[0] ); |
| 1916 | + } |
| 1917 | + } |
| 1918 | + } |
| 1919 | + } |
| 1920 | + endCallback(); |
| 1921 | + } ); |
| 1922 | + }, |
| 1923 | + |
| 1924 | + completeDetailsSubmission: function() { |
| 1925 | + var _this = this; |
| 1926 | + _this.upload.state = 'complete'; |
| 1927 | + // de-spinnerize |
| 1928 | + _this.upload.detailsProgress = 1.0; |
| 1929 | + }, |
| 1930 | + |
| 1931 | + dateInputCount: 0 |
| 1932 | + |
| 1933 | + |
| 1934 | +}; |
| 1935 | + |
| 1936 | + |
| 1937 | +/** |
| 1938 | + * Object that reperesents the entire multi-step Upload Wizard |
| 1939 | + */ |
| 1940 | +mw.UploadWizard = function( config ) { |
| 1941 | + |
| 1942 | + this.uploads = []; |
| 1943 | + |
| 1944 | + // making a sort of global for now, should be done by passing in config or fragments of config when needed |
| 1945 | + // elsewhere |
| 1946 | + mw.UploadWizard.config = config; |
| 1947 | + |
| 1948 | + // XXX need a robust way of defining default config |
| 1949 | + this.maxUploads = mw.UploadWizard.config[ 'maxUploads' ] || 10; |
| 1950 | + this.maxSimultaneousConnections = mw.UploadWizard.config[ 'maxSimultaneousConnections' ] || 2; |
| 1951 | + |
| 1952 | +}; |
| 1953 | + |
| 1954 | + |
| 1955 | +mw.UploadWizard.userAgent = "UploadWizard (alpha)"; |
| 1956 | + |
| 1957 | + |
| 1958 | +mw.UploadWizard.prototype = { |
| 1959 | + stepNames: [ 'file', 'deeds', 'details', 'thanks' ], |
| 1960 | + currentStepName: undefined, |
| 1961 | + |
| 1962 | + /* |
| 1963 | + // list possible upload handlers in order of preference |
| 1964 | + // these should all be in the mw.* namespace |
| 1965 | + // hardcoded for now. maybe some registry system might work later, like, all |
| 1966 | + // things which subclass off of UploadHandler |
| 1967 | + uploadHandlers: [ |
| 1968 | + 'FirefoggUploadHandler', |
| 1969 | + 'XhrUploadHandler', |
| 1970 | + 'ApiIframeUploadHandler', |
| 1971 | + 'SimpleUploadHandler', |
| 1972 | + 'NullUploadHandler' |
| 1973 | + ], |
| 1974 | + |
| 1975 | + * We can use various UploadHandlers based on the browser's capabilities. Let's pick one. |
| 1976 | + * For example, the ApiUploadHandler should work just about everywhere, but XhrUploadHandler |
| 1977 | + * allows for more fine-grained upload progress |
| 1978 | + * @return valid JS upload handler class constructor function |
| 1979 | + getUploadHandlerClass: function() { |
| 1980 | + // return mw.MockUploadHandler; |
| 1981 | + return mw.ApiUploadHandler; |
| 1982 | + var _this = this; |
| 1983 | + for ( var i = 0; i < uploadHandlers.length; i++ ) { |
| 1984 | + var klass = mw[uploadHandlers[i]]; |
| 1985 | + if ( klass != undefined && klass.canRun( this.config )) { |
| 1986 | + return klass; |
| 1987 | + } |
| 1988 | + } |
| 1989 | + // this should never happen; NullUploadHandler should always work |
| 1990 | + return null; |
| 1991 | + }, |
| 1992 | + */ |
| 1993 | + |
| 1994 | + /** |
| 1995 | + * Reset the entire interface so we can upload more stuff |
| 1996 | + * Depending on whether we split uploading / detailing, it may actually always be as simple as loading a URL |
| 1997 | + */ |
| 1998 | + reset: function() { |
| 1999 | + window.location.reload(); |
| 2000 | + }, |
| 2001 | + |
| 2002 | + |
| 2003 | + /** |
| 2004 | + * create the basic interface to make an upload in this div |
| 2005 | + * @param div The div in the DOM to put all of this into. |
| 2006 | + */ |
| 2007 | + createInterface: function( selector ) { |
| 2008 | + var _this = this; |
| 2009 | + var div = $j( selector ).get(0); |
| 2010 | + div.innerHTML = |
| 2011 | + // the arrow steps |
| 2012 | + '<ul id="mwe-upwiz-steps">' |
| 2013 | + + '<li id="mwe-upwiz-step-file"><div>' + gM('mwe-upwiz-step-file') + '</div></li>' |
| 2014 | + + '<li id="mwe-upwiz-step-deeds"><div>' + gM('mwe-upwiz-step-deeds') + '</div></li>' |
| 2015 | + + '<li id="mwe-upwiz-step-details"><div>' + gM('mwe-upwiz-step-details') + '</div></li>' |
| 2016 | + + '<li id="mwe-upwiz-step-thanks"><div>' + gM('mwe-upwiz-step-thanks') + '</div></li>' |
| 2017 | + + '</ul>' |
| 2018 | + |
| 2019 | + // the individual steps, all at once |
| 2020 | + + '<div id="mwe-upwiz-content">' |
| 2021 | + |
| 2022 | + + '<div class="mwe-upwiz-stepdiv ui-helper-clearfix" id="mwe-upwiz-stepdiv-file">' |
| 2023 | + + '<div id="mwe-upwiz-intro">' + gM('mwe-upwiz-intro') + '</div>' |
| 2024 | + + '<div id="mwe-upwiz-files">' |
| 2025 | + + '<div id="mwe-upwiz-upload-ctrls" class="mwe-upwiz-file">' |
| 2026 | + + '<div id="mwe-upwiz-add-file-container" class="mwe-upwiz-add-files-0">' |
| 2027 | + + '<a id="mwe-upwiz-add-file">' + gM("mwe-upwiz-add-file-0") + '</a>' |
| 2028 | + + '</div>' |
| 2029 | + + '</div>' |
| 2030 | + + '<div id="mwe-upwiz-progress" class="ui-helper-clearfix"></div>' |
| 2031 | + + '</div>' |
| 2032 | + + '<div class="mwe-upwiz-buttons" style="display: none"/>' |
| 2033 | + + '<button class="mwe-upwiz-button-next" />' |
| 2034 | + + '</div>' |
| 2035 | + + '</div>' |
| 2036 | + |
| 2037 | + + '<div class="mwe-upwiz-stepdiv" id="mwe-upwiz-stepdiv-deeds">' |
| 2038 | + + '<div id="mwe-upwiz-deeds-intro"></div>' |
| 2039 | + + '<div id="mwe-upwiz-deeds-thumbnails" class="ui-helper-clearfix"></div>' |
| 2040 | + + '<div id="mwe-upwiz-deeds" class="ui-helper-clearfix"></div>' |
| 2041 | + + '<div id="mwe-upwiz-deeds-custom" class="ui-helper-clearfix"></div>' |
| 2042 | + + '<div class="mwe-upwiz-buttons"/>' |
| 2043 | + + '<button class="mwe-upwiz-button-next" />' |
| 2044 | + + '</div>' |
| 2045 | + + '</div>' |
| 2046 | + |
| 2047 | + + '<div class="mwe-upwiz-stepdiv" id="mwe-upwiz-stepdiv-details">' |
| 2048 | + + '<div id="mwe-upwiz-macro">' |
| 2049 | + + '<div id="mwe-upwiz-macro-progress" class="ui-helper-clearfix"></div>' |
| 2050 | + + '<div id="mwe-upwiz-macro-choice">' |
| 2051 | + + '<div>' + gM( 'mwe-upwiz-details-intro' ) + '</div>' |
| 2052 | + + '</div>' |
| 2053 | + + '<div id="mwe-upwiz-macro-files"></div>' |
| 2054 | + + '</div>' |
| 2055 | + + '<div class="mwe-upwiz-buttons"/>' |
| 2056 | + + '<button class="mwe-upwiz-button-next" />' |
| 2057 | + + '</div>' |
| 2058 | + + '</div>' |
| 2059 | + |
| 2060 | + + '<div class="mwe-upwiz-stepdiv" id="mwe-upwiz-stepdiv-thanks">' |
| 2061 | + + '<div id="mwe-upwiz-thanks"></div>' |
| 2062 | + + '<div class="mwe-upwiz-buttons"/>' |
| 2063 | + + '<button class="mwe-upwiz-button-begin"></button>' |
| 2064 | + + '<br/><button class="mwe-upwiz-button-home"></button>' |
| 2065 | + + '</div>' |
| 2066 | + + '</div>' |
| 2067 | + |
| 2068 | + + '</div>' |
| 2069 | + |
| 2070 | + + '<div class="mwe-upwiz-clearing"></div>'; |
| 2071 | + |
| 2072 | + $j( '#mwe-upwiz-steps' ) |
| 2073 | + .addClass( 'ui-helper-clearfix ui-state-default ui-widget ui-helper-reset ui-helper-clearfix' ) |
| 2074 | + .arrowSteps(); |
| 2075 | + |
| 2076 | + $j( '.mwe-upwiz-button-home' ) |
| 2077 | + .append( gM( 'mwe-upwiz-home' ) ) |
| 2078 | + .click( function() { window.location.href = '/'; } ); |
| 2079 | + |
| 2080 | + $j( '.mwe-upwiz-button-begin' ) |
| 2081 | + .append( gM( 'mwe-upwiz-upload-another' ) ) |
| 2082 | + .click( function() { _this.reset(); } ); |
| 2083 | + |
| 2084 | + |
| 2085 | + |
| 2086 | + // handler for next button |
| 2087 | + $j( '#mwe-upwiz-stepdiv-file .mwe-upwiz-button-next') |
| 2088 | + .append( gM( 'mwe-upwiz-next-file' ) ) |
| 2089 | + .click( function() { |
| 2090 | + // check if there is an upload at all |
| 2091 | + if ( _this.uploads.length === 0 ) { |
| 2092 | + alert( gM( 'mwe-upwiz-file-need-file' ) ); |
| 2093 | + return; |
| 2094 | + } |
| 2095 | + |
| 2096 | + _this.removeEmptyUploads(); |
| 2097 | + _this.startUploads( function() { |
| 2098 | + |
| 2099 | + // okay all uploads are done, we're ready to go to the next step |
| 2100 | + |
| 2101 | + // do some last minute prep before advancing to the DEEDS page |
| 2102 | + |
| 2103 | + // these deeds are standard |
| 2104 | + var deeds = [ |
| 2105 | + new mw.UploadWizardDeedOwnWork( _this.uploads.length ), |
| 2106 | + new mw.UploadWizardDeedThirdParty( _this.uploads.length ) |
| 2107 | + ]; |
| 2108 | + |
| 2109 | + // if we have multiple uploads, also give them the option to set |
| 2110 | + // licenses individually |
| 2111 | + if ( _this.uploads.length > 1 ) { |
| 2112 | + var customDeed = $j.extend( new mw.UploadWizardDeed(), { |
| 2113 | + valid: function() { return true; }, |
| 2114 | + name: 'custom' |
| 2115 | + } ); |
| 2116 | + deeds.push( customDeed ); |
| 2117 | + } |
| 2118 | + |
| 2119 | + _this.deedChooser = new mw.UploadWizardDeedChooser( |
| 2120 | + '#mwe-upwiz-deeds', |
| 2121 | + deeds, |
| 2122 | + _this.uploads.length ); |
| 2123 | + |
| 2124 | + $j( '<div>' ).html( gM( 'mwe-upwiz-deeds-macro-prompt', _this.uploads.length ) ) |
| 2125 | + .insertBefore ( _this.deedChooser.$selector.find( '.mwe-upwiz-deed-ownwork' ) ); |
| 2126 | + |
| 2127 | + if ( _this.uploads.length > 1 ) { |
| 2128 | + $j( '<div style="margin-top: 1em">' ).html( gM( 'mwe-upwiz-deeds-custom-prompt' ) ) |
| 2129 | + .insertBefore( _this.deedChooser.$selector.find( '.mwe-upwiz-deed-custom' ) ); |
| 2130 | + } |
| 2131 | + |
| 2132 | + |
| 2133 | + _this.moveToStep( 'deeds' ); |
| 2134 | + |
| 2135 | + } ); |
| 2136 | + } ); |
| 2137 | + |
| 2138 | + |
| 2139 | + // DEEDS div |
| 2140 | + |
| 2141 | + $j( '#mwe-upwiz-deeds-intro' ).html( gM( 'mwe-upwiz-deeds-intro' ) ); |
| 2142 | + |
| 2143 | + $j( '#mwe-upwiz-stepdiv-deeds .mwe-upwiz-button-next') |
| 2144 | + .append( gM( 'mwe-upwiz-next-deeds' ) ) |
| 2145 | + .click( function() { |
| 2146 | + // validate has the side effect of notifying the user of problems, or removing existing notifications. |
| 2147 | + // if returns false, you can assume there are notifications in the interface. |
| 2148 | + if ( _this.deedChooser.valid() ) { |
| 2149 | + |
| 2150 | + var lastUploadIndex = _this.uploads.length - 1; |
| 2151 | + |
| 2152 | + $j.each( _this.uploads, function( i, upload ) { |
| 2153 | + |
| 2154 | + if ( _this.deedChooser.deed.name == 'custom' ) { |
| 2155 | + upload.details.useCustomDeedChooser(); |
| 2156 | + } else { |
| 2157 | + upload.deedChooser = _this.deedChooser; |
| 2158 | + } |
| 2159 | + |
| 2160 | + /* put a border below every details div except the last */ |
| 2161 | + if ( i < lastUploadIndex ) { |
| 2162 | + upload.details.div.css( 'border-bottom', '1px solid #e0e0e0' ); |
| 2163 | + } |
| 2164 | + |
| 2165 | + upload.details.titleInput.checkUnique(); |
| 2166 | + } ); |
| 2167 | + |
| 2168 | + _this.moveToStep( 'details' ); |
| 2169 | + } |
| 2170 | + } ); |
| 2171 | + |
| 2172 | + |
| 2173 | + // DETAILS div |
| 2174 | + |
| 2175 | + $j( '#mwe-upwiz-stepdiv-details .mwe-upwiz-button-next' ) |
| 2176 | + .append( gM( 'mwe-upwiz-next-details' ) ) |
| 2177 | + .click( function() { |
| 2178 | + if ( _this.detailsValid() ) { |
| 2179 | + _this.detailsSubmit( function() { |
| 2180 | + _this.prefillThanksPage(); |
| 2181 | + _this.moveToStep( 'thanks' ); |
| 2182 | + } ); |
| 2183 | + } |
| 2184 | + } ); |
| 2185 | + |
| 2186 | + |
| 2187 | + |
| 2188 | + // WIZARD |
| 2189 | + |
| 2190 | + // add one upload field to start (this is the big one that asks you to upload something) |
| 2191 | + var upload = _this.newUpload( '#mwe-upwiz-add-file' ); |
| 2192 | + |
| 2193 | + // "select" the first step - highlight, make it visible, hide all others |
| 2194 | + _this.moveToStep( 'file', function() { |
| 2195 | + // XXX moving the file input doesn't seem to work at this point; we get its old position before |
| 2196 | + // CSS is applied. Hence, using a timeout. |
| 2197 | + // XXX using a timeout is lame, are there other options? |
| 2198 | + // XXX Trevor suggests that using addClass() may queue stuff unnecessarily; use 'concrete' HTML |
| 2199 | + setTimeout( function() { |
| 2200 | + upload.ui.moveFileInputToCover( '#mwe-upwiz-add-file' ); |
| 2201 | + }, 300 ); |
| 2202 | + } ); |
| 2203 | + |
| 2204 | + }, |
| 2205 | + |
| 2206 | + /** |
| 2207 | + * Advance one "step" in the wizard interface. |
| 2208 | + * It is assumed that the previous step to the current one was selected. |
| 2209 | + * We do not hide the tabs because this messes up certain calculations we'd like to make about dimensions, while elements are not |
| 2210 | + * on screen. So instead we make the tabs zero height and, in CSS, they are already overflow hidden |
| 2211 | + * @param selectedStepName |
| 2212 | + * @param callback to do after layout is ready? |
| 2213 | + */ |
| 2214 | + moveToStep: function( selectedStepName, callback ) { |
| 2215 | + var _this = this; |
| 2216 | + $j.each( _this.stepNames, function(i, stepName) { |
| 2217 | + |
| 2218 | + // the step indicator |
| 2219 | + var step = $j( '#mwe-upwiz-step-' + stepName ); |
| 2220 | + |
| 2221 | + // the step's contents |
| 2222 | + var stepDiv = $j( '#mwe-upwiz-stepdiv-' + stepName ); |
| 2223 | + |
| 2224 | + if ( _this.currentStepName == stepName ) { |
| 2225 | + stepDiv.hide(); |
| 2226 | + // we hide the old stepDivs because we are afraid of some z-index elements that may interfere with later tabs |
| 2227 | + // this will break if we ever allow people to page back and forth. |
| 2228 | + } else { |
| 2229 | + if ( selectedStepName == stepName ) { |
| 2230 | + stepDiv.maskSafeShow(); |
| 2231 | + } else { |
| 2232 | + stepDiv.maskSafeHide( 1000 ); |
| 2233 | + } |
| 2234 | + } |
| 2235 | + |
| 2236 | + } ); |
| 2237 | + |
| 2238 | + $j( '#mwe-upwiz-steps' ).arrowStepsHighlight( '#mwe-upwiz-step-' + selectedStepName ); |
| 2239 | + |
| 2240 | + _this.currentStepName = selectedStepName; |
| 2241 | + |
| 2242 | + $j.each( _this.uploads, function(i, upload) { |
| 2243 | + upload.state = selectedStepName; |
| 2244 | + } ); |
| 2245 | + |
| 2246 | + if ( callback ) { |
| 2247 | + callback(); |
| 2248 | + } |
| 2249 | + }, |
| 2250 | + |
| 2251 | + /** |
| 2252 | + * add an Upload |
| 2253 | + * we create the upload interface, a handler to transport it to the server, |
| 2254 | + * and UI for the upload itself and the "details" at the second step of the wizard. |
| 2255 | + * we don't yet add it to the list of uploads; that only happens when it gets a real file. |
| 2256 | + * @return the new upload |
| 2257 | + */ |
| 2258 | + newUpload: function() { |
| 2259 | + var _this = this; |
| 2260 | + if ( _this.uploads.length == _this.maxUploads ) { |
| 2261 | + return false; |
| 2262 | + } |
| 2263 | + |
| 2264 | + var upload = new mw.UploadWizardUpload( _this, '#mwe-upwiz-files' ); |
| 2265 | + _this.uploadToAdd = upload; |
| 2266 | + |
| 2267 | + upload.ui.moveFileInputToCover( '#mwe-upwiz-add-file' ); |
| 2268 | + // we bind to the ui div since unbind doesn't work for non-DOM objects |
| 2269 | + |
| 2270 | + $j( upload.ui.div ).bind( 'filenameAccepted', function(e) { _this.updateFileCounts(); e.stopPropagation(); } ); |
| 2271 | + $j( upload.ui.div ).bind( 'removeUploadEvent', function(e) { _this.removeUpload( upload ); e.stopPropagation(); } ); |
| 2272 | + $j( upload.ui.div ).bind( 'filled', function(e) { |
| 2273 | + mw.log( "filled! received!" ); |
| 2274 | + _this.newUpload(); |
| 2275 | + mw.log( "filled! new upload!" ); |
| 2276 | + _this.setUploadFilled(upload); |
| 2277 | + mw.log( "filled! set upload filled!" ); |
| 2278 | + e.stopPropagation(); |
| 2279 | + mw.log( "filled! stop propagation!" ); |
| 2280 | + } ); |
| 2281 | + // XXX bind to some error state |
| 2282 | + |
| 2283 | + |
| 2284 | + return upload; |
| 2285 | + }, |
| 2286 | + |
| 2287 | + /** |
| 2288 | + * When an upload is filled with a real file, accept it in the wizard's list of uploads |
| 2289 | + * and set up some other interfaces |
| 2290 | + * @param UploadWizardUpload |
| 2291 | + */ |
| 2292 | + setUploadFilled: function( upload ) { |
| 2293 | + var _this = this; |
| 2294 | + |
| 2295 | + // XXX check if it has a file? |
| 2296 | + _this.uploads.push( upload ); |
| 2297 | + |
| 2298 | + /* useful for making ids unique and so on */ |
| 2299 | + _this.uploadsSeen++; |
| 2300 | + upload.index = _this.uploadsSeen; |
| 2301 | + |
| 2302 | + _this.updateFileCounts(); |
| 2303 | + |
| 2304 | + upload.deedPreview = new mw.UploadWizardDeedPreview( upload ); |
| 2305 | + |
| 2306 | + // XXX do we really need to do this now? some things will even change after step 2. |
| 2307 | + // legacy. |
| 2308 | + // set up details |
| 2309 | + upload.details = new mw.UploadWizardDetails( upload, $j( '#mwe-upwiz-macro-files' ) ); |
| 2310 | + }, |
| 2311 | + |
| 2312 | + /* increments with every upload */ |
| 2313 | + uploadsSeen: 0, |
| 2314 | + |
| 2315 | + /** |
| 2316 | + * Remove an upload from our array of uploads, and the HTML UI |
| 2317 | + * We can remove the HTML UI directly, as jquery will just get the parent. |
| 2318 | + * We need to grep through the array of uploads, since we don't know the current index. |
| 2319 | + * We need to update file counts for obvious reasons. |
| 2320 | + * |
| 2321 | + * @param upload |
| 2322 | + */ |
| 2323 | + removeUpload: function( upload ) { |
| 2324 | + var _this = this; |
| 2325 | + // remove the div that passed along the trigger |
| 2326 | + var $div = $j( upload.ui.div ); |
| 2327 | + $div.unbind(); // everything |
| 2328 | + // sexily fade away |
| 2329 | + $div.fadeOut('fast', function() { |
| 2330 | + $div.remove(); |
| 2331 | + // and do what we in the wizard need to do after an upload is removed |
| 2332 | + mw.UploadWizardUtil.removeItem( _this.uploads, upload ); |
| 2333 | + _this.updateFileCounts(); |
| 2334 | + }); |
| 2335 | + }, |
| 2336 | + |
| 2337 | + /** |
| 2338 | + * This is useful to clean out unused upload file inputs if the user hits GO. |
| 2339 | + * We are using a second array to iterate, because we will be splicing the main one, _this.uploads |
| 2340 | + */ |
| 2341 | + removeEmptyUploads: function() { |
| 2342 | + var _this = this; |
| 2343 | + var toRemove = []; |
| 2344 | + |
| 2345 | + for ( var i = 0; i < _this.uploads.length; i++ ) { |
| 2346 | + if ( mw.isEmpty( _this.uploads[i].ui.fileInputCtrl.value ) ) { |
| 2347 | + toRemove.push( _this.uploads[i] ); |
| 2348 | + } |
| 2349 | + } |
| 2350 | + |
| 2351 | + for ( var j = 0; j < toRemove.length; j++ ) { |
| 2352 | + toRemove[j].remove(); |
| 2353 | + } |
| 2354 | + }, |
| 2355 | + |
| 2356 | + /** |
| 2357 | + * Manage transitioning all of our uploads from one state to another -- like from "new" to "uploaded". |
| 2358 | + * |
| 2359 | + * @param beginState what state the upload should be in before starting. |
| 2360 | + * @param progressState the state to set the upload to while it's doing whatever |
| 2361 | + * @param endState the state to set the upload to after it's done whatever |
| 2362 | + * @param starter function, taking single argument (upload) which starts the process we're interested in |
| 2363 | + * @param endCallback function to call when all uploads are in the end state. |
| 2364 | + */ |
| 2365 | + makeTransitioner: function( beginState, progressState, endState, starter, endCallback ) { |
| 2366 | + |
| 2367 | + var _this = this; |
| 2368 | + |
| 2369 | + var transitioner = function() { |
| 2370 | + var uploadsToStart = _this.maxSimultaneousConnections; |
| 2371 | + var endStateCount = 0; |
| 2372 | + $j.each( _this.uploads, function(i, upload) { |
| 2373 | + if ( upload.state == endState ) { |
| 2374 | + endStateCount++; |
| 2375 | + } else if ( upload.state == progressState ) { |
| 2376 | + uploadsToStart--; |
| 2377 | + } else if ( ( upload.state == beginState ) && ( uploadsToStart > 0 ) ) { |
| 2378 | + starter( upload ); |
| 2379 | + uploadsToStart--; |
| 2380 | + } |
| 2381 | + } ); |
| 2382 | + |
| 2383 | + // build in a little delay even for the end state, so user can see progress bar in a complete state. |
| 2384 | + var nextAction = ( endStateCount == _this.uploads.length ) ? endCallback : transitioner; |
| 2385 | + |
| 2386 | + setTimeout( nextAction, _this.transitionerDelay ); |
| 2387 | + }; |
| 2388 | + |
| 2389 | + transitioner(); |
| 2390 | + }, |
| 2391 | + |
| 2392 | + transitionerDelay: 200, // milliseconds |
| 2393 | + |
| 2394 | + |
| 2395 | + /** |
| 2396 | + * Kick off the upload processes. |
| 2397 | + * Does some precalculations, changes the interface to be less mutable, moves the uploads to a queue, |
| 2398 | + * and kicks off a thread which will take from the queue. |
| 2399 | + * @param endCallback - to execute when uploads are completed |
| 2400 | + */ |
| 2401 | + startUploads: function( endCallback ) { |
| 2402 | + var _this = this; |
| 2403 | + // remove the upload button, and the add file button |
| 2404 | + $j( '#mwe-upwiz-upload-ctrls' ).hide(); |
| 2405 | + $j( '#mwe-upwiz-add-file' ).hide(); |
| 2406 | + |
| 2407 | + var allowCloseWindow = $j().preventCloseWindow( { |
| 2408 | + message: gM( 'mwe-prevent-close') |
| 2409 | + } ); |
| 2410 | + |
| 2411 | + |
| 2412 | + var progressBar = new mw.GroupProgressBar( '#mwe-upwiz-progress', |
| 2413 | + gM( 'mwe-upwiz-uploading' ), |
| 2414 | + _this.uploads, |
| 2415 | + 'transported', |
| 2416 | + 'transportProgress', |
| 2417 | + 'transportWeight' ); |
| 2418 | + progressBar.start(); |
| 2419 | + |
| 2420 | + |
| 2421 | + // remove ability to change files |
| 2422 | + // ideally also hide the "button"... but then we require styleable file input CSS trickery |
| 2423 | + // although, we COULD do this just for files already in progress... |
| 2424 | + |
| 2425 | + // it might be interesting to just make this creational -- attach it to the dom element representing |
| 2426 | + // the progress bar and elapsed time |
| 2427 | + _this.makeTransitioner( |
| 2428 | + 'new', |
| 2429 | + 'transporting', |
| 2430 | + 'transported', |
| 2431 | + function( upload ) { |
| 2432 | + upload.start(); |
| 2433 | + }, |
| 2434 | + function() { |
| 2435 | + allowCloseWindow(); |
| 2436 | + $j().notify( gM( 'mwe-upwiz-files-complete' ) ); |
| 2437 | + endCallback(); |
| 2438 | + } |
| 2439 | + ); |
| 2440 | + }, |
| 2441 | + |
| 2442 | + |
| 2443 | + |
| 2444 | + /** |
| 2445 | + * Occurs whenever we need to update the interface based on how many files there are |
| 2446 | + * Thhere is an uncounted upload, waiting to be used, which has a fileInput which covers the |
| 2447 | + * "add an upload" button. This is absolutely positioned, so it needs to be moved if another upload was removed. |
| 2448 | + * The uncounted upload is also styled differently between the zero and n files cases |
| 2449 | + */ |
| 2450 | + updateFileCounts: function() { |
| 2451 | + var _this = this; |
| 2452 | + |
| 2453 | + if ( _this.uploads.length ) { |
| 2454 | + $j( '#mwe-upwiz-upload-ctrl' ).removeAttr( 'disabled' ); |
| 2455 | + $j( '#mwe-upwiz-stepdiv-file .mwe-upwiz-buttons' ).show(); |
| 2456 | + $j( '#mwe-upwiz-add-file' ).html( gM( 'mwe-upwiz-add-file-n' ) ); |
| 2457 | + $j( '#mwe-upwiz-add-file-container' ).removeClass('mwe-upwiz-add-files-0'); |
| 2458 | + $j( '#mwe-upwiz-add-file-container' ).addClass('mwe-upwiz-add-files-n'); |
| 2459 | + $j( '#mwe-upwiz-files .mwe-upwiz-file.filled:odd' ).addClass( 'odd' ); |
| 2460 | + $j( '#mwe-upwiz-files .mwe-upwiz-file:filled:even' ).removeClass( 'odd' ); |
| 2461 | + } else { |
| 2462 | + $j( '#mwe-upwiz-upload-ctrl' ).attr( 'disabled', 'disabled' ); |
| 2463 | + $j( '#mwe-upwiz-stepdiv-file .mwe-upwiz-buttons' ).hide(); |
| 2464 | + $j( '#mwe-upwiz-add-file' ).html( gM( 'mwe-upwiz-add-file-0' ) ); |
| 2465 | + $j( '#mwe-upwiz-add-file-container' ).addClass('mwe-upwiz-add-files-0'); |
| 2466 | + $j( '#mwe-upwiz-add-file-container' ).removeClass('mwe-upwiz-add-files-n'); |
| 2467 | + } |
| 2468 | + |
| 2469 | + if ( _this.uploads.length < _this.maxUploads ) { |
| 2470 | + $j( '#mwe-upwiz-add-file' ).removeAttr( 'disabled' ); |
| 2471 | + $j( _this.uploadToAdd.ui.div ).show(); |
| 2472 | + _this.uploadToAdd.ui.moveFileInputToCover( '#mwe-upwiz-add-file' ); |
| 2473 | + } else { |
| 2474 | + $j( '#mwe-upwiz-add-file' ).attr( 'disabled', true ); |
| 2475 | + $j( _this.uploadToAdd.ui.div ).hide(); |
| 2476 | + } |
| 2477 | + |
| 2478 | + |
| 2479 | + }, |
| 2480 | + |
| 2481 | + |
| 2482 | + /** |
| 2483 | + * are all the details valid? |
| 2484 | + * @return boolean |
| 2485 | + */ |
| 2486 | + detailsValid: function() { |
| 2487 | + var _this = this; |
| 2488 | + var valid = true; |
| 2489 | + $j.each( _this.uploads, function(i, upload) { |
| 2490 | + valid &= upload.details.valid(); |
| 2491 | + } ); |
| 2492 | + return valid; |
| 2493 | + }, |
| 2494 | + |
| 2495 | + /** |
| 2496 | + * Submit all edited details and other metadata |
| 2497 | + * Works just like startUploads -- parallel simultaneous submits with progress bar. |
| 2498 | + */ |
| 2499 | + detailsSubmit: function( endCallback ) { |
| 2500 | + var _this = this; |
| 2501 | + // some details blocks cannot be submitted (for instance, identical file hash) |
| 2502 | + _this.removeBlockedDetails(); |
| 2503 | + |
| 2504 | + // XXX validate all |
| 2505 | + |
| 2506 | + // remove ability to edit details |
| 2507 | + $j.each( _this.uploads, function( i, upload ) { |
| 2508 | + upload.details.div.mask(); |
| 2509 | + upload.details.div.data( 'mask' ).loadingSpinner(); |
| 2510 | + } ); |
| 2511 | + |
| 2512 | + // add the upload progress bar, with ETA |
| 2513 | + // add in the upload count |
| 2514 | + _this.makeTransitioner( |
| 2515 | + 'details', |
| 2516 | + 'submitting-details', |
| 2517 | + 'complete', |
| 2518 | + function( upload ) { |
| 2519 | + upload.details.submit( function() { |
| 2520 | + upload.details.div.data( 'mask' ).html(); |
| 2521 | + } ); |
| 2522 | + }, |
| 2523 | + endCallback |
| 2524 | + ); |
| 2525 | + }, |
| 2526 | + |
| 2527 | + /** |
| 2528 | + * Removes(?) details that we can't edit for whatever reason -- might just advance them to a different state? |
| 2529 | + */ |
| 2530 | + removeBlockedDetails: function() { |
| 2531 | + // TODO |
| 2532 | + }, |
| 2533 | + |
| 2534 | + |
| 2535 | + prefillThanksPage: function() { |
| 2536 | + var _this = this; |
| 2537 | + |
| 2538 | + $j( '#mwe-upwiz-thanks' ) |
| 2539 | + .append( $j( '<h3 style="text-align: center;">' ).append( gM( 'mwe-upwiz-thanks-intro' ) ), |
| 2540 | + $j( '<p style="margin-bottom: 2em; text-align: center;">' ) |
| 2541 | + .append( gM( 'mwe-upwiz-thanks-explain', _this.uploads.length ) ) ); |
| 2542 | + |
| 2543 | + $j.each( _this.uploads, function(i, upload) { |
| 2544 | + var thanksDiv = $j( '<div class="mwe-upwiz-thanks ui-helper-clearfix" />' ); |
| 2545 | + _this.thanksDiv = thanksDiv; |
| 2546 | + |
| 2547 | + var thumbnailDiv = $j( '<div class="mwe-upwiz-thumbnail mwe-upwiz-thumbnail-side"></div>' ); |
| 2548 | + upload.setThumbnail( thumbnailDiv ); |
| 2549 | + thumbnailDiv.append( $j('<p/>').append( |
| 2550 | + $j( '<a />' ) |
| 2551 | + .attr( { target: '_new', |
| 2552 | + href: upload.imageinfo.descriptionurl } ) |
| 2553 | + .text( upload.title ) |
| 2554 | + ) ); |
| 2555 | + |
| 2556 | + thanksDiv.append( thumbnailDiv ); |
| 2557 | + |
| 2558 | + var thumbWikiText = "[[" + upload.title + "|thumb|Add caption here]]"; |
| 2559 | + |
| 2560 | + thanksDiv.append( |
| 2561 | + $j( '<div class="mwe-upwiz-data"></div>' ) |
| 2562 | + .append( |
| 2563 | + $j('<p/>').append( |
| 2564 | + gM( 'mwe-upwiz-thanks-wikitext' ), |
| 2565 | + $j( '<br />' ), |
| 2566 | + $j( '<textarea class="mwe-long-textarea" rows="2"/>' ) |
| 2567 | + .growTextArea() |
| 2568 | + .readonly() |
| 2569 | + .append( thumbWikiText ) |
| 2570 | + .trigger('resizeEvent') |
| 2571 | + ), |
| 2572 | + $j('<p/>').append( |
| 2573 | + gM( 'mwe-upwiz-thanks-url' ), |
| 2574 | + $j( '<br />' ), |
| 2575 | + $j( '<textarea class="mwe-long-textarea" rows="2"/>' ) |
| 2576 | + .growTextArea() |
| 2577 | + .readonly() |
| 2578 | + .append( upload.imageinfo.descriptionurl ) |
| 2579 | + .trigger('resizeEvent') |
| 2580 | + ) |
| 2581 | + ) |
| 2582 | + ); |
| 2583 | + |
| 2584 | + $j( '#mwe-upwiz-thanks' ).append( thanksDiv ); |
| 2585 | + } ); |
| 2586 | + }, |
| 2587 | + |
| 2588 | + /** |
| 2589 | + * |
| 2590 | + */ |
| 2591 | + pause: function() { |
| 2592 | + |
| 2593 | + }, |
| 2594 | + |
| 2595 | + /** |
| 2596 | + * |
| 2597 | + */ |
| 2598 | + stop: function() { |
| 2599 | + |
| 2600 | + } |
| 2601 | +}; |
| 2602 | + |
| 2603 | + |
| 2604 | +mw.UploadWizardDeedPreview = function(upload) { |
| 2605 | + this.upload = upload; |
| 2606 | +}; |
| 2607 | + |
| 2608 | +mw.UploadWizardDeedPreview.prototype = { |
| 2609 | + setup: function() { |
| 2610 | + var _this = this; |
| 2611 | + // add a preview on the deeds page |
| 2612 | + var thumbnailDiv = $j( '<div class="mwe-upwiz-thumbnail-small"></div>' ); |
| 2613 | + $j( '#mwe-upwiz-deeds-thumbnails' ).append( thumbnailDiv ); |
| 2614 | + _this.upload.setThumbnail( thumbnailDiv, mw.UploadWizard.config[ 'smallThumbnailWidth' ] ); |
| 2615 | + } |
| 2616 | +}; |
| 2617 | + |
| 2618 | +mw.UploadWizardNullDeed = $j.extend( new mw.UploadWizardDeed(), { |
| 2619 | + valid: function() { |
| 2620 | + return false; |
| 2621 | + } |
| 2622 | +} ); |
| 2623 | + |
| 2624 | + |
| 2625 | +/** |
| 2626 | + * Set up the form and deed object for the deed option that says these uploads are all the user's own work. |
| 2627 | + * XXX these deeds are starting to turn into jquery fns |
| 2628 | + */ |
| 2629 | +mw.UploadWizardDeedOwnWork = function( uploadCount ) { |
| 2630 | + uploadCount = uploadCount ? uploadCount : 1; |
| 2631 | + |
| 2632 | + var _this = new mw.UploadWizardDeed(); |
| 2633 | + |
| 2634 | + _this.authorInput = $j( '<input />') |
| 2635 | + .attr( { name: "author", type: "text" } ) |
| 2636 | + .addClass( 'mwe-upwiz-sign' ); |
| 2637 | + |
| 2638 | + var licenseInputDiv = $j( '<div class="mwe-upwiz-deed-license"></div>' ); |
| 2639 | + _this.licenseInput = new mw.UploadWizardLicenseInput( licenseInputDiv ); |
| 2640 | + _this.licenseInput.setDefaultValues(); |
| 2641 | + |
| 2642 | + return $j.extend( _this, { |
| 2643 | + |
| 2644 | + name: 'ownwork', |
| 2645 | + |
| 2646 | + /** |
| 2647 | + * Is this correctly set, with side effects of causing errors to show in interface. |
| 2648 | + * @return boolean true if valid, false if not |
| 2649 | + */ |
| 2650 | + valid: function() { |
| 2651 | + // n.b. valid() has side effects and both should be called every time the function is called. |
| 2652 | + // do not short-circuit. |
| 2653 | + var formValid = _this.$form.valid(); |
| 2654 | + var licenseInputValid = _this.licenseInput.valid(); |
| 2655 | + return formValid && licenseInputValid; |
| 2656 | + }, |
| 2657 | + |
| 2658 | + getSourceWikiText: function() { |
| 2659 | + return '{{own}}'; |
| 2660 | + }, |
| 2661 | + |
| 2662 | + // XXX do we need to escape authorInput, or is wikitext a feature here? |
| 2663 | + // what about scripts? |
| 2664 | + getAuthorWikiText: function() { |
| 2665 | + return "[[User:" + mw.UploadWizard.config[ 'userName' ] + '|' + $j( _this.authorInput ).val() + ']]'; |
| 2666 | + }, |
| 2667 | + |
| 2668 | + |
| 2669 | + getLicenseWikiText: function() { |
| 2670 | + var wikiText = '{{self'; |
| 2671 | + $j.each( _this.licenseInput.getTemplates(), function( i, template ) { |
| 2672 | + wikiText += '|' + template; |
| 2673 | + } ); |
| 2674 | + wikiText += '}}'; |
| 2675 | + return wikiText; |
| 2676 | + }, |
| 2677 | + |
| 2678 | + setFormFields: function( $selector ) { |
| 2679 | + _this.$selector = $selector; |
| 2680 | + |
| 2681 | + _this.$form = $j( '<form/>' ); |
| 2682 | + |
| 2683 | + var $standardDiv = $j( '<div />' ).append( |
| 2684 | + $j( '<label for="author2" generated="true" class="mwe-validator-error" style="display:block;"/>' ), |
| 2685 | + $j( '<p>' ) |
| 2686 | + .html( gM( 'mwe-upwiz-source-ownwork-assert', |
| 2687 | + uploadCount, |
| 2688 | + '<span class="mwe-standard-author-input"></span>' ) |
| 2689 | + ), |
| 2690 | + $j( '<p class="mwe-small-print" />' ).append( gM( 'mwe-upwiz-source-ownwork-assert-note' ) ) |
| 2691 | + ); |
| 2692 | + $standardDiv.find( '.mwe-standard-author-input' ).append( $j( '<input name="author2" type="text" class="mwe-upwiz-sign" />' ) ); |
| 2693 | + |
| 2694 | + var $customDiv = $j('<div/>').append( |
| 2695 | + $j( '<label for="author" generated="true" class="mwe-validator-error" style="display:block;"/>' ), |
| 2696 | + $j( '<p>' ) |
| 2697 | + .html( gM( 'mwe-upwiz-source-ownwork-assert-custom', |
| 2698 | + uploadCount, |
| 2699 | + '<span class="mwe-custom-author-input"></span>' ) ), |
| 2700 | + licenseInputDiv |
| 2701 | + ); |
| 2702 | + // have to add the author input this way -- gM() will flatten it to a string and we'll lose it as a dom object |
| 2703 | + $customDiv.find( '.mwe-custom-author-input' ).append( _this.authorInput ); |
| 2704 | + |
| 2705 | + |
| 2706 | + var $crossfader = $j( '<div>' ).append( $standardDiv, $customDiv ); |
| 2707 | + var $toggler = $j( '<p class="mwe-more-options" style="text-align: right" />' ) |
| 2708 | + .append( $j( '<a />' ) |
| 2709 | + .append( gM( 'mwe-upwiz-license-show-all' ) ) |
| 2710 | + .click( function() { |
| 2711 | + _this.formValidator.resetForm(); |
| 2712 | + if ( $crossfader.data( 'crossfadeDisplay' ) === $customDiv ) { |
| 2713 | + _this.licenseInput.setDefaultValues(); |
| 2714 | + $crossfader.morphCrossfade( $standardDiv ); |
| 2715 | + $j( this ).html( gM( 'mwe-upwiz-license-show-all' ) ); |
| 2716 | + } else { |
| 2717 | + $crossfader.morphCrossfade( $customDiv ); |
| 2718 | + $j( this ).html( gM( 'mwe-upwiz-license-show-recommended' ) ); |
| 2719 | + } |
| 2720 | + } ) ); |
| 2721 | + |
| 2722 | + var $formFields = $j( '<div class="mwe-upwiz-deed-form-internal" />' ) |
| 2723 | + .append( $crossfader, $toggler ); |
| 2724 | + |
| 2725 | + |
| 2726 | + // synchronize both username signatures |
| 2727 | + // set initial value to configured username |
| 2728 | + // if one changes all the others change (keyup event) |
| 2729 | + // |
| 2730 | + // also set tooltips ( the title, tipsy() ) |
| 2731 | + $formFields.find( '.mwe-upwiz-sign' ) |
| 2732 | + .attr( { |
| 2733 | + title: gM( 'mwe-upwiz-tooltip-sign' ), |
| 2734 | + value: mw.UploadWizard.config[ 'userName' ] |
| 2735 | + } ) |
| 2736 | + .tipsyPlus() |
| 2737 | + .keyup( function() { |
| 2738 | + var thisInput = this; |
| 2739 | + var thisVal = $j( thisInput ).val(); |
| 2740 | + $j.each( $formFields.find( '.mwe-upwiz-sign' ), function( i, input ) { |
| 2741 | + if (thisInput !== input) { |
| 2742 | + $j( input ).val( thisVal ); |
| 2743 | + } |
| 2744 | + } ); |
| 2745 | + } ); |
| 2746 | + |
| 2747 | + _this.$form.append( $formFields ); |
| 2748 | + $selector.append( _this.$form ); |
| 2749 | + |
| 2750 | + // done after added to the DOM, so there are true heights |
| 2751 | + $crossfader.morphCrossfader(); |
| 2752 | + |
| 2753 | + |
| 2754 | + // and finally, make it validatable |
| 2755 | + _this.formValidator = _this.$form.validate( { |
| 2756 | + rules: { |
| 2757 | + author2: { |
| 2758 | + required: function( element ) { |
| 2759 | + return $crossfader.data( 'crossfadeDisplay' ).get(0) === $standardDiv.get(0); |
| 2760 | + }, |
| 2761 | + minlength: mw.UploadWizard.config[ 'minAuthorLength' ], |
| 2762 | + maxlength: mw.UploadWizard.config[ 'maxAuthorLength' ] |
| 2763 | + }, |
| 2764 | + author: { |
| 2765 | + required: function( element ) { |
| 2766 | + return $crossfader.data( 'crossfadeDisplay' ).get(0) === $customDiv.get(0); |
| 2767 | + }, |
| 2768 | + minlength: mw.UploadWizard.config[ 'minAuthorLength' ], |
| 2769 | + maxlength: mw.UploadWizard.config[ 'maxAuthorLength' ] |
| 2770 | + } |
| 2771 | + }, |
| 2772 | + messages: { |
| 2773 | + author2: { |
| 2774 | + required: gM( 'mwe-upwiz-error-signature-blank' ), |
| 2775 | + minlength: gM( 'mwe-upwiz-error-signature-too-short', mw.UploadWizard.config[ 'minAuthorLength' ] ), |
| 2776 | + maxlength: gM( 'mwe-upwiz-error-signature-too-long', mw.UploadWizard.config[ 'maxAuthorLength' ] ) |
| 2777 | + }, |
| 2778 | + author: { |
| 2779 | + required: gM( 'mwe-upwiz-error-signature-blank' ), |
| 2780 | + minlength: gM( 'mwe-upwiz-error-signature-too-short', mw.UploadWizard.config[ 'minAuthorLength' ] ), |
| 2781 | + maxlength: gM( 'mwe-upwiz-error-signature-too-long', mw.UploadWizard.config[ 'maxAuthorLength' ] ) |
| 2782 | + } |
| 2783 | + } |
| 2784 | + } ); |
| 2785 | + } |
| 2786 | + |
| 2787 | + |
| 2788 | + } ); |
| 2789 | + |
| 2790 | +}; |
| 2791 | + |
| 2792 | +// XXX these deeds are starting to turn into jquery fns |
| 2793 | +mw.UploadWizardDeedThirdParty = function( uploadCount ) { |
| 2794 | + var _this = new mw.UploadWizardDeed(); |
| 2795 | + |
| 2796 | + _this.uploadCount = uploadCount ? uploadCount : 1; |
| 2797 | + _this.sourceInput = $j('<textarea class="mwe-source mwe-long-textarea" name="source" rows="1" cols="40"></textarea>' ) |
| 2798 | + .growTextArea() |
| 2799 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-source' ) ) |
| 2800 | + .tipsyPlus(); |
| 2801 | + _this.authorInput = $j('<textarea class="mwe-author mwe-long-textarea" name="author" rows="1" cols="40"></textarea>' ) |
| 2802 | + .growTextArea() |
| 2803 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-author' ) ) |
| 2804 | + .tipsyPlus(); |
| 2805 | + licenseInputDiv = $j( '<div class="mwe-upwiz-deed-license"></div>' ); |
| 2806 | + _this.licenseInput = new mw.UploadWizardLicenseInput( licenseInputDiv ); |
| 2807 | + |
| 2808 | + |
| 2809 | + return $j.extend( _this, mw.UploadWizardDeed.prototype, { |
| 2810 | + name: 'thirdparty', |
| 2811 | + |
| 2812 | + setFormFields: function( $selector ) { |
| 2813 | + var _this = this; |
| 2814 | + _this.$form = $j( '<form/>' ); |
| 2815 | + |
| 2816 | + var $formFields = $j( '<div class="mwe-upwiz-deed-form-internal"/>' ); |
| 2817 | + |
| 2818 | + if ( uploadCount > 1 ) { |
| 2819 | + $formFields.append( $j( '<div />' ).append( gM( 'mwe-upwiz-source-thirdparty-custom-multiple-intro' ) ) ); |
| 2820 | + } |
| 2821 | + |
| 2822 | + $formFields.append ( |
| 2823 | + $j( '<div class="mwe-upwiz-source-thirdparty-custom-multiple-intro" />' ), |
| 2824 | + $j( '<label for="source" generated="true" class="mwe-validator-error" style="display:block;"/>' ), |
| 2825 | + $j( '<div class="mwe-upwiz-thirdparty-fields" />' ) |
| 2826 | + .append( $j( '<label for="source"/>' ).text( gM( 'mwe-upwiz-source' ) ), |
| 2827 | + _this.sourceInput ), |
| 2828 | + $j( '<label for="author" generated="true" class="mwe-validator-error" style="display:block;"/>' ), |
| 2829 | + $j( '<div class="mwe-upwiz-thirdparty-fields" />' ) |
| 2830 | + .append( $j( '<label for="author"/>' ).text( gM( 'mwe-upwiz-author' ) ), |
| 2831 | + _this.authorInput ), |
| 2832 | + $j( '<div class="mwe-upwiz-thirdparty-license" />' ) |
| 2833 | + .append( gM( 'mwe-upwiz-source-thirdparty-license', uploadCount ) ), |
| 2834 | + licenseInputDiv |
| 2835 | + ); |
| 2836 | + |
| 2837 | + _this.$form.validate( { |
| 2838 | + rules: { |
| 2839 | + source: { required: true, |
| 2840 | + minlength: mw.UploadWizard.config[ 'minSourceLength' ], |
| 2841 | + maxlength: mw.UploadWizard.config[ 'maxSourceLength' ] }, |
| 2842 | + author: { required: true, |
| 2843 | + minlength: mw.UploadWizard.config[ 'minAuthorLength' ], |
| 2844 | + maxlength: mw.UploadWizard.config[ 'maxAuthorLength' ] } |
| 2845 | + }, |
| 2846 | + messages: { |
| 2847 | + source: { |
| 2848 | + required: gM( 'mwe-upwiz-error-blank' ), |
| 2849 | + minlength: gM( 'mwe-upwiz-error-too-short', mw.UploadWizard.config[ 'minSourceLength' ] ), |
| 2850 | + maxlength: gM( 'mwe-upwiz-error-too-long', mw.UploadWizard.config[ 'maxSourceLength' ] ) |
| 2851 | + }, |
| 2852 | + author: { |
| 2853 | + required: gM( 'mwe-upwiz-error-blank' ), |
| 2854 | + minlength: gM( 'mwe-upwiz-error-too-short', mw.UploadWizard.config[ 'minAuthorLength' ] ), |
| 2855 | + maxlength: gM( 'mwe-upwiz-error-too-long', mw.UploadWizard.config[ 'maxAuthorLength' ] ) |
| 2856 | + } |
| 2857 | + } |
| 2858 | + } ); |
| 2859 | + |
| 2860 | + _this.$form.append( $formFields ); |
| 2861 | + |
| 2862 | + $selector.append( _this.$form ); |
| 2863 | + }, |
| 2864 | + |
| 2865 | + /** |
| 2866 | + * Is this correctly set, with side effects of causing errors to show in interface. |
| 2867 | + * this is exactly the same as the ownwork valid() function... hopefully we can reduce these to nothing if we make |
| 2868 | + * all validators work the same. |
| 2869 | + * @return boolean true if valid, false if not |
| 2870 | + */ |
| 2871 | + valid: function() { |
| 2872 | + // n.b. valid() has side effects and both should be called every time the function is called. |
| 2873 | + // do not short-circuit. |
| 2874 | + var formValid = _this.$form.valid(); |
| 2875 | + var licenseInputValid = _this.licenseInput.valid(); |
| 2876 | + return formValid && licenseInputValid; |
| 2877 | + } |
| 2878 | + } ); |
| 2879 | +}; |
| 2880 | + |
| 2881 | + |
| 2882 | + |
| 2883 | + |
| 2884 | +/** |
| 2885 | + * @param selector where to put this deed chooser |
| 2886 | + * @param isPlural whether this chooser applies to multiple files (changes messaging mostly) |
| 2887 | + */ |
| 2888 | +mw.UploadWizardDeedChooser = function( selector, deeds, uploadCount ) { |
| 2889 | + var _this = this; |
| 2890 | + _this.$selector = $j( selector ); |
| 2891 | + _this.uploadCount = uploadCount ? uploadCount : 1; |
| 2892 | + |
| 2893 | + |
| 2894 | + _this.$errorEl = $j( '<div class="mwe-error"></div>' ); |
| 2895 | + _this.$selector.append( _this.$errorEl ); |
| 2896 | + |
| 2897 | + // name for radio button set |
| 2898 | + mw.UploadWizardDeedChooser.prototype.widgetCount++; |
| 2899 | + _this.name = 'deedChooser' + mw.UploadWizardDeedChooser.prototype.widgetCount.toString(); |
| 2900 | + |
| 2901 | + $j.each( deeds, function (i, deed) { |
| 2902 | + var id = _this.name + '-' + deed.name; |
| 2903 | + |
| 2904 | + var $deedInterface = $j( |
| 2905 | + '<div class="mwe-upwiz-deed mwe-upwiz-deed-' + deed.name + '">' |
| 2906 | + + '<div class="mwe-upwiz-deed-option-title">' |
| 2907 | + + '<span class="mwe-upwiz-deed-header">' |
| 2908 | + + '<input id="' + id +'" name="' + _this.name + '" type="radio" value="' + deed.name + '">' |
| 2909 | + + '<label for="' + id + '" class="mwe-upwiz-deed-name">' |
| 2910 | + + gM( 'mwe-upwiz-source-' + deed.name, _this.uploadCount ) |
| 2911 | + + '</label>' |
| 2912 | + + '</input>' |
| 2913 | + + '</span>' |
| 2914 | + // + ' <a class="mwe-upwiz-macro-deeds-return">' + gM( 'mwe-upwiz-change' ) + '</a>' |
| 2915 | + + '</div>' |
| 2916 | + + '<div class="mwe-upwiz-deed-form">' |
| 2917 | + + '</div>' |
| 2918 | + ); |
| 2919 | + |
| 2920 | + var $deedSelector = _this.$selector.append( $deedInterface ); |
| 2921 | + |
| 2922 | + deed.setFormFields( $deedInterface.find( '.mwe-upwiz-deed-form' ) ); |
| 2923 | + |
| 2924 | + $deedInterface.find( 'span.mwe-upwiz-deed-header input' ).click( function() { |
| 2925 | + if ( $j( this ).is(':checked' ) ) { |
| 2926 | + _this.choose( deed ); |
| 2927 | + _this.showDeed( $deedInterface ); |
| 2928 | + } |
| 2929 | + } ); |
| 2930 | + |
| 2931 | + } ); |
| 2932 | + |
| 2933 | + /* |
| 2934 | + $j( '.mwe-upwiz-macro-deeds-return' ).click( function() { |
| 2935 | + _this.choose( mw.UploadWizardNullDeed ); |
| 2936 | + _this.showDeedChoice(); |
| 2937 | + } ); |
| 2938 | + */ |
| 2939 | + |
| 2940 | + _this.choose( mw.UploadWizardNullDeed ); |
| 2941 | + _this.showDeedChoice(); |
| 2942 | + |
| 2943 | + |
| 2944 | +}; |
| 2945 | + |
| 2946 | + |
| 2947 | +mw.UploadWizardDeedChooser.prototype = { |
| 2948 | + |
| 2949 | + /** |
| 2950 | + * How many deed choosers there are (important for creating unique ids, element names) |
| 2951 | + */ |
| 2952 | + widgetCount: 0, |
| 2953 | + |
| 2954 | + /** |
| 2955 | + * Check if this form is filled out correctly, with side effects of showing error messages if invalid |
| 2956 | + * @return boolean; true if valid, false if not |
| 2957 | + */ |
| 2958 | + valid: function() { |
| 2959 | + var _this = this; |
| 2960 | + // we assume there is always a deed available, even if it's just the null deed. |
| 2961 | + var valid = _this.deed.valid(); |
| 2962 | + // the only time we need to set an error message is if the null deed is selected. |
| 2963 | + // otherwise, we can assume that the widgets have already added error messages. |
| 2964 | + if (valid) { |
| 2965 | + _this.hideError(); |
| 2966 | + } else { |
| 2967 | + if ( _this.deed === mw.UploadWizardNullDeed ) { |
| 2968 | + _this.showError( gM( 'mwe-upwiz-deeds-need-deed', _this.uploadCount ) ); |
| 2969 | + $j( _this ).bind( 'chooseDeed', function() { |
| 2970 | + _this.hideError(); |
| 2971 | + } ); |
| 2972 | + } |
| 2973 | + } |
| 2974 | + return valid; |
| 2975 | + }, |
| 2976 | + |
| 2977 | + showError: function( error ) { |
| 2978 | + this.$errorEl.html( error ); |
| 2979 | + this.$errorEl.fadeIn(); |
| 2980 | + }, |
| 2981 | + |
| 2982 | + hideError: function() { |
| 2983 | + this.$errorEl.fadeOut(); |
| 2984 | + this.$errorEl.empty(); |
| 2985 | + }, |
| 2986 | + |
| 2987 | + /** |
| 2988 | + * How many uploads this deed controls |
| 2989 | + */ |
| 2990 | + uploadCount: 0, |
| 2991 | + |
| 2992 | + |
| 2993 | + // XXX it's impossible to choose the null deed if we stick with radio buttons, so that may be useless later |
| 2994 | + choose: function( deed ) { |
| 2995 | + var _this = this; |
| 2996 | + _this.deed = deed; |
| 2997 | + if ( deed === mw.UploadWizardNullDeed ) { |
| 2998 | + $j( _this ).trigger( 'chooseNullDeed' ); |
| 2999 | + //_this.trigger( 'isNotReady' ); |
| 3000 | + _this.$selector |
| 3001 | + .find( 'input.mwe-accept-deed' ) |
| 3002 | + .attr( 'checked', false ); |
| 3003 | + } else { |
| 3004 | + $j( _this ).trigger( 'chooseDeed' ); |
| 3005 | + } |
| 3006 | + }, |
| 3007 | + |
| 3008 | + /** |
| 3009 | + * Go back to original source choice. |
| 3010 | + */ |
| 3011 | + showDeedChoice: function() { |
| 3012 | + var $allDeeds = this.$selector.find( '.mwe-upwiz-deed' ); |
| 3013 | + this.deselectDeed( $allDeeds ); |
| 3014 | + // $allDeeds.fadeTo( 'fast', 1.0 ); //maskSafeShow(); |
| 3015 | + }, |
| 3016 | + |
| 3017 | + /** |
| 3018 | + * From the deed choices, make a choice fade to the background a bit, hide the extended form |
| 3019 | + */ |
| 3020 | + deselectDeed: function( $deedSelector ) { |
| 3021 | + $deedSelector.removeClass( 'selected' ); |
| 3022 | + // $deedSelector.find( 'a.mwe-upwiz-macro-deeds-return' ).hide(); |
| 3023 | + $deedSelector.find( '.mwe-upwiz-deed-form' ).slideUp( 500 ); //.maskSafeHide(); |
| 3024 | + }, |
| 3025 | + |
| 3026 | + /** |
| 3027 | + * From the deed choice page, show a particular deed |
| 3028 | + */ |
| 3029 | + showDeed: function( $deedSelector ) { |
| 3030 | + var $otherDeeds = $deedSelector.siblings().filter( '.mwe-upwiz-deed' ); |
| 3031 | + this.deselectDeed( $otherDeeds ); |
| 3032 | + // $siblings.fadeTo( 'fast', 0.5 ) // maskSafeHide(); |
| 3033 | + |
| 3034 | + $deedSelector |
| 3035 | + .addClass('selected') |
| 3036 | + .fadeTo( 'fast', 1.0 ) |
| 3037 | + .find( '.mwe-upwiz-deed-form' ).slideDown( 500 ); // maskSafeShow(); |
| 3038 | + // $deedSelector.find( 'a.mwe-upwiz-macro-deeds-return' ).show(); |
| 3039 | + } |
| 3040 | + |
| 3041 | +}; |
| 3042 | + |
| 3043 | + |
| 3044 | + |
| 3045 | +/** |
| 3046 | + * Miscellaneous utilities |
| 3047 | + */ |
| 3048 | +mw.UploadWizardUtil = { |
| 3049 | + |
| 3050 | + /** |
| 3051 | + * Simple 'more options' toggle that opens more of a form. |
| 3052 | + * |
| 3053 | + * @param toggleDiv the div which has the control to open and shut custom options |
| 3054 | + * @param moreDiv the div containing the custom options |
| 3055 | + */ |
| 3056 | + makeToggler: function ( toggleDiv, moreDiv ) { |
| 3057 | + var $toggleLink = $j( '<a>' ) |
| 3058 | + .addClass( 'mwe-upwiz-toggler mwe-upwiz-more-options' ) |
| 3059 | + .append( gM( 'mwe-upwiz-more-options' ) ); |
| 3060 | + $j( toggleDiv ).append( $toggleLink ); |
| 3061 | + |
| 3062 | + |
| 3063 | + var toggle = function( open ) { |
| 3064 | + if ( typeof open === 'undefined' ) { |
| 3065 | + open = ! ( $j( this ).data( 'open' ) ) ; |
| 3066 | + } |
| 3067 | + $j( this ).data( 'open', open ); |
| 3068 | + if ( open ) { |
| 3069 | + moreDiv.maskSafeShow(); |
| 3070 | + /* when open, show control to close */ |
| 3071 | + $toggleLink.html( gM( 'mwe-upwiz-fewer-options' ) ); |
| 3072 | + $toggleLink.addClass( "mwe-upwiz-toggler-open" ); |
| 3073 | + } else { |
| 3074 | + moreDiv.maskSafeHide(); |
| 3075 | + /* when closed, show control to open */ |
| 3076 | + $toggleLink.html( gM( 'mwe-upwiz-more-options' ) ); |
| 3077 | + $toggleLink.removeClass( "mwe-upwiz-toggler-open" ); |
| 3078 | + } |
| 3079 | + }; |
| 3080 | + |
| 3081 | + toggle(false); |
| 3082 | + |
| 3083 | + $toggleLink.click( function( e ) { e.stopPropagation(); toggle(); } ); |
| 3084 | + |
| 3085 | + $j( moreDiv ).addClass( 'mwe-upwiz-toggled' ); |
| 3086 | + }, |
| 3087 | + |
| 3088 | + /** |
| 3089 | + * remove an item from an array. Tests for === identity to remove the item |
| 3090 | + * XXX the entire rationale for this file may be wrong. |
| 3091 | + * XXX The jQuery way would be to query the DOM for objects, not to keep a separate array hanging around |
| 3092 | + * @param items the array where we want to remove an item |
| 3093 | + * @param item the item to remove |
| 3094 | + */ |
| 3095 | + removeItem: function( items, item ) { |
| 3096 | + for ( var i = 0; i < items.length; i++ ) { |
| 3097 | + if ( items[i] === item ) { |
| 3098 | + items.splice( i, 1 ); |
| 3099 | + break; |
| 3100 | + } |
| 3101 | + } |
| 3102 | + }, |
| 3103 | + |
| 3104 | + /** |
| 3105 | + * Capitalise first letter and replace spaces by underscores |
| 3106 | + * @param filename (basename, without directories) |
| 3107 | + * @return typical title as would appear on MediaWiki |
| 3108 | + */ |
| 3109 | + pathToTitle: function ( filename ) { |
| 3110 | + return mw.ucfirst( $j.trim( filename ).replace(/ /g, '_' ) ); |
| 3111 | + }, |
| 3112 | + |
| 3113 | + /** |
| 3114 | + * Capitalise first letter and replace underscores by spaces |
| 3115 | + * @param title typical title as would appear on MediaWiki |
| 3116 | + * @return plausible local filename |
| 3117 | + */ |
| 3118 | + titleToPath: function ( title ) { |
| 3119 | + return mw.ucfirst( $j.trim( title ).replace(/_/g, ' ' ) ); |
| 3120 | + }, |
| 3121 | + |
| 3122 | + |
| 3123 | + /** |
| 3124 | + * Transform "File:title_with_spaces.jpg" into "title with spaces" |
| 3125 | + * @param typical title that would appear on mediawiki, with File: and extension, may include underscores |
| 3126 | + * @return human readable title |
| 3127 | + */ |
| 3128 | + fileTitleToHumanTitle: function( title ) { |
| 3129 | + var extension = mw.UploadWizardUtil.getExtension( title ); |
| 3130 | + if ( typeof extension !== 'undefined' ) { |
| 3131 | + // the -1 is to get the '.' |
| 3132 | + title = title.substr( 0, title.length - extension.length - 1 ); |
| 3133 | + } |
| 3134 | + // usually File: |
| 3135 | + var namespace = wgFormattedNamespaces[wgNamespaceIds['file']]; |
| 3136 | + if ( title.indexOf( namespace + ':' ) === 0 ) { |
| 3137 | + title = title.substr( namespace.length + 1 ); |
| 3138 | + } |
| 3139 | + return mw.UploadWizardUtil.titleToPath( title ); |
| 3140 | + }, |
| 3141 | + |
| 3142 | + |
| 3143 | + /** |
| 3144 | + * Slice extension off a path |
| 3145 | + * We assume that extensions are 1-4 characters in length |
| 3146 | + * @param path to file, like "foo/bar/baz.jpg" |
| 3147 | + * @return extension, like ".jpg" or undefined if it doesn't look lke an extension. |
| 3148 | + */ |
| 3149 | + getExtension: function( path ) { |
| 3150 | + var extension = undefined; |
| 3151 | + var idx = path.lastIndexOf( '.' ); |
| 3152 | + if (idx > 0 && ( idx > ( path.length - 5 ) ) && ( idx < ( path.length - 1 ) ) ) { |
| 3153 | + extension = path.substr( idx + 1 ).toLowerCase(); |
| 3154 | + } |
| 3155 | + return extension; |
| 3156 | + }, |
| 3157 | + |
| 3158 | + /** |
| 3159 | + * Last resort to guess a proper extension |
| 3160 | + */ |
| 3161 | + mimetypeToExtension: { |
| 3162 | + 'image/jpeg': 'jpg', |
| 3163 | + 'image/gif': 'gif' |
| 3164 | + // fill as needed |
| 3165 | + } |
| 3166 | + |
| 3167 | + |
| 3168 | +}; |
| 3169 | + |
| 3170 | +( function( $j ) { |
| 3171 | + |
| 3172 | + $j.fn.tipsyPlus = function( optionsArg ) { |
| 3173 | + // use extend! |
| 3174 | + var titleOption = 'title'; |
| 3175 | + var htmlOption = false; |
| 3176 | + |
| 3177 | + var options = $j.extend( |
| 3178 | + { type: 'help', shadow: true }, |
| 3179 | + optionsArg |
| 3180 | + ); |
| 3181 | + |
| 3182 | + var el = this; |
| 3183 | + |
| 3184 | + if (options.plus) { |
| 3185 | + htmlOption = true; |
| 3186 | + titleOption = function() { |
| 3187 | + return $j( '<span />' ).append( |
| 3188 | + $j( this ).attr( 'original-title' ), |
| 3189 | + $j( '<a class="mwe-upwiz-tooltip-link"/>' ) |
| 3190 | + .attr( 'href', '#' ) |
| 3191 | + .append( gM( 'mwe-upwiz-tooltip-more-info' ) ) |
| 3192 | + .mouseenter( function() { |
| 3193 | + el.data('tipsy').sticky = true; |
| 3194 | + } ) |
| 3195 | + .mouseleave( function() { |
| 3196 | + el.data('tipsy').sticky = false; |
| 3197 | + } ) |
| 3198 | + .click( function() { |
| 3199 | + // show the wiki page with more |
| 3200 | + alert( options.plus ); |
| 3201 | + // pass this in as a closure to be called on dismiss |
| 3202 | + el.focus(); |
| 3203 | + el.data('tipsy').sticky = false; |
| 3204 | + } ) |
| 3205 | + ); |
| 3206 | + }; |
| 3207 | + } |
| 3208 | + |
| 3209 | + return this.tipsy( { |
| 3210 | + gravity: 'w', |
| 3211 | + trigger: 'focus', |
| 3212 | + title: titleOption, |
| 3213 | + html: htmlOption, |
| 3214 | + type: options.type, |
| 3215 | + shadow: options.shadow |
| 3216 | + } ); |
| 3217 | + |
| 3218 | + }; |
| 3219 | + |
| 3220 | + /** |
| 3221 | + * Create 'remove' control, an X which highlights in some standardized way. |
| 3222 | + */ |
| 3223 | + $j.fn.removeCtrl = function( tooltipMsgKey, callback ) { |
| 3224 | + return $j( '<div class="mwe-upwiz-remove-ctrl ui-corner-all" />' ) |
| 3225 | + .attr( 'title', gM( tooltipMsgKey ) ) |
| 3226 | + .click( callback ) |
| 3227 | + .hover( function() { $j( this ).addClass( 'hover' ); }, |
| 3228 | + function() { $j( this ).removeClass( 'hover' ); } ) |
| 3229 | + .append( $j( '<span class="ui-icon ui-icon-close" />' ) ); |
| 3230 | + }; |
| 3231 | + |
| 3232 | + /** |
| 3233 | + * Prevent the closing of a window with a confirm message (the onbeforeunload event seems to |
| 3234 | + * work in most browsers |
| 3235 | + * e.g. |
| 3236 | + * var allowCloseWindow = jQuery().preventCloseWindow( { message: "Don't go away!" } ); |
| 3237 | + * // ... do stuff that can't be interrupted ... |
| 3238 | + * allowCloseWindow(); |
| 3239 | + * |
| 3240 | + * @param options object which should have a message string, already internationalized |
| 3241 | + * @return closure execute this when you want to allow the user to close the window |
| 3242 | + */ |
| 3243 | + $j.fn.preventCloseWindow = function( options ) { |
| 3244 | + if ( typeof options === 'undefined' ) { |
| 3245 | + options = {}; |
| 3246 | + } |
| 3247 | + |
| 3248 | + if ( typeof options.message === 'undefined' ) { |
| 3249 | + options.message = 'Are you sure you want to close this window?'; |
| 3250 | + } |
| 3251 | + |
| 3252 | + $j( window ).unload( function() { |
| 3253 | + return options.message; |
| 3254 | + } ); |
| 3255 | + |
| 3256 | + return function() { |
| 3257 | + $j( window ).removeAttr( 'unload' ); |
| 3258 | + }; |
| 3259 | + |
| 3260 | + }; |
| 3261 | + |
| 3262 | + |
| 3263 | + $j.fn.notify = function ( message ) { |
| 3264 | + // could do something here with Chrome's in-browser growl-like notifications. |
| 3265 | + // play a sound? |
| 3266 | + // if the current tab does not have focus, use an alert? |
| 3267 | + // alert( message ); |
| 3268 | + }; |
| 3269 | + |
| 3270 | + $j.fn.enableNextButton = function() { |
| 3271 | + return this.find( '.mwe-upwiz-button-next' ) |
| 3272 | + .removeAttr( 'disabled' ); |
| 3273 | + // .effect( 'pulsate', { times: 3 }, 1000 ); |
| 3274 | + }; |
| 3275 | + |
| 3276 | + $j.fn.disableNextButton = function() { |
| 3277 | + return this.find( '.mwe-upwiz-button-next' ) |
| 3278 | + .attr( 'disabled', true ); |
| 3279 | + }; |
| 3280 | + |
| 3281 | + $j.fn.readonly = function() { |
| 3282 | + return this.attr( 'readonly', 'readonly' ).addClass( 'mwe-readonly' ); |
| 3283 | + }; |
| 3284 | + |
| 3285 | + /* will change in RTL, but I can't think of an easy way to do this with only CSS */ |
| 3286 | + $j.fn.requiredFieldLabel = function() { |
| 3287 | + this.addClass( 'mwe-upwiz-required-field' ); |
| 3288 | + return this.prepend( $j( '<span/>' ).append( '*' ).addClass( 'mwe-upwiz-required-marker' ) ); |
| 3289 | + }; |
| 3290 | + |
| 3291 | + /** |
| 3292 | + * Upper-case the first letter of a string. XXX move to common library |
| 3293 | + * @param string |
| 3294 | + * @return string with first letter uppercased. |
| 3295 | + */ |
| 3296 | + mw.ucfirst = function( s ) { |
| 3297 | + return s.substring(0,1).toUpperCase() + s.substr(1); |
| 3298 | + }; |
| 3299 | + |
| 3300 | + |
| 3301 | + |
| 3302 | + /** |
| 3303 | + * jQuery extension. Makes a textarea automatically grow if you enter overflow |
| 3304 | + * (This feature was in the old Commons interface with a confusing arrow icon; it's nicer to make it automatic.) |
| 3305 | + */ |
| 3306 | + jQuery.fn.growTextArea = function( options ) { |
| 3307 | + |
| 3308 | + // this is a jquery-style object |
| 3309 | + |
| 3310 | + // in MSIE, this makes it possible to know what scrollheight is |
| 3311 | + // Technically this means text could now dangle over the edge, |
| 3312 | + // but it shouldn't because it will always grow to accomodate very quickly. |
| 3313 | + |
| 3314 | + if ($j.msie) { |
| 3315 | + this.each( function(i, textArea) { |
| 3316 | + textArea.style.overflow = 'visible'; |
| 3317 | + } ); |
| 3318 | + } |
| 3319 | + |
| 3320 | + var resizeIfNeeded = function() { |
| 3321 | + // this is the dom element |
| 3322 | + // is there a better way to do this? |
| 3323 | + if (this.scrollHeight >= this.offsetHeight) { |
| 3324 | + this.rows++; |
| 3325 | + while (this.scrollHeight > this.offsetHeight) { |
| 3326 | + this.rows++; |
| 3327 | + } |
| 3328 | + } |
| 3329 | + return this; |
| 3330 | + }; |
| 3331 | + |
| 3332 | + this.addClass( 'mwe-grow-textarea' ); |
| 3333 | + |
| 3334 | + this.bind( 'resizeEvent', resizeIfNeeded ); |
| 3335 | + |
| 3336 | + this.keyup( resizeIfNeeded ); |
| 3337 | + this.change( resizeIfNeeded ); |
| 3338 | + |
| 3339 | + |
| 3340 | + return this; |
| 3341 | + }; |
| 3342 | + |
| 3343 | + jQuery.fn.mask = function( options ) { |
| 3344 | + |
| 3345 | + // intercept clicks... |
| 3346 | + // Note: the size of the div must be obtainable. Hence, this cannot be a div without layout (e.g. display:none). |
| 3347 | + // some of this is borrowed from http://code.google.com/p/jquery-loadmask/ , but simplified |
| 3348 | + $j.each( this, function( i, el ) { |
| 3349 | + |
| 3350 | + if ( ! $j( el ).data( 'mask' ) ) { |
| 3351 | + |
| 3352 | + |
| 3353 | + //fix for z-index bug with selects in IE6 |
| 3354 | + if ( $j.browser.msie && $j.browser.version.substring(0,1) === '6' ){ |
| 3355 | + el.find( "select" ).addClass( "masked-hidden" ); |
| 3356 | + } |
| 3357 | + |
| 3358 | + var mask = $j( '<div />' ) |
| 3359 | + .css( { 'position' : 'absolute', |
| 3360 | + 'top' : '0px', |
| 3361 | + 'left' : '0px', |
| 3362 | + 'width' : el.offsetWidth + 'px', |
| 3363 | + 'height' : el.offsetHeight + 'px', |
| 3364 | + 'z-index' : 100 } ) |
| 3365 | + .click( function( e ) { e.stopPropagation(); } ); |
| 3366 | + |
| 3367 | + $j( el ).css( { 'position' : 'relative' } ) |
| 3368 | + .fadeTo( 'fast', 0.5 ) |
| 3369 | + .append( mask ) |
| 3370 | + .data( 'mask', mask ); |
| 3371 | + |
| 3372 | + //auto height fix for IE -- not sure about this, i think offsetWidth + Height is a better solution. Test! |
| 3373 | + /* |
| 3374 | + if( $j.browser.msie ) { |
| 3375 | + mask.height(el.height() + parseInt(el.css("padding-top")) + parseInt(el.css("padding-bottom"))); |
| 3376 | + mask.width(el.width() + parseInt(el.css("padding-left")) + parseInt(el.css("padding-right"))); |
| 3377 | + } |
| 3378 | + */ |
| 3379 | + |
| 3380 | + } |
| 3381 | + |
| 3382 | + // XXX bind to a custom event in case the div size changes : ? |
| 3383 | + |
| 3384 | + } ); |
| 3385 | + |
| 3386 | + return this; |
| 3387 | + |
| 3388 | + }; |
| 3389 | + |
| 3390 | + jQuery.fn.unmask = function( options ) { |
| 3391 | + |
| 3392 | + $j.each( this, function( i, el ) { |
| 3393 | + if ( $j( el ).data( 'mask' ) ) { |
| 3394 | + var mask = $j( el ).data( 'mask' ); |
| 3395 | + $j( el ).removeData( 'mask' ); // from the data |
| 3396 | + mask.remove(); // from the DOM |
| 3397 | + $j( el ).fadeTo( 'fast', 1.0 ); |
| 3398 | + } |
| 3399 | + } ); |
| 3400 | + |
| 3401 | + |
| 3402 | + return this; |
| 3403 | + }; |
| 3404 | + |
| 3405 | + |
| 3406 | + /** |
| 3407 | + * Safe hide and show |
| 3408 | + * Rather than use display: none, this collapses the divs to zero height |
| 3409 | + * This is good because then the elements in the divs still have layout and we can do things like mask and unmask (above) |
| 3410 | + * XXX may be obsolete as we are not really doing this any more |
| 3411 | + * disable form fields so we do not tab through them when hidden |
| 3412 | + * XXX for some reason the disabling doesn't work with the date field. |
| 3413 | + */ |
| 3414 | + |
| 3415 | + jQuery.fn.maskSafeHide = function( options ) { |
| 3416 | + $j.each( this.find( ':enabled' ), function(i, input) { |
| 3417 | + $j( input ).data( 'wasEnabled', true ) |
| 3418 | + .attr( 'disabled', 'disabled' ); |
| 3419 | + } ); |
| 3420 | + return this.css( { 'height' : '0px', 'overflow' : 'hidden' } ); |
| 3421 | + }; |
| 3422 | + |
| 3423 | + // may be causing scrollbar to appear when div changes size |
| 3424 | + // re-enable form fields (disabled so we did not tab through them when hidden) |
| 3425 | + jQuery.fn.maskSafeShow = function( options ) { |
| 3426 | + $j.each( this.find( ':disabled' ), function (i, input) { |
| 3427 | + if ($j( input ).data( 'wasEnabled' )) { |
| 3428 | + $j( input ).removeAttr( 'disabled' ) |
| 3429 | + .removeData( 'wasEnabled' ); |
| 3430 | + } |
| 3431 | + } ); |
| 3432 | + return this.css( { 'height' : 'auto', 'overflow' : 'visible' } ); |
| 3433 | + }; |
| 3434 | + |
| 3435 | + $j.validator.setDefaults( { |
| 3436 | + debug: true, |
| 3437 | + errorClass: 'mwe-validator-error' |
| 3438 | + } ); |
| 3439 | + |
| 3440 | +} )( jQuery ); |
Property changes on: trunk/extensions/UploadWizard/resources/mw.UploadWizard.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 3441 | + native |
Index: trunk/extensions/UploadWizard/resources/mw.DestinationChecker.js |
— | — | @@ -0,0 +1,211 @@ |
| 2 | +/** |
| 3 | + * Object to attach to a file name input, to be run on its change() event |
| 4 | + * Largely derived from wgUploadWarningObj in old upload.js |
| 5 | + * Perhaps this could be a jQuery ext |
| 6 | + * @param options dictionary of options |
| 7 | + * selector required, the selector for the input to check |
| 8 | + * processResult required, function to execute on results. accepts two args: |
| 9 | + * 1) filename that invoked this request -- should check if this is still current filename |
| 10 | + * 2) an object with the following fields |
| 11 | + * isUnique: boolean |
| 12 | + * img: thumbnail image src (if not unique) |
| 13 | + * href: the url of the full image (if not unique) |
| 14 | + * title: normalized title of file (if not unique) |
| 15 | + * spinner required, closure to execute to show progress: accepts true to start, false to stop |
| 16 | + * apiUrl optional url to call for api. falls back to local api url |
| 17 | + * delay optional how long to delay after a change in ms. falls back to configured default |
| 18 | + * preprocess optional: function to apply to the contents of selector before testing |
| 19 | + * events what events on the input trigger a check. |
| 20 | + */ |
| 21 | +mw.DestinationChecker = function( options ) { |
| 22 | + |
| 23 | + var _this = this; |
| 24 | + _this.selector = options.selector; |
| 25 | + _this.spinner = options.spinner; |
| 26 | + _this.processResult = options.processResult; |
| 27 | + |
| 28 | + // optional overrides |
| 29 | + |
| 30 | + if (options.apiUrl) { |
| 31 | + _this.apiUrl = options.apiUrl; |
| 32 | + } else { |
| 33 | + _this.apiUrl = mw.UploadWizard.config[ 'apiUrl' ]; |
| 34 | + } |
| 35 | + |
| 36 | + $j.each( ['preprocess', 'delay', 'events'], function( i, option ) { |
| 37 | + if ( options[option] ) { |
| 38 | + _this[option] = options[option]; |
| 39 | + } |
| 40 | + } ); |
| 41 | + |
| 42 | + |
| 43 | + // initialize! |
| 44 | + |
| 45 | + var check = _this.getDelayedChecker(); |
| 46 | + |
| 47 | + $j.each( _this.events, function(i, eventName) { |
| 48 | + $j( _this.selector )[eventName]( check ); |
| 49 | + } ); |
| 50 | + |
| 51 | +}; |
| 52 | + |
| 53 | +mw.DestinationChecker.prototype = { |
| 54 | + |
| 55 | + // events that the input undergoes which fire off a check |
| 56 | + events: [ 'change', 'keyup' ], |
| 57 | + |
| 58 | + // how long the input muse be "idle" before doing call (don't want to check on each key press) |
| 59 | + delay: 500, // ms; |
| 60 | + |
| 61 | + // what tracks the wait |
| 62 | + timeoutId: null, |
| 63 | + |
| 64 | + // cached results from api calls |
| 65 | + cachedResult: {}, |
| 66 | + |
| 67 | + /** |
| 68 | + * There is an option to preprocess the name (in order to perhaps convert it from |
| 69 | + * title to path, e.g. spaces to underscores, or to add the "File:" part.) Depends on |
| 70 | + * exactly what your input field represents. |
| 71 | + * In the event that the invoker doesn't supply a name preprocessor, use this identity function |
| 72 | + * as default |
| 73 | + * |
| 74 | + * @param something |
| 75 | + * @return that same thing |
| 76 | + */ |
| 77 | + preprocess: function(x) { return x; }, |
| 78 | + |
| 79 | + /** |
| 80 | + * fire when the input changes value or keypress |
| 81 | + * will trigger a check of the name if the field has been idle for delay ms. |
| 82 | + */ |
| 83 | + getDelayedChecker: function() { |
| 84 | + var checker = this; |
| 85 | + return function() { |
| 86 | + var el = this; // but we don't use it, since we already have it in _this.selector |
| 87 | + |
| 88 | + // if we changed before the old timeout ran, clear that timeout. |
| 89 | + if ( checker.timeoutId ) { |
| 90 | + window.clearTimeout( checker.timeoutId ); |
| 91 | + } |
| 92 | + |
| 93 | + // and start another, hoping this time we'll be idle for delay ms. |
| 94 | + checker.timeoutId = window.setTimeout( |
| 95 | + function() { checker.checkUnique(); }, |
| 96 | + checker.delay |
| 97 | + ); |
| 98 | + }; |
| 99 | + }, |
| 100 | + |
| 101 | + /** |
| 102 | + * Get the current value of the input, with optional preprocessing |
| 103 | + * @return the current input value, with optional processing |
| 104 | + */ |
| 105 | + getName: function() { |
| 106 | + var _this = this; |
| 107 | + return _this.preprocess( $j( _this.selector ).val() ); |
| 108 | + }, |
| 109 | + |
| 110 | + /** |
| 111 | + * Async check if a filename is unique. Can be attached to a field's change() event |
| 112 | + * This is a more abstract version of AddMedia/UploadHandler.js::doDestCheck |
| 113 | + */ |
| 114 | + checkUnique: function() { |
| 115 | + var _this = this; |
| 116 | + |
| 117 | + var found = false; |
| 118 | + var name = _this.getName(); |
| 119 | + |
| 120 | + if ( _this.cachedResult[name] !== undefined ) { |
| 121 | + _this.processResult( _this.cachedResult[name] ); |
| 122 | + return; |
| 123 | + } |
| 124 | + |
| 125 | + // set the spinner to spin |
| 126 | + _this.spinner( true ); |
| 127 | + |
| 128 | + // Setup the request -- will return thumbnail data if it finds one |
| 129 | + var request = { |
| 130 | + 'titles': 'File:' + name, |
| 131 | + 'prop': 'imageinfo', |
| 132 | + 'iiprop': 'url|mime|size', |
| 133 | + 'iiurlwidth': 150 |
| 134 | + }; |
| 135 | + |
| 136 | + // Do the destination check |
| 137 | + mw.getJSON( _this.apiUrl, request, function( data ) { |
| 138 | + // Remove spinner |
| 139 | + _this.spinner( false ); |
| 140 | + |
| 141 | + // if the name's changed in the meantime, our result is useless |
| 142 | + if ( name != _this.getName() ) { |
| 143 | + return; |
| 144 | + } |
| 145 | + |
| 146 | + if ( !data || !data.query || !data.query.pages ) { |
| 147 | + // Ignore a null result |
| 148 | + mw.log(" No data in checkUnique result"); |
| 149 | + return; |
| 150 | + } |
| 151 | + |
| 152 | + var result = undefined; |
| 153 | + |
| 154 | + if ( data.query.pages[-1] ) { |
| 155 | + // No conflict found; this file name is unique |
| 156 | + mw.log(" No pages in checkUnique result"); |
| 157 | + result = { isUnique: true }; |
| 158 | + |
| 159 | + } else { |
| 160 | + |
| 161 | + for ( var page_id in data.query.pages ) { |
| 162 | + if ( !data.query.pages[ page_id ].imageinfo ) { |
| 163 | + continue; |
| 164 | + } |
| 165 | + |
| 166 | + // Conflict found, this filename is NOT unique |
| 167 | + mw.log( " conflict! " ); |
| 168 | + |
| 169 | + var ntitle; |
| 170 | + if ( data.query.normalized ) { |
| 171 | + ntitle = data.query.normalized[0].to; |
| 172 | + } else { |
| 173 | + ntitle = data.query.pages[ page_id ].title; |
| 174 | + } |
| 175 | + |
| 176 | + var img = data.query.pages[ page_id ].imageinfo[0]; |
| 177 | + |
| 178 | + result = { |
| 179 | + isUnique: false, |
| 180 | + img: img, |
| 181 | + title: ntitle, |
| 182 | + href : img.descriptionurl |
| 183 | + }; |
| 184 | + |
| 185 | + break; |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + if ( result !== undefined ) { |
| 190 | + _this.cachedResult[name] = result; |
| 191 | + _this.processResult( result ); |
| 192 | + } |
| 193 | + |
| 194 | + } ); |
| 195 | + } |
| 196 | + |
| 197 | +}; |
| 198 | + |
| 199 | + |
| 200 | +/** |
| 201 | + * jQuery extension to make a field upload-checkable |
| 202 | + */ |
| 203 | +( function ( $ ) { |
| 204 | + $.fn.destinationChecked = function( options ) { |
| 205 | + var _this = this; |
| 206 | + options.selector = _this; |
| 207 | + var checker = new mw.DestinationChecker( options ); |
| 208 | + // this should really be done with triggers |
| 209 | + _this.checkUnique = function() { checker.checkUnique(); }; |
| 210 | + return _this; |
| 211 | + }; |
| 212 | +} )( jQuery ); |
Property changes on: trunk/extensions/UploadWizard/resources/mw.DestinationChecker.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 213 | + native |
Index: trunk/extensions/UploadWizard/resources/toggle-open.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Property changes on: trunk/extensions/UploadWizard/resources/toggle-open.png |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 214 | + image/png |
Index: trunk/extensions/UploadWizard/resources/arrow-tail.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Property changes on: trunk/extensions/UploadWizard/resources/arrow-tail.png |
___________________________________________________________________ |
Added: svn:mime-type |
3 | 215 | + image/png |
Index: trunk/extensions/UploadWizard/resources/toggle.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Property changes on: trunk/extensions/UploadWizard/resources/toggle.png |
___________________________________________________________________ |
Added: svn:mime-type |
4 | 216 | + image/png |
Index: trunk/extensions/UploadWizard/resources/mw.Utilities.js |
— | — | @@ -0,0 +1,26 @@ |
| 2 | +/** |
| 3 | +* Check if an object is empty or if its an empty string. |
| 4 | +* |
| 5 | +* @param {Object} object Object to be checked |
| 6 | +*/ |
| 7 | +mw.isEmpty = function( object ) { |
| 8 | + if( typeof object == 'string' ) { |
| 9 | + if( object == '' ) return true; |
| 10 | + // Non empty string: |
| 11 | + return false; |
| 12 | + } |
| 13 | + |
| 14 | + // If an array check length: |
| 15 | + if( Object.prototype.toString.call( object ) === "[object Array]" |
| 16 | + && object.length == 0 ) { |
| 17 | + return true; |
| 18 | + } |
| 19 | + |
| 20 | + // Else check as an object: |
| 21 | + for( var i in object ) { return false; } |
| 22 | + |
| 23 | + // Else object is empty: |
| 24 | + return true; |
| 25 | +} |
| 26 | + |
| 27 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.Utilities.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 28 | + native |
Index: trunk/extensions/UploadWizard/resources/mw.ApiUploadHandler.js |
— | — | @@ -0,0 +1,100 @@ |
| 2 | +/** |
| 3 | + * An attempt to refactor out the stuff that does API-via-iframe transport |
| 4 | + * In the hopes that this will eventually work for AddMediaWizard too |
| 5 | + */ |
| 6 | + |
| 7 | +// n.b. if there are message strings, or any assumption about HTML structure of the form. |
| 8 | +// then we probably did it wrong |
| 9 | + |
| 10 | +/** |
| 11 | + * Represents an object which configures a form to upload its files via an iframe talking to the MediaWiki API. |
| 12 | + * @param an UploadInterface object, which contains a .form property which points to a real HTML form in the DOM |
| 13 | + */ |
| 14 | +mw.ApiUploadHandler = function( upload ) { |
| 15 | + var _this = this; |
| 16 | + _this.upload = upload; |
| 17 | + |
| 18 | + _this.configureForm(); |
| 19 | + |
| 20 | + // hardcoded for now |
| 21 | + // can also use Xhr Binary depending on config |
| 22 | + _this.transport = new mw.IframeTransport( |
| 23 | + _this.upload.ui.form, |
| 24 | + function( fraction ){ _this.upload.setTransportProgress( fraction ); }, |
| 25 | + function( result ) { _this.upload.setTransported( result ); } |
| 26 | + ); |
| 27 | + |
| 28 | +}; |
| 29 | + |
| 30 | +mw.ApiUploadHandler.prototype = { |
| 31 | + /** |
| 32 | + * Configure an HTML form so that it will submit its files to our transport (an iframe) |
| 33 | + * with proper params for the API |
| 34 | + * @param callback |
| 35 | + */ |
| 36 | + configureForm: function() { |
| 37 | + var apiUrl = mw.UploadWizard.config[ 'apiUrl' ]; // XXX or? throw new Error( "configuration", "no API url" ); |
| 38 | + if ( ! ( mw.UploadWizard.config[ 'token' ] ) ) { |
| 39 | + throw new Error( "configuration", "no edit token" ); |
| 40 | + } |
| 41 | + |
| 42 | + var _this = this; |
| 43 | + mw.log( "configuring form for Upload API" ); |
| 44 | + |
| 45 | + // Set the form action |
| 46 | + try { |
| 47 | + $j( _this.upload.ui.form ) |
| 48 | + .attr( 'action', apiUrl ) |
| 49 | + .attr( 'method', 'POST' ) |
| 50 | + .attr( 'enctype', 'multipart/form-data' ); |
| 51 | + } catch ( e ) { |
| 52 | + alert( "oops, form modification didn't work in ApiUploadHandler" ); |
| 53 | + mw.log( "IE for some reason error's out when you change the action" ); |
| 54 | + // well, if IE fucks this up perhaps we should do something to make sure it writes correctly |
| 55 | + // from the outset? |
| 56 | + } |
| 57 | + |
| 58 | + _this.addFormInputIfMissing( 'token', mw.UploadWizard.config[ 'token' ]); |
| 59 | + _this.addFormInputIfMissing( 'action', 'upload' ); |
| 60 | + _this.addFormInputIfMissing( 'format', 'jsonfm' ); |
| 61 | + |
| 62 | + // XXX only for testing, so it stops complaining about dupes |
| 63 | + if ( mw.UploadWizard.config[ 'debug' ]) { |
| 64 | + _this.addFormInputIfMissing( 'ignorewarnings', '1' ); |
| 65 | + } |
| 66 | + }, |
| 67 | + |
| 68 | + /** |
| 69 | + * Add a hidden input to a form if it was not already there. |
| 70 | + * @param name the name of the input |
| 71 | + * @param value the value of the input |
| 72 | + */ |
| 73 | + addFormInputIfMissing: function( name, value ) { |
| 74 | + var _this = this; |
| 75 | + var $jForm = $j( _this.upload.ui.form ); |
| 76 | + if ( $jForm.find( "[name='" + name + "']" ).length === 0 ) { |
| 77 | + $jForm.append( |
| 78 | + $j( '<input />' ) |
| 79 | + .attr( { |
| 80 | + 'type': "hidden", |
| 81 | + 'name' : name, |
| 82 | + 'value' : value |
| 83 | + } ) |
| 84 | + ); |
| 85 | + } |
| 86 | + }, |
| 87 | + |
| 88 | + /** |
| 89 | + * Kick off the upload! |
| 90 | + */ |
| 91 | + start: function() { |
| 92 | + var _this = this; |
| 93 | + mw.log( "api: upload start!" ); |
| 94 | + _this.beginTime = ( new Date() ).getTime(); |
| 95 | + _this.upload.ui.busy(); |
| 96 | + $j( this.upload.ui.form ).submit(); |
| 97 | + } |
| 98 | +}; |
| 99 | + |
| 100 | + |
| 101 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.ApiUploadHandler.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 102 | + native |
Index: trunk/extensions/UploadWizard/resources/mw.UploadApiProcessor.js |
— | — | @@ -0,0 +1,296 @@ |
| 2 | +// might be useful to raise exceptions when the api goes wrong |
| 3 | +// however, when everything is async, then who receives them? |
| 4 | +mw.UploadApiProcessor = function(doneCb, errorCb) { |
| 5 | + var _this = this; |
| 6 | + _this.doneCb = doneCb; |
| 7 | + _this.errorCb = errorCb; |
| 8 | +}; |
| 9 | + |
| 10 | + |
| 11 | +mw.UploadApiProcessor.prototype = { |
| 12 | + |
| 13 | + warnings: [ |
| 14 | + 'badfilename', // was the resultant filename different from desired? If so return what we actually got in the 'badfilename' warnings |
| 15 | + 'filetype-unwanted-type', // bad filetype, as determined from extenstion. content is the bad extension |
| 16 | + 'large-file', // $wgUploadSizeWarning, numeric value of largest size (bytes, kb, what?) |
| 17 | + 'emptyfile', // set to "true" if file was empty |
| 18 | + 'exists', // set to true if file by that name already existed |
| 19 | + 'duplicate', // hash collision found |
| 20 | + 'duplicate-archive' // hash collision found in archives |
| 21 | + ], |
| 22 | + |
| 23 | + errors: [ |
| 24 | + 'empty-file', |
| 25 | + 'filetype-missing', //(missing an extension) |
| 26 | + 'filetype-banned', // (extension banned) // also returns: { filetype => the filetype we thought it was, allowed => [ extensions ] } |
| 27 | + 'filename-tooshort', |
| 28 | + 'illegal-filename', // { filename => verification[filtered] } |
| 29 | + 'overwrite', // overwrite existing file (failed?) |
| 30 | + 'verification-error', // {details => verification-details} |
| 31 | + 'hookaborted', // error => verificationerror } |
| 32 | + 'unknown_error' |
| 33 | + ], |
| 34 | + |
| 35 | + |
| 36 | + // NB |
| 37 | + // It's not clear if we can even get these errors (error_msg_key, error_onlykey) any more |
| 38 | + |
| 39 | + // There are many possible error messages here, so we don't load all |
| 40 | + // message text in advance, instead we use mw.getRemoteMsg() for some. |
| 41 | + // |
| 42 | + // This code is similar to the error handling code formerly in |
| 43 | + // SpecialUpload::processUpload() |
| 44 | + error_msg_key: { |
| 45 | + '2' : 'largefileserver', |
| 46 | + '3' : 'emptyfile', |
| 47 | + '4' : 'minlength1', |
| 48 | + '5' : 'illegalfilename' |
| 49 | + }, |
| 50 | + |
| 51 | + // NOTE:: handle these error types |
| 52 | + error_onlykey: { |
| 53 | + '1': 'BEFORE_PROCESSING', |
| 54 | + '6': 'PROTECTED_PAGE', |
| 55 | + '7': 'OVERWRITE_EXISTING_FILE', |
| 56 | + '8': 'FILETYPE_MISSING', |
| 57 | + '9': 'FILETYPE_BADTYPE', |
| 58 | + '10': 'VERIFICATION_ERROR', |
| 59 | + '11': 'UPLOAD_VERIFICATION_ERROR', |
| 60 | + '12': 'UPLOAD_WARNING', |
| 61 | + '13': 'INTERNAL_ERROR', |
| 62 | + '14': 'MIN_LENGTH_PARTNAME' |
| 63 | + }, |
| 64 | + |
| 65 | + |
| 66 | + /** |
| 67 | + * Process the result of an action=upload API request, into a useful |
| 68 | + * data structure. |
| 69 | + * upload, |
| 70 | + * errors, |
| 71 | + * warnings |
| 72 | + * Augment with error messages in the local language if possible |
| 73 | + * |
| 74 | + */ |
| 75 | + processResult: function( result ) { |
| 76 | + var _this = this; |
| 77 | + mw.log( 'processResult::' ); |
| 78 | + |
| 79 | + // debugger; |
| 80 | + |
| 81 | + var parsedResult = _this.parseResult(result); |
| 82 | + |
| 83 | + if ( _this.doneCb && typeof _this.doneCb == 'function' ) { |
| 84 | + mw.log( "call doneCb" ); |
| 85 | + _this.doneCb( parsedResult ); |
| 86 | + |
| 87 | + } |
| 88 | + return true; |
| 89 | + }, |
| 90 | + |
| 91 | + |
| 92 | + parseResult: function( result ) { |
| 93 | + if ( result.upload && result.upload.imageinfo && result.upload.imageinfo.descriptionurl ) { |
| 94 | + result.isSuccess = true; |
| 95 | + } |
| 96 | + |
| 97 | + return result; |
| 98 | + |
| 99 | + } |
| 100 | + |
| 101 | + |
| 102 | +}; |
| 103 | + |
| 104 | + |
| 105 | + if ( result.error || ( result.upload && result.upload.result == "Failure" ) ) { |
| 106 | + |
| 107 | + // Check a few places for the error code |
| 108 | + var error_code = 0; |
| 109 | + var errorReplaceArg = ''; |
| 110 | + if ( result.error && result.error.code ) { |
| 111 | + error_code = result.error.code; |
| 112 | + } else if ( result.upload.code ) { |
| 113 | + if ( typeof result.upload.code == 'object' ) { |
| 114 | + if ( result.upload.code[0] ) { |
| 115 | + error_code = result.upload.code[0]; |
| 116 | + } |
| 117 | + if ( result.upload.code['status'] ) { |
| 118 | + error_code = result.upload.code['status']; |
| 119 | + if ( result.upload.code['filtered'] ) { |
| 120 | + errorReplaceArg = result.upload.code['filtered']; |
| 121 | + } |
| 122 | + } |
| 123 | + } else { |
| 124 | + result.upload.code; // XXX ?? |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + var error_msg = ''; |
| 129 | + if ( typeof result.error == 'string' ) { |
| 130 | + error_msg = result.error; |
| 131 | + } |
| 132 | + |
| 133 | + if ( !error_code || error_code == 'unknown-error' ) { |
| 134 | + if ( typeof JSON != 'undefined' ) { |
| 135 | + mw.log( 'Error: result: ' + JSON.stringify( result ) ); |
| 136 | + } |
| 137 | + if ( result.upload.error == 'internal-error' ) { |
| 138 | + // Do a remote message load |
| 139 | + errorKey = result.upload.details[0]; |
| 140 | + |
| 141 | + mw.getRemoteMsg( errorKey, function() { |
| 142 | + _this.ui.setPrompt( gM( 'mwe-uploaderror' ), gM( errorKey ), buttons ); |
| 143 | + |
| 144 | + }); |
| 145 | + return false; |
| 146 | + } |
| 147 | + |
| 148 | + _this.ui.setPrompt( |
| 149 | + gM('mwe-uploaderror'), |
| 150 | + gM('mwe-unknown-error') + '<br>' + error_msg, |
| 151 | + buttons ); |
| 152 | + return false; |
| 153 | + } |
| 154 | + |
| 155 | + if ( result.error && result.error.info ) { |
| 156 | + _this.ui.setPrompt( gM( 'mwe-uploaderror' ), result.error.info, buttons ); |
| 157 | + return false; |
| 158 | + } |
| 159 | + |
| 160 | + if ( typeof error_code == 'number' |
| 161 | + && typeof _this.error_msg_key[error_code] == 'undefined' ) |
| 162 | + { |
| 163 | + if ( result.upload.code.finalExt ) { |
| 164 | + _this.ui.setPrompt( |
| 165 | + gM( 'mwe-uploaderror' ), |
| 166 | + gM( 'mwe-wgfogg_warning_bad_extension', result.upload.code.finalExt ), |
| 167 | + buttons ); |
| 168 | + } else { |
| 169 | + _this.ui.setPrompt( |
| 170 | + gM( 'mwe-uploaderror' ), |
| 171 | + gM( 'mwe-unknown-error' ) + ' : ' + error_code, |
| 172 | + buttons ); |
| 173 | + } |
| 174 | + return false; |
| 175 | + } |
| 176 | + |
| 177 | + mw.log( 'get key: ' + _this.error_msg_key[ error_code ] ) |
| 178 | + mw.getRemoteMsg( _this.error_msg_key[ error_code ], function() { |
| 179 | + _this.ui.setPrompt( |
| 180 | + gM( 'mwe-uploaderror' ), |
| 181 | + gM( _this.error_msg_key[ error_code ], errorReplaceArg ), |
| 182 | + buttons ); |
| 183 | + }); |
| 184 | + mw.log( "api.error" ); |
| 185 | + return false; |
| 186 | + } |
| 187 | + |
| 188 | + |
| 189 | + } |
| 190 | + */ |
| 191 | + |
| 192 | +/* |
| 193 | + // this doesn't seem to do anything |
| 194 | + // Check for warnings: |
| 195 | + if ( result.upload && result.upload.warnings ) { |
| 196 | + for ( var wtype in result.upload.warnings ) { |
| 197 | + var winfo = result.upload.warnings[wtype] |
| 198 | + switch ( wtype ) { |
| 199 | + case 'duplicate': |
| 200 | + case 'exists': |
| 201 | + if ( winfo[1] && winfo[1].title && winfo[1].title.mTextform ) { |
| 202 | + push warnings, { type: wtype, text: winfo[1].title.mTextform }, |
| 203 | + } else { |
| 204 | + push warnings, { type: wtype } |
| 205 | + } |
| 206 | + break; |
| 207 | + case 'file-thumbnail-no': |
| 208 | + push warnings, { type: wtype, info: winfo } |
| 209 | + break; |
| 210 | + default: |
| 211 | + push warnings: { type: wtype, } |
| 212 | + break; |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + if ( result.upload.sessionkey ) { |
| 217 | + _this.warnings_sessionkey = result.upload.sessionkey; |
| 218 | + } |
| 219 | + |
| 220 | + } |
| 221 | +*/ |
| 222 | +/* |
| 223 | + // Check upload.error |
| 224 | + if ( result.upload && result.upload.error ) { |
| 225 | + mw.log( ' result.upload.error: ' + result.upload.error ); |
| 226 | + _this.ui.setPrompt( |
| 227 | + gM( 'mwe-uploaderror' ), |
| 228 | + gM( 'mwe-unknown-error' ) + '<br>', |
| 229 | + buttons ); |
| 230 | + return false; |
| 231 | + } |
| 232 | +*/ |
| 233 | + |
| 234 | + /* |
| 235 | + // this ONLY applies to copy by URL method -- factor out? |
| 236 | + if ( result.upload && result.upload.upload_session_key ) { |
| 237 | + // Async upload, do AJAX status polling |
| 238 | + _this.upload_session_key = result.upload.upload_session_key; |
| 239 | + _this.doAjaxUploadStatus(); |
| 240 | + mw.log( "set upload_session_key: " + _this.upload_session_key ); |
| 241 | + return; |
| 242 | + } |
| 243 | + */ |
| 244 | + |
| 245 | + /* |
| 246 | + var buttons = {}; |
| 247 | + // "Return" button |
| 248 | + buttons[ gM( 'mwe-return-to-form' ) ] = function() { |
| 249 | + $j( this ).dialog( 'destroy' ).remove(); |
| 250 | + _this.form_post_override = false; |
| 251 | + } |
| 252 | + // "Go to resource" button |
| 253 | + buttons[ gM('mwe-go-to-resource') ] = function() { |
| 254 | + window.location = url; |
| 255 | + }; |
| 256 | + _this.action_done = true; |
| 257 | + _this.interface.setPrompt( |
| 258 | + gM( 'mwe-successfulupload' ), |
| 259 | + gM( 'mwe-upload_done', url), |
| 260 | + buttons ); |
| 261 | + mw.log( 'result.upload.imageinfo::' + url ); |
| 262 | + return true; |
| 263 | + */ |
| 264 | + |
| 265 | +/* |
| 266 | + // Create the "ignore warning" button |
| 267 | + var buttons = {}; |
| 268 | + buttons[ gM( 'mwe-ignorewarning' ) ] = function() { |
| 269 | + // Check if we have a stashed key: |
| 270 | + if ( _this.warnings_sessionkey ) { |
| 271 | + //set to "loading" |
| 272 | + $j( '#upProgressDialog' ).loadingSpinner() ); |
| 273 | + //setup request: |
| 274 | + var request = { |
| 275 | + 'action': 'upload', |
| 276 | + 'sessionkey': _this.warnings_sessionkey, |
| 277 | + 'ignorewarnings': 1, |
| 278 | + 'filename': $j( '#wpDestFile' ).val(), |
| 279 | + 'token' : _this.editToken, |
| 280 | + 'comment' : _this.getUploadDescription() |
| 281 | + }; |
| 282 | + //run the upload from stash request |
| 283 | + mw.getJSON(_this.api_url, request, function( data ) { |
| 284 | + _this.processApiResult( data ); |
| 285 | + } ); |
| 286 | + } else { |
| 287 | + mw.log( 'No session key re-sending upload' ) |
| 288 | + |
| 289 | + |
| 290 | + //do a stashed upload |
| 291 | + $j( '#wpIgnoreWarning' ).attr( 'checked', true ); |
| 292 | + $j( _this.editForm ).submit(); |
| 293 | + } |
| 294 | + }; |
| 295 | + */ |
| 296 | + |
| 297 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.UploadApiProcessor.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 298 | + native |
Index: trunk/extensions/UploadWizard/resources/arrow-head.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Property changes on: trunk/extensions/UploadWizard/resources/arrow-head.png |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 299 | + image/png |
Index: trunk/extensions/UploadWizard/resources/mw.IframeTransport.js |
— | — | @@ -0,0 +1,108 @@ |
| 2 | +/** |
| 3 | + * Represents a "transport" for files to upload; in this case an iframe. |
| 4 | + * The iframe is made to be the target of a form so that the existing page does not reload, even though it's a POST. |
| 5 | + * @param form an HTML form |
| 6 | + * @param progressCb callback to execute when we've started. (does not do float here because iframes can't |
| 7 | + * monitor fractional progress). |
| 8 | + * @param transportedCb callback to execute when we've finished the upload |
| 9 | + */ |
| 10 | +mw.IframeTransport = function( form, progressCb, transportedCb ) { |
| 11 | + var _this = this; |
| 12 | + |
| 13 | + _this.form = form; |
| 14 | + _this.progressCb = progressCb; |
| 15 | + _this.transportedCb = transportedCb; |
| 16 | + |
| 17 | + _this.iframeId = 'f_' + ( $j( 'iframe' ).length + 1 ); |
| 18 | + |
| 19 | + //IE only works if you "create element with the name" ( not jquery style ) |
| 20 | + var iframe; |
| 21 | + try { |
| 22 | + iframe = document.createElement( '<iframe name="' + _this.iframeId + '">' ); |
| 23 | + } catch ( ex ) { |
| 24 | + iframe = document.createElement( 'iframe' ); |
| 25 | + } |
| 26 | + |
| 27 | + // we configure form on load, because the first time it loads, it's blank |
| 28 | + // then we configure it to deal with an API submission |
| 29 | + $j( iframe ) |
| 30 | + .attr( { 'src' : 'javascript:false;', |
| 31 | + 'id' : _this.iframeId, |
| 32 | + 'name' : _this.iframeId } ) |
| 33 | + .load( function() { _this.configureForm(); } ) |
| 34 | + .css( 'display', 'none' ); |
| 35 | + |
| 36 | + $j( "body" ).append( iframe ); |
| 37 | +}; |
| 38 | + |
| 39 | +mw.IframeTransport.prototype = { |
| 40 | + /** |
| 41 | + * Configure a form with a File Input so that it submits to the iframe |
| 42 | + * Ensure callback on completion of upload |
| 43 | + */ |
| 44 | + configureForm: function() { |
| 45 | + mw.log( "configuring form for iframe transport" ); |
| 46 | + var _this = this; |
| 47 | + // Set the form target to the iframe |
| 48 | + var $jForm = $j( _this.form ); |
| 49 | + $jForm.attr( 'target', _this.iframeId ); |
| 50 | + |
| 51 | + // attach an additional handler to the form, so, when submitted, it starts showing the progress |
| 52 | + // XXX this is lame .. there should be a generic way to indicate busy status... |
| 53 | + $jForm.submit( function() { |
| 54 | + mw.log( "submitting to iframe..." ); |
| 55 | + return true; |
| 56 | + } ); |
| 57 | + |
| 58 | + // Set up the completion callback |
| 59 | + $j( '#' + _this.iframeId ).load( function() { |
| 60 | + mw.log( "received result in iframe" ); |
| 61 | + _this.progressCb( 1.0 ); |
| 62 | + _this.processIframeResult( $j( this ).get( 0 ) ); |
| 63 | + } ); |
| 64 | + }, |
| 65 | + |
| 66 | + /** |
| 67 | + * Process the result of the form submission, returned to an iframe. |
| 68 | + * This is the iframe's onload event. |
| 69 | + * |
| 70 | + * @param {Element} iframe iframe to extract result from |
| 71 | + */ |
| 72 | + processIframeResult: function( iframe ) { |
| 73 | + var _this = this; |
| 74 | + var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document; |
| 75 | + // Fix for Opera 9.26 |
| 76 | + if ( doc.readyState && doc.readyState != 'complete' ) { |
| 77 | + mw.log( "not complete" ); |
| 78 | + return; |
| 79 | + } |
| 80 | + |
| 81 | + // Fix for Opera 9.64 |
| 82 | + if ( doc.body && doc.body.innerHTML == "false" ) { |
| 83 | + mw.log( "no innerhtml" ); |
| 84 | + return; |
| 85 | + } |
| 86 | + var response; |
| 87 | + if ( doc.XMLDocument ) { |
| 88 | + // The response is a document property in IE |
| 89 | + response = doc.XMLDocument; |
| 90 | + } else if ( doc.body ) { |
| 91 | + // Get the json string |
| 92 | + // XXX wait... why are we grepping it out of an HTML doc? We requested jsonfm, why? |
| 93 | + json = $j( doc.body ).find( 'pre' ).text(); |
| 94 | + mw.log( 'iframe:json::' + json ); |
| 95 | + if ( json ) { |
| 96 | + response = window["eval"]( "( " + json + " )" ); |
| 97 | + } else { |
| 98 | + response = {}; |
| 99 | + } |
| 100 | + } else { |
| 101 | + // Response is a xml document |
| 102 | + response = doc; |
| 103 | + } |
| 104 | + // Process the API result |
| 105 | + _this.transportedCb( response ); |
| 106 | + } |
| 107 | +}; |
| 108 | + |
| 109 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.IframeTransport.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 110 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery.tipsy.css |
— | — | @@ -0,0 +1,30 @@ |
| 2 | +.tipsy { padding: 5px; font-size: small; position: absolute; z-index: 100000; } |
| 3 | + .tipsy-inner { padding: 5px 8px 4px 8px; background-color: black; color: white; max-width: 200px; text-align: center; } |
| 4 | + .tipsy-inner { border-radius: 3px; -moz-border-radius:3px; -webkit-border-radius:3px; } |
| 5 | + .tipsy-arrow { position: absolute; background: url('jquery.tipsy.gif') no-repeat top left; width: 9px; height: 5px; } |
| 6 | + .tipsy-n .tipsy-arrow { top: 0; left: 50%; margin-left: -4px; } |
| 7 | + .tipsy-nw .tipsy-arrow { top: 0; left: 10px; } |
| 8 | + .tipsy-ne .tipsy-arrow { top: 0; right: 10px; } |
| 9 | + .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -4px; background-position: bottom left; } |
| 10 | + .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; background-position: bottom left; } |
| 11 | + .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; background-position: bottom left; } |
| 12 | + .tipsy-e .tipsy-arrow { top: 50%; margin-top: -4px; right: 0; width: 5px; height: 9px; background-position: top right; } |
| 13 | + .tipsy-w .tipsy-arrow { top: 50%; margin-top: -4px; left: 0; width: 5px; height: 9px; } |
| 14 | + |
| 15 | + |
| 16 | +.tipsy-help .tipsy-inner { background-color: #96d8d9; color: black; } |
| 17 | +.tipsy-help .tipsy-arrow { background: url('jquery.tipsy.help.gif') } |
| 18 | + |
| 19 | +.tipsy-error .tipsy-inner { background-color: #f89c90; color: black; } |
| 20 | +.tipsy-error .tipsy-arrow { background: url('jquery.tipsy.error.gif') } |
| 21 | + |
| 22 | +.shadow { |
| 23 | + /* offset left, top, thickness, color with alpha */ |
| 24 | + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); |
| 25 | + -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); |
| 26 | + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); |
| 27 | + /* IE */ |
| 28 | + filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray'); |
| 29 | + /* slightly different syntax for IE8 */ |
| 30 | + -ms-filter:"progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray')"; |
| 31 | +} |
Property changes on: trunk/extensions/UploadWizard/resources/jquery.tipsy.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 32 | + native |
Index: trunk/extensions/UploadWizard/resources/calendar.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/resources/calendar.gif |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 33 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/resources/mw.Log.js |
— | — | @@ -0,0 +1,50 @@ |
| 2 | +// placeholders for stuff we used to get from mwEmbed |
| 3 | + |
| 4 | +/** |
| 5 | +* Log a string msg to the console |
| 6 | +* |
| 7 | +* all mw.log statements will be removed on minification so |
| 8 | +* lots of mw.log calls will not impact performance in non debug mode |
| 9 | +* |
| 10 | +* @param {String} string String to output to console |
| 11 | +*/ |
| 12 | +mw.log = function( string ) { |
| 13 | + |
| 14 | + // Add any prepend debug strings if necessary |
| 15 | + if ( mw.log.preAppendLog ) { |
| 16 | + string = mw.log.preAppendLog + string; |
| 17 | + } |
| 18 | + |
| 19 | + if ( window.console ) { |
| 20 | + window.console.log( string ); |
| 21 | + } else { |
| 22 | + |
| 23 | + /** |
| 24 | + * Old IE and non-Firebug debug: ( commented out for now ) |
| 25 | + */ |
| 26 | + var log_elm = document.getElementById('mv_js_log'); |
| 27 | + if(!log_elm) { |
| 28 | + var body = document.getElementsByTagName("body")[0]; |
| 29 | + if (body) { |
| 30 | + body.innerHTML = document.getElementsByTagName("body")[0].innerHTML + |
| 31 | + '<div style="position:absolute;z-index:500;bottom:0px;left:0px;right:0px;height:100px;">'+ |
| 32 | + '<textarea id="mv_js_log" cols="120" rows="4"></textarea>'+ |
| 33 | + '</div>'; |
| 34 | + log_elm = document.getElementById('mv_js_log'); |
| 35 | + } else { |
| 36 | + mw.logBuffered += string + "\n"; |
| 37 | + } |
| 38 | + } |
| 39 | + if(log_elm) { |
| 40 | + if (mw.logBuffered.length) { |
| 41 | + log_elm.value += mw.logBuffered; |
| 42 | + mw.logBuffered = ""; |
| 43 | + } |
| 44 | + log_elm.value+=string+"\n"; |
| 45 | + } |
| 46 | + |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +mw.logBuffered = ""; |
| 51 | + |
Property changes on: trunk/extensions/UploadWizard/resources/mw.Log.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 52 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery.arrowSteps.css |
— | — | @@ -0,0 +1,40 @@ |
| 2 | +.arrowSteps { |
| 3 | + list-style-type: none; |
| 4 | + list-style-image: none; |
| 5 | + border: 1px solid #666666; |
| 6 | + position: relative; |
| 7 | +} |
| 8 | + |
| 9 | +.arrowSteps li { |
| 10 | + float: left; |
| 11 | + padding: 0px; |
| 12 | + margin: 0px; |
| 13 | + border: 0 none; |
| 14 | +} |
| 15 | + |
| 16 | +.arrowSteps li div { |
| 17 | + padding: 0.5em; |
| 18 | + text-align: center; |
| 19 | + white-space: nowrap; |
| 20 | + overflow: hidden; |
| 21 | +} |
| 22 | + |
| 23 | +.arrowSteps li.arrow div { |
| 24 | + background: url(inactive-arrow-divider.png) no-repeat right center; |
| 25 | +} |
| 26 | + |
| 27 | +/* applied to the element preceding the highlighted step */ |
| 28 | +.arrowSteps li.arrow.tail div { |
| 29 | + background: url(arrow-tail.png) no-repeat right center; |
| 30 | +} |
| 31 | + |
| 32 | +/* this applies to all highlighted, including the last */ |
| 33 | +.arrowSteps li.head div { |
| 34 | + background: url(arrow-head.png) no-repeat left center; |
| 35 | + font-weight: bold; |
| 36 | +} |
| 37 | + |
| 38 | +/* this applies to all highlighted arrows except the last */ |
| 39 | +.arrowSteps li.arrow.head div { |
| 40 | + background: url(arrow-head.png) no-repeat right center; |
| 41 | +} |
Property changes on: trunk/extensions/UploadWizard/resources/jquery.arrowSteps.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 42 | + native |
Index: trunk/extensions/UploadWizard/resources/mw.LanguageUpWiz.js |
— | — | @@ -0,0 +1,490 @@ |
| 2 | +mw.addMessages({ |
| 3 | + "mwe-upwiz-code-unknown": "Unknown language" |
| 4 | +}); |
| 5 | + |
| 6 | +/** |
| 7 | + * Utility class which knows about languages, and how to construct HTML to select them |
| 8 | + * TODO: make this a more common library, used by this and TimedText |
| 9 | + */ |
| 10 | +mw.LanguageUpWiz = { |
| 11 | + |
| 12 | + defaultCode: 'en', // when we absolutely have no idea what language to preselect |
| 13 | + |
| 14 | + initialized: false, |
| 15 | + |
| 16 | + UNKNOWN: 'unknown', |
| 17 | + |
| 18 | + /** |
| 19 | + * List of all languages mediaWiki supports ( Avoid an api call to get this same info ) |
| 20 | + * http://commons.wikimedia.org/w/api.php?action=query&meta=siteinfo&siprop=languages&format=jsonfm |
| 21 | + * |
| 22 | + * Languages sorted by name, using tools in $SVNROOT/mediawiki/trunk/tools/langcodes |
| 23 | + * This is somewhat better than sorting by code (which produces totally bizarre results) but is not |
| 24 | + * a true lexicographic sort |
| 25 | + */ |
| 26 | + languages: [ |
| 27 | + { code: "ace", text: "Ac\u00e8h" }, |
| 28 | + { code: "af", text: "Afrikaans" }, |
| 29 | + { code: "ak", text: "Akan" }, |
| 30 | + { code: "als", text: "Alemannisch" }, // XXX someone fix this please |
| 31 | + { code: "gsw", text: "Alemannisch" }, // |
| 32 | + { code: "ang", text: "Anglo-Saxon" }, |
| 33 | + { code: "an", text: "Aragon\u00e9s" }, |
| 34 | + { code: "roa-rup", text: "Arm\u00e3neashce" }, |
| 35 | + { code: "frp", text: "Arpetan" }, |
| 36 | + { code: "ast", text: "Asturianu" }, |
| 37 | + { code: "gn", text: "Ava\u00f1e'\u1ebd" }, |
| 38 | + { code: "ay", text: "Aymar aru" }, |
| 39 | + { code: "az", text: "Az\u0259rbaycan" }, |
| 40 | + { code: "id", text: "Bahasa Indonesia" }, |
| 41 | + { code: "ms", text: "Bahasa Melayu" }, |
| 42 | + { code: "bm", text: "Bamanankan" }, |
| 43 | + { code: "map-bms", text: "Basa Banyumasan" }, |
| 44 | + { code: "jv", text: "Basa Jawa" }, |
| 45 | + { code: "su", text: "Basa Sunda" }, |
| 46 | + { code: "bcl", text: "Bikol Central" }, |
| 47 | + { code: "bi", text: "Bislama" }, |
| 48 | + { code: "bar", text: "Boarisch" }, |
| 49 | + { code: "bs", text: "Bosanski" }, |
| 50 | + { code: "br", text: "Brezhoneg" }, |
| 51 | + { code: "en-gb", text: "British English" }, |
| 52 | + { code: "nan", text: "B\u00e2n-l\u00e2m-g\u00fa" }, |
| 53 | + { code: "zh-min-nan", text: "B\u00e2n-l\u00e2m-g\u00fa" }, |
| 54 | + { code: "ca", text: "Catal\u00e0" }, |
| 55 | + { code: "ceb", text: "Cebuano" }, |
| 56 | + { code: "ch", text: "Chamoru" }, |
| 57 | + { code: "cbk-zam", text: "Chavacano de Zamboanga" }, |
| 58 | + { code: "ny", text: "Chi-Chewa" }, |
| 59 | + { code: "cho", text: "Choctaw" }, |
| 60 | + { code: "sei", text: "Cmique Itom" }, |
| 61 | + { code: "co", text: "Corsu" }, |
| 62 | + { code: "cy", text: "Cymraeg" }, |
| 63 | + { code: "da", text: "Dansk" }, |
| 64 | + { code: "dk", text: "Dansk (deprecated:da)" }, // XXX deprecated? |
| 65 | + { code: "pdc", text: "Deitsch" }, |
| 66 | + { code: "de", text: "Deutsch" }, |
| 67 | + { code: "de-formal", text: "Deutsch (Sie-Form)" }, |
| 68 | + { code: "nv", text: "Din\u00e9 bizaad" }, |
| 69 | + { code: "dsb", text: "Dolnoserbski" }, |
| 70 | + { code: "na", text: "Dorerin Naoero" }, |
| 71 | + { code: "mh", text: "Ebon" }, |
| 72 | + { code: "et", text: "Eesti" }, |
| 73 | + { code: "eml", text: "Emili\u00e0n e rumagn\u00f2l" }, |
| 74 | + { code: "en", text: "English" }, |
| 75 | + { code: "es", text: "Espa\u00f1ol" }, |
| 76 | + { code: "eo", text: "Esperanto" }, |
| 77 | + { code: "ext", text: "Estreme\u00f1u" }, |
| 78 | + { code: "eu", text: "Euskara" }, |
| 79 | + { code: "ee", text: "E\u028begbe" }, |
| 80 | + { code: "hif", text: "Fiji Hindi" }, // XXX fix this |
| 81 | + { code: "hif-latn", text: "Fiji Hindi" }, // |
| 82 | + { code: "fr", text: "Fran\u00e7ais" }, |
| 83 | + { code: "frc", text: "Fran\u00e7ais canadien" }, |
| 84 | + { code: "fy", text: "Frysk" }, |
| 85 | + { code: "ff", text: "Fulfulde" }, |
| 86 | + { code: "fur", text: "Furlan" }, |
| 87 | + { code: "fo", text: "F\u00f8royskt" }, |
| 88 | + { code: "ga", text: "Gaeilge" }, |
| 89 | + { code: "gv", text: "Gaelg" }, |
| 90 | + { code: "sm", text: "Gagana Samoa" }, |
| 91 | + { code: "gag", text: "Gagauz" }, |
| 92 | + { code: "gl", text: "Galego" }, |
| 93 | + { code: "aln", text: "Geg\u00eb" }, |
| 94 | + { code: "gd", text: "G\u00e0idhlig" }, |
| 95 | + { code: "ki", text: "G\u0129k\u0169y\u0169" }, |
| 96 | + { code: "hak", text: "Hak-k\u00e2-fa" }, |
| 97 | + { code: "haw", text: "Hawai`i" }, |
| 98 | + { code: "ho", text: "Hiri Motu" }, |
| 99 | + { code: "hsb", text: "Hornjoserbsce" }, |
| 100 | + { code: "hr", text: "Hrvatski" }, |
| 101 | + { code: "io", text: "Ido" }, |
| 102 | + { code: "ig", text: "Igbo" }, |
| 103 | + { code: "ilo", text: "Ilokano" }, |
| 104 | + { code: "hil", text: "Ilonggo" }, |
| 105 | + { code: "ia", text: "Interlingua" }, |
| 106 | + { code: "ie", text: "Interlingue" }, |
| 107 | + { code: "it", text: "Italiano" }, |
| 108 | + { code: "ik", text: "I\u00f1upiak" }, |
| 109 | + { code: "jut", text: "Jysk" }, |
| 110 | + { code: "kl", text: "Kalaallisut" }, |
| 111 | + { code: "kr", text: "Kanuri" }, |
| 112 | + { code: "pam", text: "Kapampangan" }, |
| 113 | + { code: "csb", text: "Kasz\u00ebbsczi" }, |
| 114 | + { code: "kw", text: "Kernowek" }, |
| 115 | + { code: "krj", text: "Kinaray-a" }, |
| 116 | + { code: "rw", text: "Kinyarwanda" }, |
| 117 | + { code: "rn", text: "Kirundi" }, |
| 118 | + { code: "sw", text: "Kiswahili" }, |
| 119 | + { code: "kg", text: "Kongo" }, |
| 120 | + { code: "avk", text: "Kotava" }, |
| 121 | + { code: "ht", text: "Krey\u00f2l ayisyen" }, |
| 122 | + { code: "kri", text: "Krio" }, |
| 123 | + { code: "ku", text: "Kurd\u00ee \/ \u0643\u0648\u0631\u062f\u06cc" }, |
| 124 | + { code: "kiu", text: "Kurmanc\u00ee" }, |
| 125 | + { code: "kj", text: "Kwanyama" }, |
| 126 | + { code: "lad", text: "Ladino" }, |
| 127 | + { code: "la", text: "Latina" }, |
| 128 | + { code: "lv", text: "Latvie\u0161u" }, |
| 129 | + { code: "lt", text: "Lietuvi\u0173" }, |
| 130 | + { code: "li", text: "Limburgs" }, |
| 131 | + { code: "lfn", text: "Lingua Franca Nova" }, |
| 132 | + { code: "ln", text: "Ling\u00e1la" }, |
| 133 | + { code: "jbo", text: "Lojban" }, |
| 134 | + { code: "lg", text: "Luganda" }, |
| 135 | + { code: "lmo", text: "Lumbaart" }, |
| 136 | + { code: "lb", text: "L\u00ebtzebuergesch" }, |
| 137 | + { code: "lij", text: "L\u00edguru" }, |
| 138 | + { code: "hu", text: "Magyar" }, |
| 139 | + { code: "mg", text: "Malagasy" }, |
| 140 | + { code: "mt", text: "Malti" }, |
| 141 | + { code: "arn", text: "Mapudungun" }, |
| 142 | + { code: "mwl", text: "Mirand\u00e9s" }, |
| 143 | + { code: "mus", text: "Mvskoke" }, |
| 144 | + { code: "cdo", text: "M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304" }, |
| 145 | + { code: "mi", text: "M\u0101ori" }, |
| 146 | + { code: "fj", text: "Na Vosa Vakaviti" }, |
| 147 | + { code: "nl", text: "Nederlands" }, |
| 148 | + { code: "nds-nl", text: "Nedersaksisch" }, |
| 149 | + { code: "niu", text: "Niu\u0113" }, |
| 150 | + { code: "nap", text: "Nnapulitano" }, |
| 151 | + { code: "pih", text: "Norfuk \/ Pitkern" }, |
| 152 | + { code: "nb", text: "Norsk (bokm\u00e5l)" }, |
| 153 | + { code: "no", text: "Norsk (bokm\u00e5l)" }, |
| 154 | + { code: "nn", text: "Norsk (nynorsk)" }, |
| 155 | + { code: "nrm", text: "Nouormand" }, |
| 156 | + { code: "nov", text: "Novial" }, |
| 157 | + { code: "nah", text: "N\u0101huatl" }, |
| 158 | + { code: "cr", text: "N\u0113hiyaw\u0113win \/ \u14c0\u1426\u1403\u152d\u140d\u140f\u1423" }, |
| 159 | + { code: "uz", text: "O'zbek" }, |
| 160 | + { code: "oc", text: "Occitan" }, |
| 161 | + { code: "om", text: "Oromoo" }, |
| 162 | + { code: "ng", text: "Oshiwambo" }, |
| 163 | + { code: "hz", text: "Otsiherero" }, |
| 164 | + { code: "pag", text: "Pangasinan" }, |
| 165 | + { code: "pap", text: "Papiamentu" }, |
| 166 | + { code: "pfl", text: "Pf\u00e4lzisch" }, |
| 167 | + { code: "pcd", text: "Picard" }, |
| 168 | + { code: "pms", text: "Piemont\u00e8is" }, |
| 169 | + { code: "nds", text: "Plattd\u00fc\u00fctsch" }, |
| 170 | + { code: "pdt", text: "Plautdietsch" }, |
| 171 | + { code: "pl", text: "Polski" }, |
| 172 | + { code: "pt", text: "Portugu\u00eas" }, |
| 173 | + { code: "pt-br", text: "Portugu\u00eas do Brasil" }, |
| 174 | + { code: "aa", text: "Qaf\u00e1r af" }, |
| 175 | + { code: "kaa", text: "Qaraqalpaqsha" }, |
| 176 | + { code: "crh", text: "Q\u0131r\u0131mtatarca" }, |
| 177 | + { code: "ty", text: "Reo M\u0101`ohi" }, |
| 178 | + { code: "ksh", text: "Ripoarisch" }, |
| 179 | + { code: "rmy", text: "Romani" }, |
| 180 | + { code: "ro", text: "Rom\u00e2n\u0103" }, |
| 181 | + { code: "rm", text: "Rumantsch" }, |
| 182 | + { code: "qu", text: "Runa Simi" }, |
| 183 | + { code: "sc", text: "Sardu" }, |
| 184 | + { code: "sdc", text: "Sassaresu" }, |
| 185 | + { code: "sli", text: "Schl\u00e4sch" }, |
| 186 | + { code: "de-ch", text: "Schweizer Hochdeutsch" }, |
| 187 | + { code: "sco", text: "Scots" }, |
| 188 | + { code: "stq", text: "Seeltersk" }, |
| 189 | + { code: "st", text: "Sesotho" }, |
| 190 | + { code: "nso", text: "Sesotho sa Leboa" }, |
| 191 | + { code: "tn", text: "Setswana" }, |
| 192 | + { code: "sq", text: "Shqip" }, |
| 193 | + { code: "ss", text: "SiSwati" }, |
| 194 | + { code: "scn", text: "Sicilianu" }, |
| 195 | + { code: "loz", text: "Silozi" }, |
| 196 | + { code: "simple", text: "Simple English" }, |
| 197 | + { code: "sk", text: "Sloven\u010dina" }, |
| 198 | + { code: "sl", text: "Sloven\u0161\u010dina" }, |
| 199 | + { code: "so", text: "Soomaaliga" }, |
| 200 | + { code: "ckb", text: "Soran\u00ee \/ \u06a9\u0648\u0631\u062f\u06cc" }, |
| 201 | + { code: "srn", text: "Sranantongo" }, |
| 202 | + { code: "sr-el", text: "Srpski (latinica)" }, |
| 203 | + { code: "sh", text: "Srpskohrvatski \/ \u0421\u0440\u043f\u0441\u043a\u043e\u0445\u0440\u0432\u0430\u0442\u0441\u043a\u0438" }, |
| 204 | + { code: "fi", text: "Suomi" }, |
| 205 | + { code: "sv", text: "Svenska" }, |
| 206 | + { code: "se", text: "S\u00e1megiella" }, |
| 207 | + { code: "sg", text: "S\u00e4ng\u00f6" }, |
| 208 | + { code: "tl", text: "Tagalog" }, |
| 209 | + { code: "kab", text: "Taqbaylit" }, |
| 210 | + { code: "roa-tara", text: "Tarand\u00edne" }, |
| 211 | + { code: "rif", text: "Tarifit" }, |
| 212 | + { code: "tt-latn", text: "Tatar\u00e7a" }, |
| 213 | + { code: "shi", text: "Ta\u0161l\u1e25iyt" }, |
| 214 | + { code: "tet", text: "Tetun" }, |
| 215 | + { code: "vi", text: "Ti\u1ebfng Vi\u1ec7t" }, |
| 216 | + { code: "tpi", text: "Tok Pisin" }, |
| 217 | + { code: "tokipona", text: "Toki Pona" }, |
| 218 | + { code: "tp", text: "Toki Pona (deprecated:tokipona)" }, // XXX deprecated? |
| 219 | + { code: "chy", text: "Tsets\u00eahest\u00e2hese" }, |
| 220 | + { code: "ve", text: "Tshivenda" }, |
| 221 | + { code: "tw", text: "Twi" }, |
| 222 | + { code: "tk", text: "T\u00fcrkmen\u00e7e" }, |
| 223 | + { code: "tr", text: "T\u00fcrk\u00e7e" }, |
| 224 | + { code: "ug-latn", text: "Uyghurche\u200e" }, |
| 225 | + { code: "ug", text: "Uyghurche\u200e \/ \u0626\u06c7\u064a\u063a\u06c7\u0631\u0686\u06d5" }, |
| 226 | + { code: "za", text: "Vahcuengh" }, |
| 227 | + { code: "vep", text: "Vepsan kel'" }, |
| 228 | + { code: "ruq", text: "Vl\u0103he\u015fte" }, |
| 229 | + { code: "ruq-latn", text: "Vl\u0103he\u015fte" }, |
| 230 | + { code: "vo", text: "Volap\u00fck" }, |
| 231 | + { code: "vec", text: "V\u00e8neto" }, |
| 232 | + { code: "fiu-vro", text: "V\u00f5ro" }, |
| 233 | + { code: "vro", text: "V\u00f5ro" }, |
| 234 | + { code: "wa", text: "Walon" }, |
| 235 | + { code: "vls", text: "West-Vlams" }, |
| 236 | + { code: "war", text: "Winaray" }, |
| 237 | + { code: "wo", text: "Wolof" }, |
| 238 | + { code: "ts", text: "Xitsonga" }, |
| 239 | + { code: "yo", text: "Yor\u00f9b\u00e1" }, |
| 240 | + { code: "diq", text: "Zazaki" }, |
| 241 | + { code: "zea", text: "Ze\u00eauws" }, |
| 242 | + { code: "sn", text: "chiShona" }, |
| 243 | + { code: "tum", text: "chiTumbuka" }, |
| 244 | + { code: "ike-latn", text: "inuktitut" }, |
| 245 | + { code: "xh", text: "isiXhosa" }, |
| 246 | + { code: "zu", text: "isiZulu" }, |
| 247 | + { code: "to", text: "lea faka-Tonga" }, |
| 248 | + { code: "tg-latn", text: "tojik\u012b" }, |
| 249 | + { code: "is", text: "\u00cdslenska" }, |
| 250 | + { code: "de-at", text: "\u00d6sterreichisches Deutsch" }, |
| 251 | + { code: "szl", text: "\u015al\u016fnski" }, |
| 252 | + { code: "el", text: "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac" }, |
| 253 | + { code: "pnt", text: "\u03a0\u03bf\u03bd\u03c4\u03b9\u03b1\u03ba\u03ac" }, |
| 254 | + { code: "av", text: "\u0410\u0432\u0430\u0440" }, |
| 255 | + { code: "ab", text: "\u0410\u04a7\u0441\u0443\u0430" }, |
| 256 | + { code: "ba", text: "\u0411\u0430\u0448\u04a1\u043e\u0440\u0442" }, |
| 257 | + { code: "be", text: "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f" }, |
| 258 | + { code: "be-tarask", text: "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430)" }, |
| 259 | + { code: "be-x-old", text: "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430)" }, |
| 260 | + { code: "bxr", text: "\u0411\u0443\u0440\u044f\u0430\u0434" }, |
| 261 | + { code: "bg", text: "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438" }, |
| 262 | + { code: "ruq-cyrl", text: "\u0412\u043b\u0430\u0445\u0435\u0441\u0442\u0435" }, |
| 263 | + { code: "inh", text: "\u0413\u0406\u0430\u043b\u0433\u0406\u0430\u0439 \u011eal\u011faj" }, |
| 264 | + { code: "os", text: "\u0418\u0440\u043e\u043d\u0430\u0443" }, |
| 265 | + { code: "kv", text: "\u041a\u043e\u043c\u0438" }, |
| 266 | + { code: "ky", text: "\u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430" }, |
| 267 | + { code: "lbe", text: "\u041b\u0430\u043a\u043a\u0443" }, |
| 268 | + { code: "lez", text: "\u041b\u0435\u0437\u0433\u0438" }, |
| 269 | + { code: "mk", text: "\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438" }, |
| 270 | + { code: "mdf", text: "\u041c\u043e\u043a\u0448\u0435\u043d\u044c" }, |
| 271 | + { code: "mo", text: "\u041c\u043e\u043b\u0434\u043e\u0432\u0435\u043d\u044f\u0441\u043a\u044d" }, |
| 272 | + { code: "mn", text: "\u041c\u043e\u043d\u0433\u043e\u043b" }, |
| 273 | + { code: "ce", text: "\u041d\u043e\u0445\u0447\u0438\u0439\u043d" }, |
| 274 | + { code: "mhr", text: "\u041e\u043b\u044b\u043a \u041c\u0430\u0440\u0438\u0439" }, |
| 275 | + { code: "ru", text: "\u0420\u0443\u0441\u0441\u043a\u0438\u0439" }, |
| 276 | + { code: "sah", text: "\u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430" }, |
| 277 | + { code: "cu", text: "\u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f" }, |
| 278 | + { code: "sr-ec", text: "\u0421\u0440\u043f\u0441\u043a\u0438 (\u045b\u0438\u0440\u0438\u043b\u0438\u0446\u0430)" }, |
| 279 | + { code: "sr", text: "\u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski" }, |
| 280 | + { code: "tt-cyrl", text: "\u0422\u0430\u0442\u0430\u0440\u0447\u0430" }, |
| 281 | + { code: "tt", text: "\u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a" }, |
| 282 | + { code: "tg", text: "\u0422\u043e\u04b7\u0438\u043a\u04e3" }, |
| 283 | + { code: "tg-cyrl", text: "\u0422\u043e\u04b7\u0438\u043a\u04e3" }, |
| 284 | + { code: "tyv", text: "\u0422\u044b\u0432\u0430 \u0434\u044b\u043b" }, |
| 285 | + { code: "udm", text: "\u0423\u0434\u043c\u0443\u0440\u0442" }, |
| 286 | + { code: "uk", text: "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430" }, |
| 287 | + { code: "xal", text: "\u0425\u0430\u043b\u044c\u043c\u0433" }, |
| 288 | + { code: "cv", text: "\u0427\u04d1\u0432\u0430\u0448\u043b\u0430" }, |
| 289 | + { code: "myv", text: "\u042d\u0440\u0437\u044f\u043d\u044c" }, |
| 290 | + { code: "kk", text: "\u049a\u0430\u0437\u0430\u049b\u0448\u0430" }, |
| 291 | + { code: "hy", text: "\u0540\u0561\u0575\u0565\u0580\u0565\u0576" }, |
| 292 | + { code: "yi", text: "\u05d9\u05d9\u05b4\u05d3\u05d9\u05e9" }, |
| 293 | + { code: "he", text: "\u05e2\u05d1\u05e8\u05d9\u05ea" }, |
| 294 | + { code: "ug-arab", text: "\u0626\u06c7\u064a\u063a\u06c7\u0631\u0686\u06d5" }, |
| 295 | + { code: "ur", text: "\u0627\u0631\u062f\u0648" }, |
| 296 | + { code: "ar", text: "\u0627\u0644\u0639\u0631\u0628\u064a\u0629" }, |
| 297 | + { code: "bqi", text: "\u0628\u062e\u062a\u064a\u0627\u0631\u064a" }, |
| 298 | + { code: "bcc", text: "\u0628\u0644\u0648\u0686\u06cc \u0645\u06a9\u0631\u0627\u0646\u06cc" }, |
| 299 | + { code: "sd", text: "\u0633\u0646\u068c\u064a" }, |
| 300 | + { code: "fa", text: "\u0641\u0627\u0631\u0633\u06cc" }, |
| 301 | + { code: "arz", text: "\u0645\u0635\u0631\u0649" }, |
| 302 | + { code: "mzn", text: "\u0645\u064e\u0632\u0650\u0631\u0648\u0646\u064a" }, |
| 303 | + { code: "ha", text: "\u0647\u064e\u0648\u064f\u0633\u064e" }, |
| 304 | + { code: "pnb", text: "\u067e\u0646\u062c\u0627\u0628\u06cc" }, |
| 305 | + { code: "ps", text: "\u067e\u069a\u062a\u0648" }, |
| 306 | + { code: "glk", text: "\u06af\u06cc\u0644\u06a9\u06cc" }, |
| 307 | + { code: "arc", text: "\u0710\u072a\u0721\u071d\u0710" }, |
| 308 | + { code: "dv", text: "\u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0" }, |
| 309 | + { code: "ks", text: "\u0915\u0936\u094d\u092e\u0940\u0930\u0940 - (\u0643\u0634\u0645\u064a\u0631\u064a)" }, |
| 310 | + { code: "new", text: "\u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e" }, |
| 311 | + { code: "ne", text: "\u0928\u0947\u092a\u093e\u0932\u0940" }, |
| 312 | + { code: "pi", text: "\u092a\u093e\u093f\u0934" }, |
| 313 | + { code: "hif-deva", text: "\u092b\u093c\u0940\u091c\u0940 \u0939\u093f\u0928\u094d\u0926\u0940" }, |
| 314 | + { code: "bh", text: "\u092d\u094b\u091c\u092a\u0941\u0930\u0940" }, |
| 315 | + { code: "mr", text: "\u092e\u0930\u093e\u0920\u0940" }, |
| 316 | + { code: "mai", text: "\u092e\u0948\u0925\u093f\u0932\u0940" }, |
| 317 | + { code: "sa", text: "\u0938\u0902\u0938\u094d\u0915\u0943\u0924" }, |
| 318 | + { code: "hi", text: "\u0939\u093f\u0928\u094d\u0926\u0940" }, |
| 319 | + { code: "as", text: "\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be" }, |
| 320 | + { code: "bpy", text: "\u0987\u09ae\u09be\u09b0 \u09a0\u09be\u09b0\/\u09ac\u09bf\u09b7\u09cd\u09a3\u09c1\u09aa\u09cd\u09b0\u09bf\u09af\u09bc\u09be \u09ae\u09a3\u09bf\u09aa\u09c1\u09b0\u09c0" }, |
| 321 | + { code: "bn", text: "\u09ac\u09be\u0982\u09b2\u09be" }, |
| 322 | + { code: "pa", text: "\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40" }, |
| 323 | + { code: "gu", text: "\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0" }, |
| 324 | + { code: "or", text: "\u0b13\u0b21\u0b3c\u0b3f\u0b06" }, |
| 325 | + { code: "ta", text: "\u0ba4\u0bae\u0bbf\u0bb4\u0bcd" }, |
| 326 | + { code: "te", text: "\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41" }, |
| 327 | + { code: "sma", text: "\u00c5arjelsaemien" }, |
| 328 | + { code: "kn", text: "\u0c95\u0ca8\u0ccd\u0ca8\u0ca1" }, |
| 329 | + { code: "tcy", text: "\u0ca4\u0cc1\u0cb3\u0cc1" }, |
| 330 | + { code: "ml", text: "\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02" }, |
| 331 | + { code: "si", text: "\u0dc3\u0dd2\u0d82\u0dc4\u0dbd" }, |
| 332 | + { code: "th", text: "\u0e44\u0e17\u0e22" }, |
| 333 | + { code: "lo", text: "\u0ea5\u0eb2\u0ea7" }, |
| 334 | + { code: "dz", text: "\u0f47\u0f7c\u0f44\u0f0b\u0f41" }, |
| 335 | + { code: "bo", text: "\u0f56\u0f7c\u0f51\u0f0b\u0f61\u0f72\u0f42" }, |
| 336 | + { code: "my", text: "\u1019\u103c\u1014\u103a\u1019\u102c\u1018\u102c\u101e\u102c" }, |
| 337 | + { code: "cs", text: "\u010cesky" }, |
| 338 | + { code: "xmf", text: "\u10db\u10d0\u10e0\u10d2\u10d0\u10da\u10e3\u10e0\u10d8" }, |
| 339 | + { code: "ka", text: "\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8" }, |
| 340 | + { code: "ti", text: "\u1275\u130d\u122d\u129b" }, |
| 341 | + { code: "am", text: "\u12a0\u121b\u122d\u129b" }, |
| 342 | + { code: "chr", text: "\u13e3\u13b3\u13a9" }, |
| 343 | + { code: "ike-cans", text: "\u1403\u14c4\u1483\u144e\u1450\u1466" }, |
| 344 | + { code: "iu", text: "\u1403\u14c4\u1483\u144e\u1450\u1466\/inuktitut" }, |
| 345 | + { code: "km", text: "\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a" }, |
| 346 | + { code: "bat-smg", text: "\u017demait\u0117\u0161ka" }, |
| 347 | + { code: "bug", text: "\u1a05\u1a14 \u1a15\u1a18\u1a01\u1a17" }, |
| 348 | + { code: "grc", text: "\u1f08\u03c1\u03c7\u03b1\u03af\u03b1 \u1f11\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u1f74" }, |
| 349 | + { code: "ku-latn", text: "\u202aKurd\u00ee (lat\u00een\u00ee)\u202c" }, |
| 350 | + { code: "kk-tr", text: "\u202aQazaq\u015fa (T\u00fcrk\u00efya)\u202c" }, |
| 351 | + { code: "kk-latn", text: "\u202aQazaq\u015fa (lat\u0131n)\u202c" }, |
| 352 | + { code: "crh-latn", text: "\u202aQ\u0131r\u0131mtatarca (Latin)\u202c" }, |
| 353 | + { code: "crh-cyrl", text: "\u202a\u041a\u044a\u044b\u0440\u044b\u043c\u0442\u0430\u0442\u0430\u0440\u0434\u0436\u0430 (\u041a\u0438\u0440\u0438\u043b\u043b)\u202c" }, |
| 354 | + { code: "kk-cyrl", text: "\u202a\u049a\u0430\u0437\u0430\u049b\u0448\u0430 (\u043a\u0438\u0440\u0438\u043b)\u202c" }, |
| 355 | + { code: "kk-kz", text: "\u202a\u049a\u0430\u0437\u0430\u049b\u0448\u0430 (\u049a\u0430\u0437\u0430\u049b\u0441\u0442\u0430\u043d)\u202c" }, |
| 356 | + { code: "kk-arab", text: "\u202b\u0642\u0627\u0632\u0627\u0642\u0634\u0627 (\u062a\u0674\u0648\u062a\u06d5)\u202c" }, |
| 357 | + { code: "kk-cn", text: "\u202b\u0642\u0627\u0632\u0627\u0642\u0634\u0627 (\u062c\u06c7\u0646\u06af\u0648)\u202c" }, |
| 358 | + { code: "ku-arab", text: "\u202b\u0643\u0648\u0631\u062f\u064a (\u0639\u06d5\u0631\u06d5\u0628\u06cc)\u202c" }, |
| 359 | + { code: "zh", text: "\u4e2d\u6587" }, |
| 360 | + { code: "zh-cn", text: "\u4e2d\u6587(\u4e2d\u56fd\u5927\u9646)" }, |
| 361 | + { code: "zh-tw", text: "\u4e2d\u6587(\u53f0\u7063)" }, |
| 362 | + { code: "zh-sg", text: "\u4e2d\u6587(\u65b0\u52a0\u5761)" }, |
| 363 | + { code: "zh-mo", text: "\u4e2d\u6587(\u6fb3\u9580)" }, |
| 364 | + { code: "zh-hans", text: "\u4e2d\u6587(\u7b80\u4f53)" }, |
| 365 | + { code: "zh-hant", text: "\u4e2d\u6587(\u7e41\u9ad4)" }, |
| 366 | + { code: "zh-hk", text: "\u4e2d\u6587(\u9999\u6e2f)" }, |
| 367 | + { code: "zh-my", text: "\u4e2d\u6587(\u9a6c\u6765\u897f\u4e9a)" }, |
| 368 | + { code: "wuu", text: "\u5434\u8bed" }, |
| 369 | + { code: "lzh", text: "\u6587\u8a00" }, |
| 370 | + { code: "zh-classical", text: "\u6587\u8a00" }, |
| 371 | + { code: "ja", text: "\u65e5\u672c\u8a9e" }, |
| 372 | + { code: "yue", text: "\u7cb5\u8a9e" }, |
| 373 | + { code: "zh-yue", text: "\u7cb5\u8a9e" }, |
| 374 | + { code: "gan", text: "\u8d1b\u8a9e" }, |
| 375 | + { code: "gan-hant", text: "\u8d1b\u8a9e(\u7e41\u9ad4)" }, |
| 376 | + { code: "gan-hans", text: "\u8d63\u8bed(\u7b80\u4f53)" }, |
| 377 | + { code: "ii", text: "\ua187\ua259" }, |
| 378 | + { code: "ko", text: "\ud55c\uad6d\uc5b4" }, |
| 379 | + { code: "ko-kp", text: "\ud55c\uad6d\uc5b4 (\uc870\uc120)" }, |
| 380 | + { code: "got", text: "\ud800\udf32\ud800\udf3f\ud800\udf44\ud800\udf39\ud800\udf43\ud800\udf3a" } |
| 381 | + ], |
| 382 | + |
| 383 | + /** |
| 384 | + * cache some useful objects |
| 385 | + * 1) mostly ready-to-go language HTML menu. When/if we upgrade, make it a jQuery combobox |
| 386 | + * 2) dict of language code to name -- useful for testing for existence, maybe other things. |
| 387 | + */ |
| 388 | + initialize: function() { |
| 389 | + if ( mw.LanguageUpWiz.initialized ) { |
| 390 | + return; |
| 391 | + } |
| 392 | + mw.LanguageUpWiz._codes = {}; |
| 393 | + var select = $j( '<select/>' ); |
| 394 | + $j.each( mw.LanguageUpWiz.languages, function( i, language ) { |
| 395 | + select.append( |
| 396 | + $j( '<option>' ) |
| 397 | + .attr( 'value', language.code ) |
| 398 | + .append( language.text ) |
| 399 | + ); |
| 400 | + mw.LanguageUpWiz._codes[language.code] = language.text; |
| 401 | + } ); |
| 402 | + mw.LanguageUpWiz.$_select = select; |
| 403 | + mw.LanguageUpWiz.initialized = true; |
| 404 | + }, |
| 405 | + |
| 406 | + /** |
| 407 | + * Get an HTML select menu of all our languages. |
| 408 | + * @param name desired name of select element |
| 409 | + * @param code desired default language code |
| 410 | + * @return HTML select element configured as desired |
| 411 | + */ |
| 412 | + getMenu: function( name, code ) { |
| 413 | + mw.LanguageUpWiz.initialize(); |
| 414 | + var $select = mw.LanguageUpWiz.$_select.clone(); |
| 415 | + $select.attr( 'name', name ); |
| 416 | + if ( code === mw.LanguageUpWiz.UNKNOWN ) { |
| 417 | + // n.b. MediaWiki LanguageHandler has ability to add custom label for 'Unknown'; possibly as pseudo-label |
| 418 | + $select.prepend( $j( '<option>' ).attr( 'value', mw.LanguageUpWiz.UNKNOWN ).append( gM( 'mwe-upwiz-code-unknown' )) ); |
| 419 | + $select.val( mw.LanguageUpWiz.UNKNOWN ); |
| 420 | + } else if ( code !== undefined ) { |
| 421 | + $select.val( mw.LanguageUpWiz.getClosest( code )); |
| 422 | + } |
| 423 | + return $select.get( 0 ); |
| 424 | + }, |
| 425 | + |
| 426 | + /** |
| 427 | + * Figure out the closest language we have to a supplied language code. |
| 428 | + * It seems that people on Mediawiki set their language code as freetext, and it could be anything, even |
| 429 | + * variants we don't have a record for, or ones that are not in any ISO standard. |
| 430 | + * |
| 431 | + * Logic copied from MediaWiki:LanguageHandler.js |
| 432 | + * handle null cases, special cases for some Chinese variants |
| 433 | + * Otherwise, if handed "foo-bar-baz" language, try to match most specific language, |
| 434 | + * "foo-bar-baz", then "foo-bar", then "foo" |
| 435 | + * |
| 436 | + * @param code A string representing a language code, which we may or may not have. |
| 437 | + * Expected to be separated with dashes as codes from ISO 639, e.g. "zh-tw" for Chinese ( Traditional ) |
| 438 | + * @return a language code which is close to the supplied parameter, or fall back to mw.LanguageUpWiz.defaultCode |
| 439 | + */ |
| 440 | + getClosest: function( code ) { |
| 441 | + mw.LanguageUpWiz.initialize(); |
| 442 | + if ( typeof ( code ) != 'string' || code === null || code.length === 0 ) { |
| 443 | + return mw.LanguageUpWiz.defaultCode; |
| 444 | + } |
| 445 | + if ( code == 'nan' || code == 'minnan' ) { |
| 446 | + return 'zh-min-nan'; |
| 447 | + } else if ( mw.LanguageUpWiz._codes[code] !== undefined ) { |
| 448 | + return code; |
| 449 | + } |
| 450 | + return mw.LanguageUpWiz.getClosest( code.substring( 0, code.indexOf( '-' )) ); |
| 451 | + } |
| 452 | + |
| 453 | + |
| 454 | + // enhance a simple text input to be an autocompleting language menu |
| 455 | + // this will work when/if we move to jQuery 1.4. As of now the autocomplete is too underpowered for our needs without |
| 456 | + // serious hackery |
| 457 | + /* |
| 458 | + $j.fn.languageMenu = function( options ) { |
| 459 | + var _this = this; |
| 460 | + _this.autocomplete( null, { |
| 461 | + minChars: 0, |
| 462 | + width: 310, |
| 463 | + selectFirst: true, |
| 464 | + autoFill: true, |
| 465 | + mustMatch: true, |
| 466 | + matchContains: false, |
| 467 | + highlightItem: true, |
| 468 | + scroll: true, |
| 469 | + scrollHeight: 220, |
| 470 | + formatItem: function( row, i, max, term ) { |
| 471 | + return row.code + " " + row.code; |
| 472 | + }, |
| 473 | + formatMatch: function( row, i, max, term ) { |
| 474 | + return row.code + " " + row.code; |
| 475 | + }, |
| 476 | + formatResult: function( row ) { |
| 477 | + return row.code; |
| 478 | + } |
| 479 | + }, mw.Languages ); |
| 480 | + |
| 481 | + // and add a dropdown so we can see the thingy, too |
| 482 | + return _this; |
| 483 | + }; |
| 484 | + */ |
| 485 | + |
| 486 | + // XXX the concept of "internal language" exists in UploadForm.js -- seems to be how they handled i18n, with |
| 487 | + // language codes that has underscores rather than dashes, ( "en_gb" rather than the correct "en-gb" ). |
| 488 | + // although other info such as Information boxes was recorded correctly. |
| 489 | + // This is presumed not to apply to the shiny new world of JS2, where i18n is handled in other ways. |
| 490 | + |
| 491 | +}; |
Property changes on: trunk/extensions/UploadWizard/resources/mw.LanguageUpWiz.js |
___________________________________________________________________ |
Added: svn:mergeinfo |
1 | 492 | Merged /branches/sqlite/js2/mwEmbed/modules/UploadWizard/mw.Language.js:r58211-58321 |
2 | 493 | Merged /branches/REL1_15/phase3/js2/mwEmbed/modules/UploadWizard/mw.Language.js:r51646 |
Added: svn:eol-style |
3 | 494 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.tipsy.js |
— | — | @@ -0,0 +1,201 @@ |
| 2 | +// tipsy, facebook style tooltips for jquery |
| 3 | +// version 1.0.0a |
| 4 | +// (c) 2008-2010 jason frame [jason@onehackoranother.com] |
| 5 | +// releated under the MIT license |
| 6 | + |
| 7 | +(function($) { |
| 8 | + |
| 9 | + function fixTitle($ele) { |
| 10 | + if ($ele.attr('title') || typeof($ele.attr('original-title')) != 'string') { |
| 11 | + $ele.attr('original-title', $ele.attr('title') || '').removeAttr('title'); |
| 12 | + } |
| 13 | + } |
| 14 | + |
| 15 | + function Tipsy(element, options) { |
| 16 | + this.$element = $(element); |
| 17 | + this.options = options; |
| 18 | + this.enabled = true; |
| 19 | + fixTitle(this.$element); |
| 20 | + } |
| 21 | + |
| 22 | + Tipsy.prototype = { |
| 23 | + show: function() { |
| 24 | + var title = this.getTitle(); |
| 25 | + if (title && this.enabled) { |
| 26 | + var $tip = this.tip(); |
| 27 | + $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title); |
| 28 | + // $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity |
| 29 | + // the remove strips events |
| 30 | + //$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body); |
| 31 | + $tip.css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body); |
| 32 | + |
| 33 | + var pos = $.extend({}, this.$element.offset(), { |
| 34 | + width: this.$element[0].offsetWidth, |
| 35 | + height: this.$element[0].offsetHeight |
| 36 | + }); |
| 37 | + |
| 38 | + var actualWidth = $tip[0].offsetWidth, actualHeight = $tip[0].offsetHeight; |
| 39 | + var gravity = (typeof this.options.gravity == 'function') |
| 40 | + ? this.options.gravity.call(this.$element[0]) |
| 41 | + : this.options.gravity; |
| 42 | + |
| 43 | + var tp; |
| 44 | + switch (gravity.charAt(0)) { |
| 45 | + case 'n': |
| 46 | + tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; |
| 47 | + break; |
| 48 | + case 's': |
| 49 | + tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; |
| 50 | + break; |
| 51 | + case 'e': |
| 52 | + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}; |
| 53 | + break; |
| 54 | + case 'w': |
| 55 | + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}; |
| 56 | + break; |
| 57 | + } |
| 58 | + |
| 59 | + if (gravity.length == 2) { |
| 60 | + if (gravity.charAt(1) == 'w') { |
| 61 | + tp.left = pos.left + pos.width / 2 - 15; |
| 62 | + } else { |
| 63 | + tp.left = pos.left + pos.width / 2 - actualWidth + 15; |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + $tip.css(tp).addClass('tipsy-' + gravity); |
| 68 | + |
| 69 | + if (this.options.fade) { |
| 70 | + $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}); |
| 71 | + } else { |
| 72 | + $tip.css({visibility: 'visible', opacity: this.options.opacity}); |
| 73 | + } |
| 74 | + } |
| 75 | + }, |
| 76 | + |
| 77 | + hide: function() { |
| 78 | + if (!this.sticky) { |
| 79 | + if (this.options.fade) { |
| 80 | + this.tip().stop().fadeOut(function() { $(this).hide(); }); |
| 81 | + } else { |
| 82 | + this.tip().hide(); |
| 83 | + } |
| 84 | + } |
| 85 | + }, |
| 86 | + |
| 87 | + getTitle: function() { |
| 88 | + var title, $e = this.$element, o = this.options; |
| 89 | + fixTitle($e); |
| 90 | + var title, o = this.options; |
| 91 | + if (typeof o.title == 'string') { |
| 92 | + title = $e.attr(o.title == 'title' ? 'original-title' : o.title); |
| 93 | + title = ('' + title).replace(/(^\s*|\s*$)/, ""); |
| 94 | + } else if (typeof o.title == 'function') { |
| 95 | + title = o.title.call($e[0]); |
| 96 | + } |
| 97 | + return title || o.fallback; |
| 98 | + }, |
| 99 | + |
| 100 | + tip: function() { |
| 101 | + var type = 'tipsy-' + this.options.type; |
| 102 | + var shadow = this.options.shadow ? 'shadow' : ''; |
| 103 | + if (!this.$tip) { |
| 104 | + this.$tip = $('<div class="tipsy ' + type + '"></div>') |
| 105 | + .html('<div class="tipsy-arrow"></div><div class="tipsy-inner ' + shadow + '"/></div>'); |
| 106 | + } |
| 107 | + return this.$tip; |
| 108 | + }, |
| 109 | + |
| 110 | + validate: function() { |
| 111 | + if (!this.$element[0].parentNode) this.hide(); |
| 112 | + }, |
| 113 | + |
| 114 | + enable: function() { this.enabled = true; }, |
| 115 | + disable: function() { this.enabled = false; }, |
| 116 | + toggleEnabled: function() { this.enabled = !this.enabled; } |
| 117 | + }; |
| 118 | + |
| 119 | + $.fn.tipsy = function(options) { |
| 120 | + |
| 121 | + if (options === true) { |
| 122 | + return this.data('tipsy'); |
| 123 | + } else if (typeof options == 'string') { |
| 124 | + return this.data('tipsy')[options](); |
| 125 | + } |
| 126 | + |
| 127 | + options = $.extend({}, $.fn.tipsy.defaults, options); |
| 128 | + |
| 129 | + function get(ele) { |
| 130 | + var tipsy = $.data(ele, 'tipsy'); |
| 131 | + if (!tipsy) { |
| 132 | + tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options)); |
| 133 | + $.data(ele, 'tipsy', tipsy); |
| 134 | + } |
| 135 | + return tipsy; |
| 136 | + } |
| 137 | + |
| 138 | + function enter() { |
| 139 | + var tipsy = get(this); |
| 140 | + tipsy.hoverState = 'in'; |
| 141 | + if (options.delayIn == 0) { |
| 142 | + tipsy.show(); |
| 143 | + } else { |
| 144 | + setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn); |
| 145 | + } |
| 146 | + }; |
| 147 | + |
| 148 | + function leave() { |
| 149 | + var tipsy = get(this); |
| 150 | + tipsy.hoverState = 'out'; |
| 151 | + if (options.delayOut == 0) { |
| 152 | + tipsy.hide(); |
| 153 | + } else { |
| 154 | + setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut); |
| 155 | + } |
| 156 | + }; |
| 157 | + |
| 158 | + if (!options.live) this.each(function() { get(this); }); |
| 159 | + |
| 160 | + if (options.trigger != 'manual') { |
| 161 | + var binder = options.live ? 'live' : 'bind', |
| 162 | + eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', |
| 163 | + eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'; |
| 164 | + this[binder](eventIn, enter)[binder](eventOut, leave); |
| 165 | + } |
| 166 | + |
| 167 | + return this; |
| 168 | + |
| 169 | + }; |
| 170 | + |
| 171 | + $.fn.tipsy.defaults = { |
| 172 | + delayIn: 0, |
| 173 | + delayOut: 0, |
| 174 | + fade: false, |
| 175 | + fallback: '', |
| 176 | + gravity: 'n', |
| 177 | + html: false, |
| 178 | + live: false, |
| 179 | + offset: 0, |
| 180 | + opacity: 1.0, |
| 181 | + title: 'title', |
| 182 | + trigger: 'hover', |
| 183 | + type: 'help' |
| 184 | + }; |
| 185 | + |
| 186 | + // Overwrite this method to provide options on a per-element basis. |
| 187 | + // For example, you could store the gravity in a 'tipsy-gravity' attribute: |
| 188 | + // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' }); |
| 189 | + // (remember - do not modify 'options' in place!) |
| 190 | + $.fn.tipsy.elementOptions = function(ele, options) { |
| 191 | + return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; |
| 192 | + }; |
| 193 | + |
| 194 | + $.fn.tipsy.autoNS = function() { |
| 195 | + return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; |
| 196 | + }; |
| 197 | + |
| 198 | + $.fn.tipsy.autoWE = function() { |
| 199 | + return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; |
| 200 | + }; |
| 201 | + |
| 202 | +})(jQuery); |
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.tipsy.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 203 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.arrowSteps.js |
— | — | @@ -0,0 +1,81 @@ |
| 2 | +/** |
| 3 | + * jQuery arrowSteps plugin |
| 4 | + * Copyright Neil Kandalgaonkar, 2010 |
| 5 | + * |
| 6 | + * This work is licensed under the terms of the GNU General Public License, |
| 7 | + * version 2 or later. |
| 8 | + * (see http://www.fsf.org/licensing/licenses/gpl.html). |
| 9 | + * Derivative works and later versions of the code must be free software |
| 10 | + * licensed under the same or a compatible license. |
| 11 | + * |
| 12 | + * |
| 13 | + * DESCRIPTION |
| 14 | + * |
| 15 | + * Show users their progress through a series of steps, via a row of items that fit |
| 16 | + * together like arrows. One item can be highlighted at a time. |
| 17 | + * |
| 18 | + * |
| 19 | + * SYNOPSIS |
| 20 | + * |
| 21 | + * <ul id="robin-hood-daffy"> |
| 22 | + * <li id="guard"><div>Guard!</div></li> |
| 23 | + * <li id="turn"><div>Turn!</div></li> |
| 24 | + * <li id="parry"><div>Parry!</div></li> |
| 25 | + * <li id="dodge"><div>Dodge!</div></li> |
| 26 | + * <li id="spin"><div>Spin!</div></li> |
| 27 | + * <li id="ha"><div>Ha!</div></li> |
| 28 | + * <li id="thrust"><div>Thrust!</div></li> |
| 29 | + * </ul> |
| 30 | + * |
| 31 | + * <script language="javascript"><!-- |
| 32 | + * $( '#robin-hood-daffy' ).arrowSteps(); |
| 33 | + * |
| 34 | + * $( '#robin-hood-daffy' ).arrowStepsHighlight( '#guard' ); |
| 35 | + * // 'Guard!' is highlighted. |
| 36 | + * |
| 37 | + * // ... user completes the 'guard' step ... |
| 38 | + * |
| 39 | + * $( '#robin-hood-daffy' ).arrowStepsHighlight( '#turn' ); |
| 40 | + * // 'Turn!' is highlighted. |
| 41 | + * |
| 42 | + * //--> |
| 43 | + * </script> |
| 44 | + * |
| 45 | + */ |
| 46 | + |
| 47 | +( function( $j ) { |
| 48 | + $j.fn.arrowSteps = function() { |
| 49 | + this.addClass( 'arrowSteps' ); |
| 50 | + var $steps = this.find( 'li' ); |
| 51 | + |
| 52 | + var width = parseInt( 100 / $steps.length, 10 ); |
| 53 | + $steps.css( 'width', width + '%' ); |
| 54 | + |
| 55 | + // every step except the last one has an arrow at the right hand side. Also add in the padding |
| 56 | + // for the calculated arrow width. |
| 57 | + var arrowWidth = parseInt( this.outerHeight(), 10 ); |
| 58 | + $steps.filter( ':not(:last-child)' ).addClass( 'arrow' ) |
| 59 | + .find( 'div' ).css( 'padding-right', arrowWidth.toString() + 'px' ); |
| 60 | + |
| 61 | + this.data( 'arrowSteps', $steps ); |
| 62 | + return this; |
| 63 | + }; |
| 64 | + |
| 65 | + $j.fn.arrowStepsHighlight = function( selector ) { |
| 66 | + var $steps = this.data( 'arrowSteps' ); |
| 67 | + var $previous; |
| 68 | + $j.each( $steps, function( i, step ) { |
| 69 | + var $step = $j( step ); |
| 70 | + if ( $step.is( selector ) ) { |
| 71 | + if ($previous) { |
| 72 | + $previous.addClass( 'tail' ); |
| 73 | + } |
| 74 | + $step.addClass( 'head' ); |
| 75 | + } else { |
| 76 | + $step.removeClass( 'head tail lasthead' ); |
| 77 | + } |
| 78 | + $previous = $step; |
| 79 | + } ); |
| 80 | + }; |
| 81 | + |
| 82 | +} )( jQuery ); |
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.arrowSteps.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 83 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.validate.js |
— | — | @@ -0,0 +1,1145 @@ |
| 2 | +/* |
| 3 | + * jQuery validation plug-in 1.7 |
| 4 | + * |
| 5 | + * http://bassistance.de/jquery-plugins/jquery-plugin-validation/ |
| 6 | + * http://docs.jquery.com/Plugins/Validation |
| 7 | + * |
| 8 | + * Copyright (c) 2006 - 2008 Jörn Zaefferer |
| 9 | + * |
| 10 | + * $Id: jquery.validate.js 6403 2009-06-17 14:27:16Z joern.zaefferer $ |
| 11 | + * |
| 12 | + * Dual licensed under the MIT and GPL licenses: |
| 13 | + * http://www.opensource.org/licenses/mit-license.php |
| 14 | + * http://www.gnu.org/licenses/gpl.html |
| 15 | + */ |
| 16 | + |
| 17 | +(function($) { |
| 18 | + |
| 19 | +$.extend($.fn, { |
| 20 | + // http://docs.jquery.com/Plugins/Validation/validate |
| 21 | + validate: function( options ) { |
| 22 | + |
| 23 | + // if nothing is selected, return nothing; can't chain anyway |
| 24 | + if (!this.length) { |
| 25 | + options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" ); |
| 26 | + return; |
| 27 | + } |
| 28 | + |
| 29 | + // check if a validator for this form was already created |
| 30 | + var validator = $.data(this[0], 'validator'); |
| 31 | + if ( validator ) { |
| 32 | + return validator; |
| 33 | + } |
| 34 | + |
| 35 | + validator = new $.validator( options, this[0] ); |
| 36 | + $.data(this[0], 'validator', validator); |
| 37 | + |
| 38 | + if ( validator.settings.onsubmit ) { |
| 39 | + |
| 40 | + // allow suppresing validation by adding a cancel class to the submit button |
| 41 | + this.find("input, button").filter(".cancel").click(function() { |
| 42 | + validator.cancelSubmit = true; |
| 43 | + }); |
| 44 | + |
| 45 | + // when a submitHandler is used, capture the submitting button |
| 46 | + if (validator.settings.submitHandler) { |
| 47 | + this.find("input, button").filter(":submit").click(function() { |
| 48 | + validator.submitButton = this; |
| 49 | + }); |
| 50 | + } |
| 51 | + |
| 52 | + // validate the form on submit |
| 53 | + this.submit( function( event ) { |
| 54 | + if ( validator.settings.debug ) |
| 55 | + // prevent form submit to be able to see console output |
| 56 | + event.preventDefault(); |
| 57 | + |
| 58 | + function handle() { |
| 59 | + if ( validator.settings.submitHandler ) { |
| 60 | + if (validator.submitButton) { |
| 61 | + // insert a hidden input as a replacement for the missing submit button |
| 62 | + var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm); |
| 63 | + } |
| 64 | + validator.settings.submitHandler.call( validator, validator.currentForm ); |
| 65 | + if (validator.submitButton) { |
| 66 | + // and clean up afterwards; thanks to no-block-scope, hidden can be referenced |
| 67 | + hidden.remove(); |
| 68 | + } |
| 69 | + return false; |
| 70 | + } |
| 71 | + return true; |
| 72 | + } |
| 73 | + |
| 74 | + // prevent submit for invalid forms or custom submit handlers |
| 75 | + if ( validator.cancelSubmit ) { |
| 76 | + validator.cancelSubmit = false; |
| 77 | + return handle(); |
| 78 | + } |
| 79 | + if ( validator.form() ) { |
| 80 | + if ( validator.pendingRequest ) { |
| 81 | + validator.formSubmitted = true; |
| 82 | + return false; |
| 83 | + } |
| 84 | + return handle(); |
| 85 | + } else { |
| 86 | + validator.focusInvalid(); |
| 87 | + return false; |
| 88 | + } |
| 89 | + }); |
| 90 | + } |
| 91 | + |
| 92 | + return validator; |
| 93 | + }, |
| 94 | + // http://docs.jquery.com/Plugins/Validation/valid |
| 95 | + valid: function() { |
| 96 | + if ( $(this[0]).is('form')) { |
| 97 | + return this.validate().form(); |
| 98 | + } else { |
| 99 | + var valid = true; |
| 100 | + var validator = $(this[0].form).validate(); |
| 101 | + this.each(function() { |
| 102 | + valid &= validator.element(this); |
| 103 | + }); |
| 104 | + return valid; |
| 105 | + } |
| 106 | + }, |
| 107 | + // attributes: space seperated list of attributes to retrieve and remove |
| 108 | + removeAttrs: function(attributes) { |
| 109 | + var result = {}, |
| 110 | + $element = this; |
| 111 | + $.each(attributes.split(/\s/), function(index, value) { |
| 112 | + result[value] = $element.attr(value); |
| 113 | + $element.removeAttr(value); |
| 114 | + }); |
| 115 | + return result; |
| 116 | + }, |
| 117 | + // http://docs.jquery.com/Plugins/Validation/rules |
| 118 | + rules: function(command, argument) { |
| 119 | + var element = this[0]; |
| 120 | + |
| 121 | + if (command) { |
| 122 | + var settings = $.data(element.form, 'validator').settings; |
| 123 | + var staticRules = settings.rules; |
| 124 | + var existingRules = $.validator.staticRules(element); |
| 125 | + switch(command) { |
| 126 | + case "add": |
| 127 | + $.extend(existingRules, $.validator.normalizeRule(argument)); |
| 128 | + staticRules[element.name] = existingRules; |
| 129 | + if (argument.messages) |
| 130 | + settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages ); |
| 131 | + break; |
| 132 | + case "remove": |
| 133 | + if (!argument) { |
| 134 | + delete staticRules[element.name]; |
| 135 | + return existingRules; |
| 136 | + } |
| 137 | + var filtered = {}; |
| 138 | + $.each(argument.split(/\s/), function(index, method) { |
| 139 | + filtered[method] = existingRules[method]; |
| 140 | + delete existingRules[method]; |
| 141 | + }); |
| 142 | + return filtered; |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + var data = $.validator.normalizeRules( |
| 147 | + $.extend( |
| 148 | + {}, |
| 149 | + $.validator.metadataRules(element), |
| 150 | + $.validator.classRules(element), |
| 151 | + $.validator.attributeRules(element), |
| 152 | + $.validator.staticRules(element) |
| 153 | + ), element); |
| 154 | + |
| 155 | + // make sure required is at front |
| 156 | + if (data.required) { |
| 157 | + var param = data.required; |
| 158 | + delete data.required; |
| 159 | + data = $.extend({required: param}, data); |
| 160 | + } |
| 161 | + |
| 162 | + return data; |
| 163 | + } |
| 164 | +}); |
| 165 | + |
| 166 | +// Custom selectors |
| 167 | +$.extend($.expr[":"], { |
| 168 | + // http://docs.jquery.com/Plugins/Validation/blank |
| 169 | + blank: function(a) {return !$.trim("" + a.value);}, |
| 170 | + // http://docs.jquery.com/Plugins/Validation/filled |
| 171 | + filled: function(a) {return !!$.trim("" + a.value);}, |
| 172 | + // http://docs.jquery.com/Plugins/Validation/unchecked |
| 173 | + unchecked: function(a) {return !a.checked;} |
| 174 | +}); |
| 175 | + |
| 176 | +// constructor for validator |
| 177 | +$.validator = function( options, form ) { |
| 178 | + this.settings = $.extend( true, {}, $.validator.defaults, options ); |
| 179 | + this.currentForm = form; |
| 180 | + this.init(); |
| 181 | +}; |
| 182 | + |
| 183 | +$.validator.format = function(source, params) { |
| 184 | + if ( arguments.length == 1 ) |
| 185 | + return function() { |
| 186 | + var args = $.makeArray(arguments); |
| 187 | + args.unshift(source); |
| 188 | + return $.validator.format.apply( this, args ); |
| 189 | + }; |
| 190 | + if ( arguments.length > 2 && params.constructor != Array ) { |
| 191 | + params = $.makeArray(arguments).slice(1); |
| 192 | + } |
| 193 | + if ( params.constructor != Array ) { |
| 194 | + params = [ params ]; |
| 195 | + } |
| 196 | + $.each(params, function(i, n) { |
| 197 | + source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n); |
| 198 | + }); |
| 199 | + return source; |
| 200 | +}; |
| 201 | + |
| 202 | +$.extend($.validator, { |
| 203 | + |
| 204 | + defaults: { |
| 205 | + messages: {}, |
| 206 | + groups: {}, |
| 207 | + rules: {}, |
| 208 | + errorClass: "error", |
| 209 | + validClass: "valid", |
| 210 | + errorElement: "label", |
| 211 | + focusInvalid: true, |
| 212 | + errorContainer: $( [] ), |
| 213 | + errorLabelContainer: $( [] ), |
| 214 | + onsubmit: true, |
| 215 | + ignore: [], |
| 216 | + ignoreTitle: false, |
| 217 | + onfocusin: function(element) { |
| 218 | + this.lastActive = element; |
| 219 | + |
| 220 | + // hide error label and remove error class on focus if enabled |
| 221 | + if ( this.settings.focusCleanup && !this.blockFocusCleanup ) { |
| 222 | + this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); |
| 223 | + this.errorsFor(element).hide(); |
| 224 | + } |
| 225 | + }, |
| 226 | + onfocusout: function(element) { |
| 227 | + if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) { |
| 228 | + this.element(element); |
| 229 | + } |
| 230 | + }, |
| 231 | + onkeyup: function(element) { |
| 232 | + if ( element.name in this.submitted || element == this.lastElement ) { |
| 233 | + this.element(element); |
| 234 | + } |
| 235 | + }, |
| 236 | + onclick: function(element) { |
| 237 | + // click on selects, radiobuttons and checkboxes |
| 238 | + if ( element.name in this.submitted ) |
| 239 | + this.element(element); |
| 240 | + // or option elements, check parent select in that case |
| 241 | + else if (element.parentNode.name in this.submitted) |
| 242 | + this.element(element.parentNode); |
| 243 | + }, |
| 244 | + highlight: function( element, errorClass, validClass ) { |
| 245 | + $(element).addClass(errorClass).removeClass(validClass); |
| 246 | + }, |
| 247 | + unhighlight: function( element, errorClass, validClass ) { |
| 248 | + $(element).removeClass(errorClass).addClass(validClass); |
| 249 | + } |
| 250 | + }, |
| 251 | + |
| 252 | + // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults |
| 253 | + setDefaults: function(settings) { |
| 254 | + $.extend( $.validator.defaults, settings ); |
| 255 | + }, |
| 256 | + |
| 257 | + messages: { |
| 258 | + required: "This field is required.", |
| 259 | + remote: "Please fix this field.", |
| 260 | + email: "Please enter a valid email address.", |
| 261 | + url: "Please enter a valid URL.", |
| 262 | + date: "Please enter a valid date.", |
| 263 | + dateISO: "Please enter a valid date (ISO).", |
| 264 | + number: "Please enter a valid number.", |
| 265 | + digits: "Please enter only digits.", |
| 266 | + creditcard: "Please enter a valid credit card number.", |
| 267 | + equalTo: "Please enter the same value again.", |
| 268 | + accept: "Please enter a value with a valid extension.", |
| 269 | + maxlength: $.validator.format("Please enter no more than {0} characters."), |
| 270 | + minlength: $.validator.format("Please enter at least {0} characters."), |
| 271 | + rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."), |
| 272 | + range: $.validator.format("Please enter a value between {0} and {1}."), |
| 273 | + max: $.validator.format("Please enter a value less than or equal to {0}."), |
| 274 | + min: $.validator.format("Please enter a value greater than or equal to {0}.") |
| 275 | + }, |
| 276 | + |
| 277 | + autoCreateRanges: false, |
| 278 | + |
| 279 | + prototype: { |
| 280 | + |
| 281 | + init: function() { |
| 282 | + this.labelContainer = $(this.settings.errorLabelContainer); |
| 283 | + this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm); |
| 284 | + this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer ); |
| 285 | + this.submitted = {}; |
| 286 | + this.valueCache = {}; |
| 287 | + this.pendingRequest = 0; |
| 288 | + this.pending = {}; |
| 289 | + this.invalid = {}; |
| 290 | + this.reset(); |
| 291 | + |
| 292 | + var groups = (this.groups = {}); |
| 293 | + $.each(this.settings.groups, function(key, value) { |
| 294 | + $.each(value.split(/\s/), function(index, name) { |
| 295 | + groups[name] = key; |
| 296 | + }); |
| 297 | + }); |
| 298 | + var rules = this.settings.rules; |
| 299 | + $.each(rules, function(key, value) { |
| 300 | + rules[key] = $.validator.normalizeRule(value); |
| 301 | + }); |
| 302 | + |
| 303 | + function delegate(event) { |
| 304 | + var validator = $.data(this[0].form, "validator"), |
| 305 | + eventType = "on" + event.type.replace(/^validate/, ""); |
| 306 | + validator.settings[eventType] && validator.settings[eventType].call(validator, this[0] ); |
| 307 | + } |
| 308 | + $(this.currentForm) |
| 309 | + .validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate) |
| 310 | + .validateDelegate(":radio, :checkbox, select, option", "click", delegate); |
| 311 | + |
| 312 | + if (this.settings.invalidHandler) |
| 313 | + $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler); |
| 314 | + }, |
| 315 | + |
| 316 | + // http://docs.jquery.com/Plugins/Validation/Validator/form |
| 317 | + form: function() { |
| 318 | + this.checkForm(); |
| 319 | + $.extend(this.submitted, this.errorMap); |
| 320 | + this.invalid = $.extend({}, this.errorMap); |
| 321 | + if (!this.valid()) |
| 322 | + $(this.currentForm).triggerHandler("invalid-form", [this]); |
| 323 | + this.showErrors(); |
| 324 | + return this.valid(); |
| 325 | + }, |
| 326 | + |
| 327 | + checkForm: function() { |
| 328 | + this.prepareForm(); |
| 329 | + for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) { |
| 330 | + this.check( elements[i] ); |
| 331 | + } |
| 332 | + return this.valid(); |
| 333 | + }, |
| 334 | + |
| 335 | + // http://docs.jquery.com/Plugins/Validation/Validator/element |
| 336 | + element: function( element ) { |
| 337 | + element = this.clean( element ); |
| 338 | + this.lastElement = element; |
| 339 | + this.prepareElement( element ); |
| 340 | + this.currentElements = $(element); |
| 341 | + var result = this.check( element ); |
| 342 | + if ( result ) { |
| 343 | + delete this.invalid[element.name]; |
| 344 | + } else { |
| 345 | + this.invalid[element.name] = true; |
| 346 | + } |
| 347 | + if ( !this.numberOfInvalids() ) { |
| 348 | + // Hide error containers on last error |
| 349 | + this.toHide = this.toHide.add( this.containers ); |
| 350 | + } |
| 351 | + this.showErrors(); |
| 352 | + return result; |
| 353 | + }, |
| 354 | + |
| 355 | + // http://docs.jquery.com/Plugins/Validation/Validator/showErrors |
| 356 | + showErrors: function(errors) { |
| 357 | + if(errors) { |
| 358 | + // add items to error list and map |
| 359 | + $.extend( this.errorMap, errors ); |
| 360 | + this.errorList = []; |
| 361 | + for ( var name in errors ) { |
| 362 | + this.errorList.push({ |
| 363 | + message: errors[name], |
| 364 | + element: this.findByName(name)[0] |
| 365 | + }); |
| 366 | + } |
| 367 | + // remove items from success list |
| 368 | + this.successList = $.grep( this.successList, function(element) { |
| 369 | + return !(element.name in errors); |
| 370 | + }); |
| 371 | + } |
| 372 | + this.settings.showErrors |
| 373 | + ? this.settings.showErrors.call( this, this.errorMap, this.errorList ) |
| 374 | + : this.defaultShowErrors(); |
| 375 | + }, |
| 376 | + |
| 377 | + // http://docs.jquery.com/Plugins/Validation/Validator/resetForm |
| 378 | + resetForm: function() { |
| 379 | + if ( $.fn.resetForm ) |
| 380 | + $( this.currentForm ).resetForm(); |
| 381 | + this.submitted = {}; |
| 382 | + this.prepareForm(); |
| 383 | + this.hideErrors(); |
| 384 | + this.elements().removeClass( this.settings.errorClass ); |
| 385 | + }, |
| 386 | + |
| 387 | + numberOfInvalids: function() { |
| 388 | + return this.objectLength(this.invalid); |
| 389 | + }, |
| 390 | + |
| 391 | + objectLength: function( obj ) { |
| 392 | + var count = 0; |
| 393 | + for ( var i in obj ) |
| 394 | + count++; |
| 395 | + return count; |
| 396 | + }, |
| 397 | + |
| 398 | + hideErrors: function() { |
| 399 | + this.addWrapper( this.toHide ).hide(); |
| 400 | + }, |
| 401 | + |
| 402 | + valid: function() { |
| 403 | + return this.size() == 0; |
| 404 | + }, |
| 405 | + |
| 406 | + size: function() { |
| 407 | + return this.errorList.length; |
| 408 | + }, |
| 409 | + |
| 410 | + focusInvalid: function() { |
| 411 | + if( this.settings.focusInvalid ) { |
| 412 | + try { |
| 413 | + $(this.findLastActive() || this.errorList.length && this.errorList[0].element || []) |
| 414 | + .filter(":visible") |
| 415 | + .focus() |
| 416 | + // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find |
| 417 | + .trigger("focusin"); |
| 418 | + } catch(e) { |
| 419 | + // ignore IE throwing errors when focusing hidden elements |
| 420 | + } |
| 421 | + } |
| 422 | + }, |
| 423 | + |
| 424 | + findLastActive: function() { |
| 425 | + var lastActive = this.lastActive; |
| 426 | + return lastActive && $.grep(this.errorList, function(n) { |
| 427 | + return n.element.name == lastActive.name; |
| 428 | + }).length == 1 && lastActive; |
| 429 | + }, |
| 430 | + |
| 431 | + elements: function() { |
| 432 | + var validator = this, |
| 433 | + rulesCache = {}; |
| 434 | + |
| 435 | + // select all valid inputs inside the form (no submit or reset buttons) |
| 436 | + // workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved |
| 437 | + return $([]).add(this.currentForm.elements) |
| 438 | + .filter(":input") |
| 439 | + .not(":submit, :reset, :image, [disabled]") |
| 440 | + .not( this.settings.ignore ) |
| 441 | + .filter(function() { |
| 442 | + !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this); |
| 443 | + |
| 444 | + // select only the first element for each name, and only those with rules specified |
| 445 | + if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) |
| 446 | + return false; |
| 447 | + |
| 448 | + rulesCache[this.name] = true; |
| 449 | + return true; |
| 450 | + }); |
| 451 | + }, |
| 452 | + |
| 453 | + clean: function( selector ) { |
| 454 | + return $( selector )[0]; |
| 455 | + }, |
| 456 | + |
| 457 | + errors: function() { |
| 458 | + return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext ); |
| 459 | + }, |
| 460 | + |
| 461 | + reset: function() { |
| 462 | + this.successList = []; |
| 463 | + this.errorList = []; |
| 464 | + this.errorMap = {}; |
| 465 | + this.toShow = $([]); |
| 466 | + this.toHide = $([]); |
| 467 | + this.currentElements = $([]); |
| 468 | + }, |
| 469 | + |
| 470 | + prepareForm: function() { |
| 471 | + this.reset(); |
| 472 | + this.toHide = this.errors().add( this.containers ); |
| 473 | + }, |
| 474 | + |
| 475 | + prepareElement: function( element ) { |
| 476 | + this.reset(); |
| 477 | + this.toHide = this.errorsFor(element); |
| 478 | + }, |
| 479 | + |
| 480 | + check: function( element ) { |
| 481 | + element = this.clean( element ); |
| 482 | + |
| 483 | + // if radio/checkbox, validate first element in group instead |
| 484 | + if (this.checkable(element)) { |
| 485 | + element = this.findByName( element.name )[0]; |
| 486 | + } |
| 487 | + var rules = $(element).rules(); |
| 488 | + var dependencyMismatch = false; |
| 489 | + for( method in rules ) { |
| 490 | + var rule = { method: method, parameters: rules[method] }; |
| 491 | + try { |
| 492 | + var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters ); |
| 493 | + |
| 494 | + // if a method indicates that the field is optional and therefore valid, |
| 495 | + // don't mark it as valid when there are no other rules |
| 496 | + if ( result == "dependency-mismatch" ) { |
| 497 | + dependencyMismatch = true; |
| 498 | + continue; |
| 499 | + } |
| 500 | + dependencyMismatch = false; |
| 501 | + |
| 502 | + if ( result == "pending" ) { |
| 503 | + this.toHide = this.toHide.not( this.errorsFor(element) ); |
| 504 | + return; |
| 505 | + } |
| 506 | + |
| 507 | + if( !result ) { |
| 508 | + this.formatAndAdd( element, rule ); |
| 509 | + return false; |
| 510 | + } |
| 511 | + } catch(e) { |
| 512 | + this.settings.debug && window.console && console.log("exception occured when checking element " + element.id |
| 513 | + + ", check the '" + rule.method + "' method", e); |
| 514 | + throw e; |
| 515 | + } |
| 516 | + } |
| 517 | + if (dependencyMismatch) |
| 518 | + return; |
| 519 | + if ( this.objectLength(rules) ) |
| 520 | + this.successList.push(element); |
| 521 | + return true; |
| 522 | + }, |
| 523 | + |
| 524 | + // return the custom message for the given element and validation method |
| 525 | + // specified in the element's "messages" metadata |
| 526 | + customMetaMessage: function(element, method) { |
| 527 | + if (!$.metadata) |
| 528 | + return; |
| 529 | + |
| 530 | + var meta = this.settings.meta |
| 531 | + ? $(element).metadata()[this.settings.meta] |
| 532 | + : $(element).metadata(); |
| 533 | + |
| 534 | + return meta && meta.messages && meta.messages[method]; |
| 535 | + }, |
| 536 | + |
| 537 | + // return the custom message for the given element name and validation method |
| 538 | + customMessage: function( name, method ) { |
| 539 | + var m = this.settings.messages[name]; |
| 540 | + return m && (m.constructor == String |
| 541 | + ? m |
| 542 | + : m[method]); |
| 543 | + }, |
| 544 | + |
| 545 | + // return the first defined argument, allowing empty strings |
| 546 | + findDefined: function() { |
| 547 | + for(var i = 0; i < arguments.length; i++) { |
| 548 | + if (arguments[i] !== undefined) |
| 549 | + return arguments[i]; |
| 550 | + } |
| 551 | + return undefined; |
| 552 | + }, |
| 553 | + |
| 554 | + defaultMessage: function( element, method) { |
| 555 | + return this.findDefined( |
| 556 | + this.customMessage( element.name, method ), |
| 557 | + this.customMetaMessage( element, method ), |
| 558 | + // title is never undefined, so handle empty string as undefined |
| 559 | + !this.settings.ignoreTitle && element.title || undefined, |
| 560 | + $.validator.messages[method], |
| 561 | + "<strong>Warning: No message defined for " + element.name + "</strong>" |
| 562 | + ); |
| 563 | + }, |
| 564 | + |
| 565 | + formatAndAdd: function( element, rule ) { |
| 566 | + var message = this.defaultMessage( element, rule.method ), |
| 567 | + theregex = /\$?\{(\d+)\}/g; |
| 568 | + if ( typeof message == "function" ) { |
| 569 | + message = message.call(this, rule.parameters, element); |
| 570 | + } else if (theregex.test(message)) { |
| 571 | + message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters); |
| 572 | + } |
| 573 | + this.errorList.push({ |
| 574 | + message: message, |
| 575 | + element: element |
| 576 | + }); |
| 577 | + |
| 578 | + this.errorMap[element.name] = message; |
| 579 | + this.submitted[element.name] = message; |
| 580 | + }, |
| 581 | + |
| 582 | + addWrapper: function(toToggle) { |
| 583 | + if ( this.settings.wrapper ) |
| 584 | + toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); |
| 585 | + return toToggle; |
| 586 | + }, |
| 587 | + |
| 588 | + defaultShowErrors: function() { |
| 589 | + for ( var i = 0; this.errorList[i]; i++ ) { |
| 590 | + var error = this.errorList[i]; |
| 591 | + this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); |
| 592 | + this.showLabel( error.element, error.message ); |
| 593 | + } |
| 594 | + if( this.errorList.length ) { |
| 595 | + this.toShow = this.toShow.add( this.containers ); |
| 596 | + } |
| 597 | + if (this.settings.success) { |
| 598 | + for ( var i = 0; this.successList[i]; i++ ) { |
| 599 | + this.showLabel( this.successList[i] ); |
| 600 | + } |
| 601 | + } |
| 602 | + if (this.settings.unhighlight) { |
| 603 | + for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) { |
| 604 | + this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass ); |
| 605 | + } |
| 606 | + } |
| 607 | + this.toHide = this.toHide.not( this.toShow ); |
| 608 | + this.hideErrors(); |
| 609 | + this.addWrapper( this.toShow ).show(); |
| 610 | + }, |
| 611 | + |
| 612 | + validElements: function() { |
| 613 | + return this.currentElements.not(this.invalidElements()); |
| 614 | + }, |
| 615 | + |
| 616 | + invalidElements: function() { |
| 617 | + return $(this.errorList).map(function() { |
| 618 | + return this.element; |
| 619 | + }); |
| 620 | + }, |
| 621 | + |
| 622 | + showLabel: function(element, message) { |
| 623 | + var label = this.errorsFor( element ); |
| 624 | + if ( label.length ) { |
| 625 | + // refresh error/success class |
| 626 | + label.removeClass().addClass( this.settings.errorClass ); |
| 627 | + |
| 628 | + // check if we have a generated label, replace the message then |
| 629 | + label.attr("generated") && label.html(message); |
| 630 | + } else { |
| 631 | + // create label |
| 632 | + label = $("<" + this.settings.errorElement + "/>") |
| 633 | + .attr({"for": this.idOrName(element), generated: true}) |
| 634 | + .addClass(this.settings.errorClass) |
| 635 | + .html(message || ""); |
| 636 | + if ( this.settings.wrapper ) { |
| 637 | + // make sure the element is visible, even in IE |
| 638 | + // actually showing the wrapped element is handled elsewhere |
| 639 | + label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent(); |
| 640 | + } |
| 641 | + if ( !this.labelContainer.append(label).length ) |
| 642 | + this.settings.errorPlacement |
| 643 | + ? this.settings.errorPlacement(label, $(element) ) |
| 644 | + : label.insertAfter(element); |
| 645 | + } |
| 646 | + if ( !message && this.settings.success ) { |
| 647 | + label.text(""); |
| 648 | + typeof this.settings.success == "string" |
| 649 | + ? label.addClass( this.settings.success ) |
| 650 | + : this.settings.success( label ); |
| 651 | + } |
| 652 | + this.toShow = this.toShow.add(label); |
| 653 | + }, |
| 654 | + |
| 655 | + errorsFor: function(element) { |
| 656 | + var name = this.idOrName(element); |
| 657 | + return this.errors().filter(function() { |
| 658 | + return $(this).attr('for') == name; |
| 659 | + }); |
| 660 | + }, |
| 661 | + |
| 662 | + idOrName: function(element) { |
| 663 | + return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name); |
| 664 | + }, |
| 665 | + |
| 666 | + checkable: function( element ) { |
| 667 | + return /radio|checkbox/i.test(element.type); |
| 668 | + }, |
| 669 | + |
| 670 | + findByName: function( name ) { |
| 671 | + // select by name and filter by form for performance over form.find("[name=...]") |
| 672 | + var form = this.currentForm; |
| 673 | + return $(document.getElementsByName(name)).map(function(index, element) { |
| 674 | + return element.form == form && element.name == name && element || null; |
| 675 | + }); |
| 676 | + }, |
| 677 | + |
| 678 | + getLength: function(value, element) { |
| 679 | + switch( element.nodeName.toLowerCase() ) { |
| 680 | + case 'select': |
| 681 | + return $("option:selected", element).length; |
| 682 | + case 'input': |
| 683 | + if( this.checkable( element) ) |
| 684 | + return this.findByName(element.name).filter(':checked').length; |
| 685 | + } |
| 686 | + return value.length; |
| 687 | + }, |
| 688 | + |
| 689 | + depend: function(param, element) { |
| 690 | + return this.dependTypes[typeof param] |
| 691 | + ? this.dependTypes[typeof param](param, element) |
| 692 | + : true; |
| 693 | + }, |
| 694 | + |
| 695 | + dependTypes: { |
| 696 | + "boolean": function(param, element) { |
| 697 | + return param; |
| 698 | + }, |
| 699 | + "string": function(param, element) { |
| 700 | + return !!$(param, element.form).length; |
| 701 | + }, |
| 702 | + "function": function(param, element) { |
| 703 | + return param(element); |
| 704 | + } |
| 705 | + }, |
| 706 | + |
| 707 | + optional: function(element) { |
| 708 | + return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch"; |
| 709 | + }, |
| 710 | + |
| 711 | + startRequest: function(element) { |
| 712 | + if (!this.pending[element.name]) { |
| 713 | + this.pendingRequest++; |
| 714 | + this.pending[element.name] = true; |
| 715 | + } |
| 716 | + }, |
| 717 | + |
| 718 | + stopRequest: function(element, valid) { |
| 719 | + this.pendingRequest--; |
| 720 | + // sometimes synchronization fails, make sure pendingRequest is never < 0 |
| 721 | + if (this.pendingRequest < 0) |
| 722 | + this.pendingRequest = 0; |
| 723 | + delete this.pending[element.name]; |
| 724 | + if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) { |
| 725 | + $(this.currentForm).submit(); |
| 726 | + this.formSubmitted = false; |
| 727 | + } else if (!valid && this.pendingRequest == 0 && this.formSubmitted) { |
| 728 | + $(this.currentForm).triggerHandler("invalid-form", [this]); |
| 729 | + this.formSubmitted = false; |
| 730 | + } |
| 731 | + }, |
| 732 | + |
| 733 | + previousValue: function(element) { |
| 734 | + return $.data(element, "previousValue") || $.data(element, "previousValue", { |
| 735 | + old: null, |
| 736 | + valid: true, |
| 737 | + message: this.defaultMessage( element, "remote" ) |
| 738 | + }); |
| 739 | + } |
| 740 | + |
| 741 | + }, |
| 742 | + |
| 743 | + classRuleSettings: { |
| 744 | + required: {required: true}, |
| 745 | + email: {email: true}, |
| 746 | + url: {url: true}, |
| 747 | + date: {date: true}, |
| 748 | + dateISO: {dateISO: true}, |
| 749 | + dateDE: {dateDE: true}, |
| 750 | + number: {number: true}, |
| 751 | + numberDE: {numberDE: true}, |
| 752 | + digits: {digits: true}, |
| 753 | + creditcard: {creditcard: true} |
| 754 | + }, |
| 755 | + |
| 756 | + addClassRules: function(className, rules) { |
| 757 | + className.constructor == String ? |
| 758 | + this.classRuleSettings[className] = rules : |
| 759 | + $.extend(this.classRuleSettings, className); |
| 760 | + }, |
| 761 | + |
| 762 | + classRules: function(element) { |
| 763 | + var rules = {}; |
| 764 | + var classes = $(element).attr('class'); |
| 765 | + classes && $.each(classes.split(' '), function() { |
| 766 | + if (this in $.validator.classRuleSettings) { |
| 767 | + $.extend(rules, $.validator.classRuleSettings[this]); |
| 768 | + } |
| 769 | + }); |
| 770 | + return rules; |
| 771 | + }, |
| 772 | + |
| 773 | + attributeRules: function(element) { |
| 774 | + var rules = {}; |
| 775 | + var $element = $(element); |
| 776 | + |
| 777 | + for (method in $.validator.methods) { |
| 778 | + var value = $element.attr(method); |
| 779 | + if (value) { |
| 780 | + rules[method] = value; |
| 781 | + } |
| 782 | + } |
| 783 | + |
| 784 | + // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs |
| 785 | + if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) { |
| 786 | + delete rules.maxlength; |
| 787 | + } |
| 788 | + |
| 789 | + return rules; |
| 790 | + }, |
| 791 | + |
| 792 | + metadataRules: function(element) { |
| 793 | + if (!$.metadata) return {}; |
| 794 | + |
| 795 | + var meta = $.data(element.form, 'validator').settings.meta; |
| 796 | + return meta ? |
| 797 | + $(element).metadata()[meta] : |
| 798 | + $(element).metadata(); |
| 799 | + }, |
| 800 | + |
| 801 | + staticRules: function(element) { |
| 802 | + var rules = {}; |
| 803 | + var validator = $.data(element.form, 'validator'); |
| 804 | + if (validator.settings.rules) { |
| 805 | + rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {}; |
| 806 | + } |
| 807 | + return rules; |
| 808 | + }, |
| 809 | + |
| 810 | + normalizeRules: function(rules, element) { |
| 811 | + // handle dependency check |
| 812 | + $.each(rules, function(prop, val) { |
| 813 | + // ignore rule when param is explicitly false, eg. required:false |
| 814 | + if (val === false) { |
| 815 | + delete rules[prop]; |
| 816 | + return; |
| 817 | + } |
| 818 | + if (val.param || val.depends) { |
| 819 | + var keepRule = true; |
| 820 | + switch (typeof val.depends) { |
| 821 | + case "string": |
| 822 | + keepRule = !!$(val.depends, element.form).length; |
| 823 | + break; |
| 824 | + case "function": |
| 825 | + keepRule = val.depends.call(element, element); |
| 826 | + break; |
| 827 | + } |
| 828 | + if (keepRule) { |
| 829 | + rules[prop] = val.param !== undefined ? val.param : true; |
| 830 | + } else { |
| 831 | + delete rules[prop]; |
| 832 | + } |
| 833 | + } |
| 834 | + }); |
| 835 | + |
| 836 | + // evaluate parameters |
| 837 | + $.each(rules, function(rule, parameter) { |
| 838 | + rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter; |
| 839 | + }); |
| 840 | + |
| 841 | + // clean number parameters |
| 842 | + $.each(['minlength', 'maxlength', 'min', 'max'], function() { |
| 843 | + if (rules[this]) { |
| 844 | + rules[this] = Number(rules[this]); |
| 845 | + } |
| 846 | + }); |
| 847 | + $.each(['rangelength', 'range'], function() { |
| 848 | + if (rules[this]) { |
| 849 | + rules[this] = [Number(rules[this][0]), Number(rules[this][1])]; |
| 850 | + } |
| 851 | + }); |
| 852 | + |
| 853 | + if ($.validator.autoCreateRanges) { |
| 854 | + // auto-create ranges |
| 855 | + if (rules.min && rules.max) { |
| 856 | + rules.range = [rules.min, rules.max]; |
| 857 | + delete rules.min; |
| 858 | + delete rules.max; |
| 859 | + } |
| 860 | + if (rules.minlength && rules.maxlength) { |
| 861 | + rules.rangelength = [rules.minlength, rules.maxlength]; |
| 862 | + delete rules.minlength; |
| 863 | + delete rules.maxlength; |
| 864 | + } |
| 865 | + } |
| 866 | + |
| 867 | + // To support custom messages in metadata ignore rule methods titled "messages" |
| 868 | + if (rules.messages) { |
| 869 | + delete rules.messages; |
| 870 | + } |
| 871 | + |
| 872 | + return rules; |
| 873 | + }, |
| 874 | + |
| 875 | + // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} |
| 876 | + normalizeRule: function(data) { |
| 877 | + if( typeof data == "string" ) { |
| 878 | + var transformed = {}; |
| 879 | + $.each(data.split(/\s/), function() { |
| 880 | + transformed[this] = true; |
| 881 | + }); |
| 882 | + data = transformed; |
| 883 | + } |
| 884 | + return data; |
| 885 | + }, |
| 886 | + |
| 887 | + // http://docs.jquery.com/Plugins/Validation/Validator/addMethod |
| 888 | + addMethod: function(name, method, message) { |
| 889 | + $.validator.methods[name] = method; |
| 890 | + $.validator.messages[name] = message != undefined ? message : $.validator.messages[name]; |
| 891 | + if (method.length < 3) { |
| 892 | + $.validator.addClassRules(name, $.validator.normalizeRule(name)); |
| 893 | + } |
| 894 | + }, |
| 895 | + |
| 896 | + methods: { |
| 897 | + |
| 898 | + // http://docs.jquery.com/Plugins/Validation/Methods/required |
| 899 | + required: function(value, element, param) { |
| 900 | + // check if dependency is met |
| 901 | + if ( !this.depend(param, element) ) |
| 902 | + return "dependency-mismatch"; |
| 903 | + switch( element.nodeName.toLowerCase() ) { |
| 904 | + case 'select': |
| 905 | + // could be an array for select-multiple or a string, both are fine this way |
| 906 | + var val = $(element).val(); |
| 907 | + return val && val.length > 0; |
| 908 | + case 'input': |
| 909 | + if ( this.checkable(element) ) |
| 910 | + return this.getLength(value, element) > 0; |
| 911 | + default: |
| 912 | + return $.trim(value).length > 0; |
| 913 | + } |
| 914 | + }, |
| 915 | + |
| 916 | + // http://docs.jquery.com/Plugins/Validation/Methods/remote |
| 917 | + remote: function(value, element, param) { |
| 918 | + if ( this.optional(element) ) |
| 919 | + return "dependency-mismatch"; |
| 920 | + |
| 921 | + var previous = this.previousValue(element); |
| 922 | + if (!this.settings.messages[element.name] ) |
| 923 | + this.settings.messages[element.name] = {}; |
| 924 | + previous.originalMessage = this.settings.messages[element.name].remote; |
| 925 | + this.settings.messages[element.name].remote = previous.message; |
| 926 | + |
| 927 | + param = typeof param == "string" && {url:param} || param; |
| 928 | + |
| 929 | + if ( previous.old !== value ) { |
| 930 | + previous.old = value; |
| 931 | + var validator = this; |
| 932 | + this.startRequest(element); |
| 933 | + var data = {}; |
| 934 | + data[element.name] = value; |
| 935 | + $.ajax($.extend(true, { |
| 936 | + url: param, |
| 937 | + mode: "abort", |
| 938 | + port: "validate" + element.name, |
| 939 | + dataType: "json", |
| 940 | + data: data, |
| 941 | + success: function(response) { |
| 942 | + validator.settings.messages[element.name].remote = previous.originalMessage; |
| 943 | + var valid = response === true; |
| 944 | + if ( valid ) { |
| 945 | + var submitted = validator.formSubmitted; |
| 946 | + validator.prepareElement(element); |
| 947 | + validator.formSubmitted = submitted; |
| 948 | + validator.successList.push(element); |
| 949 | + validator.showErrors(); |
| 950 | + } else { |
| 951 | + var errors = {}; |
| 952 | + var message = (previous.message = response || validator.defaultMessage( element, "remote" )); |
| 953 | + errors[element.name] = $.isFunction(message) ? message(value) : message; |
| 954 | + validator.showErrors(errors); |
| 955 | + } |
| 956 | + previous.valid = valid; |
| 957 | + validator.stopRequest(element, valid); |
| 958 | + } |
| 959 | + }, param)); |
| 960 | + return "pending"; |
| 961 | + } else if( this.pending[element.name] ) { |
| 962 | + return "pending"; |
| 963 | + } |
| 964 | + return previous.valid; |
| 965 | + }, |
| 966 | + |
| 967 | + // http://docs.jquery.com/Plugins/Validation/Methods/minlength |
| 968 | + minlength: function(value, element, param) { |
| 969 | + return this.optional(element) || this.getLength($.trim(value), element) >= param; |
| 970 | + }, |
| 971 | + |
| 972 | + // http://docs.jquery.com/Plugins/Validation/Methods/maxlength |
| 973 | + maxlength: function(value, element, param) { |
| 974 | + return this.optional(element) || this.getLength($.trim(value), element) <= param; |
| 975 | + }, |
| 976 | + |
| 977 | + // http://docs.jquery.com/Plugins/Validation/Methods/rangelength |
| 978 | + rangelength: function(value, element, param) { |
| 979 | + var length = this.getLength($.trim(value), element); |
| 980 | + return this.optional(element) || ( length >= param[0] && length <= param[1] ); |
| 981 | + }, |
| 982 | + |
| 983 | + // http://docs.jquery.com/Plugins/Validation/Methods/min |
| 984 | + min: function( value, element, param ) { |
| 985 | + return this.optional(element) || value >= param; |
| 986 | + }, |
| 987 | + |
| 988 | + // http://docs.jquery.com/Plugins/Validation/Methods/max |
| 989 | + max: function( value, element, param ) { |
| 990 | + return this.optional(element) || value <= param; |
| 991 | + }, |
| 992 | + |
| 993 | + // http://docs.jquery.com/Plugins/Validation/Methods/range |
| 994 | + range: function( value, element, param ) { |
| 995 | + return this.optional(element) || ( value >= param[0] && value <= param[1] ); |
| 996 | + }, |
| 997 | + |
| 998 | + // http://docs.jquery.com/Plugins/Validation/Methods/email |
| 999 | + email: function(value, element) { |
| 1000 | + // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/ |
| 1001 | + return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value); |
| 1002 | + }, |
| 1003 | + |
| 1004 | + // http://docs.jquery.com/Plugins/Validation/Methods/url |
| 1005 | + url: function(value, element) { |
| 1006 | + // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/ |
| 1007 | + return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); |
| 1008 | + }, |
| 1009 | + |
| 1010 | + // http://docs.jquery.com/Plugins/Validation/Methods/date |
| 1011 | + date: function(value, element) { |
| 1012 | + return this.optional(element) || !/Invalid|NaN/.test(new Date(value)); |
| 1013 | + }, |
| 1014 | + |
| 1015 | + // http://docs.jquery.com/Plugins/Validation/Methods/dateISO |
| 1016 | + dateISO: function(value, element) { |
| 1017 | + return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value); |
| 1018 | + }, |
| 1019 | + |
| 1020 | + // http://docs.jquery.com/Plugins/Validation/Methods/number |
| 1021 | + number: function(value, element) { |
| 1022 | + return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value); |
| 1023 | + }, |
| 1024 | + |
| 1025 | + // http://docs.jquery.com/Plugins/Validation/Methods/digits |
| 1026 | + digits: function(value, element) { |
| 1027 | + return this.optional(element) || /^\d+$/.test(value); |
| 1028 | + }, |
| 1029 | + |
| 1030 | + // http://docs.jquery.com/Plugins/Validation/Methods/creditcard |
| 1031 | + // based on http://en.wikipedia.org/wiki/Luhn |
| 1032 | + creditcard: function(value, element) { |
| 1033 | + if ( this.optional(element) ) |
| 1034 | + return "dependency-mismatch"; |
| 1035 | + // accept only digits and dashes |
| 1036 | + if (/[^0-9-]+/.test(value)) |
| 1037 | + return false; |
| 1038 | + var nCheck = 0, |
| 1039 | + nDigit = 0, |
| 1040 | + bEven = false; |
| 1041 | + |
| 1042 | + value = value.replace(/\D/g, ""); |
| 1043 | + |
| 1044 | + for (var n = value.length - 1; n >= 0; n--) { |
| 1045 | + var cDigit = value.charAt(n); |
| 1046 | + var nDigit = parseInt(cDigit, 10); |
| 1047 | + if (bEven) { |
| 1048 | + if ((nDigit *= 2) > 9) |
| 1049 | + nDigit -= 9; |
| 1050 | + } |
| 1051 | + nCheck += nDigit; |
| 1052 | + bEven = !bEven; |
| 1053 | + } |
| 1054 | + |
| 1055 | + return (nCheck % 10) == 0; |
| 1056 | + }, |
| 1057 | + |
| 1058 | + // http://docs.jquery.com/Plugins/Validation/Methods/accept |
| 1059 | + accept: function(value, element, param) { |
| 1060 | + param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif"; |
| 1061 | + return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i")); |
| 1062 | + }, |
| 1063 | + |
| 1064 | + // http://docs.jquery.com/Plugins/Validation/Methods/equalTo |
| 1065 | + equalTo: function(value, element, param) { |
| 1066 | + // bind to the blur event of the target in order to revalidate whenever the target field is updated |
| 1067 | + // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead |
| 1068 | + var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() { |
| 1069 | + $(element).valid(); |
| 1070 | + }); |
| 1071 | + return value == target.val(); |
| 1072 | + } |
| 1073 | + |
| 1074 | + } |
| 1075 | + |
| 1076 | +}); |
| 1077 | + |
| 1078 | +// deprecated, use $.validator.format instead |
| 1079 | +$.format = $.validator.format; |
| 1080 | + |
| 1081 | +})(jQuery); |
| 1082 | + |
| 1083 | +// ajax mode: abort |
| 1084 | +// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); |
| 1085 | +// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() |
| 1086 | +;(function($) { |
| 1087 | + var ajax = $.ajax; |
| 1088 | + var pendingRequests = {}; |
| 1089 | + $.ajax = function(settings) { |
| 1090 | + // create settings for compatibility with ajaxSetup |
| 1091 | + settings = $.extend(settings, $.extend({}, $.ajaxSettings, settings)); |
| 1092 | + var port = settings.port; |
| 1093 | + if (settings.mode == "abort") { |
| 1094 | + if ( pendingRequests[port] ) { |
| 1095 | + pendingRequests[port].abort(); |
| 1096 | + } |
| 1097 | + return (pendingRequests[port] = ajax.apply(this, arguments)); |
| 1098 | + } |
| 1099 | + return ajax.apply(this, arguments); |
| 1100 | + }; |
| 1101 | +})(jQuery); |
| 1102 | + |
| 1103 | +// provides cross-browser focusin and focusout events |
| 1104 | +// IE has native support, in other browsers, use event caputuring (neither bubbles) |
| 1105 | + |
| 1106 | +// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation |
| 1107 | +// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target |
| 1108 | +;(function($) { |
| 1109 | + // only implement if not provided by jQuery core (since 1.4) |
| 1110 | + // TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs |
| 1111 | + if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) { |
| 1112 | + $.each({ |
| 1113 | + focus: 'focusin', |
| 1114 | + blur: 'focusout' |
| 1115 | + }, function( original, fix ){ |
| 1116 | + $.event.special[fix] = { |
| 1117 | + setup:function() { |
| 1118 | + this.addEventListener( original, handler, true ); |
| 1119 | + }, |
| 1120 | + teardown:function() { |
| 1121 | + this.removeEventListener( original, handler, true ); |
| 1122 | + }, |
| 1123 | + handler: function(e) { |
| 1124 | + arguments[0] = $.event.fix(e); |
| 1125 | + arguments[0].type = fix; |
| 1126 | + return $.event.handle.apply(this, arguments); |
| 1127 | + } |
| 1128 | + }; |
| 1129 | + function handler(e) { |
| 1130 | + e = $.event.fix(e); |
| 1131 | + e.type = fix; |
| 1132 | + return $.event.handle.call(this, e); |
| 1133 | + } |
| 1134 | + }); |
| 1135 | + }; |
| 1136 | + $.extend($.fn, { |
| 1137 | + validateDelegate: function(delegate, type, handler) { |
| 1138 | + return this.bind(type, function(event) { |
| 1139 | + var target = $(event.target); |
| 1140 | + if (target.is(delegate)) { |
| 1141 | + return handler.apply(target, arguments); |
| 1142 | + } |
| 1143 | + }); |
| 1144 | + } |
| 1145 | + }); |
| 1146 | +})(jQuery); |
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.validate.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 1147 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.autoSuggest.js |
— | — | @@ -0,0 +1,368 @@ |
| 2 | + /* |
| 3 | + * AutoSuggest |
| 4 | + * Copyright 2009-2010 Drew Wilson |
| 5 | + * www.drewwilson.com |
| 6 | + * code.drewwilson.com/entry/autosuggest-jquery-plugin |
| 7 | + * |
| 8 | + * Version 1.4 - Updated: Mar. 23, 2010 |
| 9 | + * |
| 10 | + * This Plug-In will auto-complete or auto-suggest completed search queries |
| 11 | + * for you as you type. You can add multiple selections and remove them on |
| 12 | + * the fly. It supports keybord navigation (UP + DOWN + RETURN), as well |
| 13 | + * as multiple AutoSuggest fields on the same page. |
| 14 | + * |
| 15 | + * Inspied by the Autocomplete plugin by: J�rn Zaefferer |
| 16 | + * and the Facelist plugin by: Ian Tearle (iantearle.com) |
| 17 | + * |
| 18 | + * This AutoSuggest jQuery plug-in is dual licensed under the MIT and GPL licenses: |
| 19 | + * http://www.opensource.org/licenses/mit-license.php |
| 20 | + * http://www.gnu.org/licenses/gpl.html |
| 21 | + */ |
| 22 | + |
| 23 | +(function($){ |
| 24 | + $.fn.autoSuggest = function(data, options) { |
| 25 | + var defaults = { |
| 26 | + asHtmlID: false, |
| 27 | + startText: "Enter Name Here", |
| 28 | + emptyText: "No Results Found", |
| 29 | + preFill: {}, |
| 30 | + limitText: "No More Selections Are Allowed", |
| 31 | + selectedItemProp: "value", //name of object property |
| 32 | + selectedValuesProp: "value", //name of object property |
| 33 | + searchObjProps: "value", //comma separated list of object property names |
| 34 | + queryParam: "q", |
| 35 | + retrieveLimit: false, //number for 'limit' param on ajax request |
| 36 | + extraParams: "", |
| 37 | + matchCase: false, |
| 38 | + minChars: 1, |
| 39 | + keyDelay: 400, |
| 40 | + resultsHighlight: true, |
| 41 | + neverSubmit: false, |
| 42 | + selectionLimit: false, |
| 43 | + showResultList: true, |
| 44 | + start: function(){}, |
| 45 | + selectionClick: function(elem){}, |
| 46 | + selectionAdded: function(elem){}, |
| 47 | + selectionRemoved: function(elem){ elem.remove(); }, |
| 48 | + formatList: false, //callback function |
| 49 | + beforeRetrieve: function(string){ return string; }, |
| 50 | + retrieveComplete: function(data){ return data; }, |
| 51 | + resultClick: function(data){}, |
| 52 | + resultsComplete: function(){} |
| 53 | + }; |
| 54 | + var opts = $.extend(defaults, options); |
| 55 | + |
| 56 | + var d_type = "object"; |
| 57 | + var d_count = 0; |
| 58 | + if(typeof data == "string") { |
| 59 | + d_type = "string"; |
| 60 | + var req_string = data; |
| 61 | + } else { |
| 62 | + var org_data = data; |
| 63 | + for (k in data) if (data.hasOwnProperty(k)) d_count++; |
| 64 | + } |
| 65 | + if((d_type == "object" && d_count > 0) || d_type == "string"){ |
| 66 | + return this.each(function(x){ |
| 67 | + if(!opts.asHtmlID){ |
| 68 | + x = x+""+Math.floor(Math.random()*100); //this ensures there will be unique IDs on the page if autoSuggest() is called multiple times |
| 69 | + var x_id = "as-input-"+x; |
| 70 | + } else { |
| 71 | + x = opts.asHtmlID; |
| 72 | + var x_id = x; |
| 73 | + } |
| 74 | + opts.start.call(this); |
| 75 | + var input = $(this); |
| 76 | + input.attr("autocomplete","off").addClass("as-input").attr("id",x_id).val(opts.startText); |
| 77 | + var input_focus = false; |
| 78 | + |
| 79 | + // Setup basic elements and render them to the DOM |
| 80 | + input.wrap('<ul class="as-selections" id="as-selections-'+x+'"></ul>').wrap('<li class="as-original" id="as-original-'+x+'"></li>'); |
| 81 | + var selections_holder = $("#as-selections-"+x); |
| 82 | + var org_li = $("#as-original-"+x); |
| 83 | + var results_holder = $('<div class="as-results" id="as-results-'+x+'"></div>').hide(); |
| 84 | + var results_ul = $('<ul class="as-list"></ul>'); |
| 85 | + var values_input = $('<input type="hidden" class="as-values" name="as_values_'+x+'" id="as-values-'+x+'" />'); |
| 86 | + var prefill_value = ""; |
| 87 | + if(typeof opts.preFill == "string"){ |
| 88 | + var vals = opts.preFill.split(","); |
| 89 | + for(var i=0; i < vals.length; i++){ |
| 90 | + var v_data = {}; |
| 91 | + v_data[opts.selectedValuesProp] = vals[i]; |
| 92 | + if(vals[i] != ""){ |
| 93 | + add_selected_item(v_data, "000"+i); |
| 94 | + } |
| 95 | + } |
| 96 | + prefill_value = opts.preFill; |
| 97 | + } else { |
| 98 | + prefill_value = ""; |
| 99 | + var prefill_count = 0; |
| 100 | + for (k in opts.preFill) if (opts.preFill.hasOwnProperty(k)) prefill_count++; |
| 101 | + if(prefill_count > 0){ |
| 102 | + for(var i=0; i < prefill_count; i++){ |
| 103 | + var new_v = opts.preFill[i][opts.selectedValuesProp]; |
| 104 | + if(new_v == undefined){ new_v = ""; } |
| 105 | + prefill_value = prefill_value+new_v+","; |
| 106 | + if(new_v != ""){ |
| 107 | + add_selected_item(opts.preFill[i], "000"+i); |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + if(prefill_value != ""){ |
| 113 | + input.val(""); |
| 114 | + var lastChar = prefill_value.substring(prefill_value.length-1); |
| 115 | + if(lastChar != ","){ prefill_value = prefill_value+","; } |
| 116 | + values_input.val(","+prefill_value); |
| 117 | + $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected"); |
| 118 | + } |
| 119 | + input.after(values_input); |
| 120 | + selections_holder.click(function(){ |
| 121 | + input_focus = true; |
| 122 | + input.focus(); |
| 123 | + }).mousedown(function(){ input_focus = false; }).after(results_holder); |
| 124 | + |
| 125 | + var timeout = null; |
| 126 | + var prev = ""; |
| 127 | + var totalSelections = 0; |
| 128 | + var tab_press = false; |
| 129 | + |
| 130 | + // Handle input field events |
| 131 | + input.focus(function(){ |
| 132 | + if($(this).val() == opts.startText && values_input.val() == ""){ |
| 133 | + $(this).val(""); |
| 134 | + } else if(input_focus){ |
| 135 | + $("li.as-selection-item", selections_holder).removeClass("blur"); |
| 136 | + if($(this).val() != ""){ |
| 137 | + results_ul.css("width",selections_holder.outerWidth()); |
| 138 | + results_holder.show(); |
| 139 | + } |
| 140 | + } |
| 141 | + input_focus = true; |
| 142 | + return true; |
| 143 | + }).blur(function(){ |
| 144 | + if($(this).val() == "" && values_input.val() == "" && prefill_value == ""){ |
| 145 | + $(this).val(opts.startText); |
| 146 | + } else if(input_focus){ |
| 147 | + $("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected"); |
| 148 | + results_holder.hide(); |
| 149 | + } |
| 150 | + }).keydown(function(e) { |
| 151 | + // track last key pressed |
| 152 | + lastKeyPressCode = e.keyCode; |
| 153 | + first_focus = false; |
| 154 | + switch(e.keyCode) { |
| 155 | + case 38: // up |
| 156 | + e.preventDefault(); |
| 157 | + moveSelection("up"); |
| 158 | + break; |
| 159 | + case 40: // down |
| 160 | + e.preventDefault(); |
| 161 | + moveSelection("down"); |
| 162 | + break; |
| 163 | + case 8: // delete |
| 164 | + if(input.val() == ""){ |
| 165 | + var last = values_input.val().split(","); |
| 166 | + last = last[last.length - 2]; |
| 167 | + selections_holder.children().not(org_li.prev()).removeClass("selected"); |
| 168 | + if(org_li.prev().hasClass("selected")){ |
| 169 | + values_input.val(values_input.val().replace(","+last+",",",")); |
| 170 | + opts.selectionRemoved.call(this, org_li.prev()); |
| 171 | + } else { |
| 172 | + opts.selectionClick.call(this, org_li.prev()); |
| 173 | + org_li.prev().addClass("selected"); |
| 174 | + } |
| 175 | + } |
| 176 | + if(input.val().length == 1){ |
| 177 | + results_holder.hide(); |
| 178 | + prev = ""; |
| 179 | + } |
| 180 | + if($(":visible",results_holder).length > 0){ |
| 181 | + if (timeout){ clearTimeout(timeout); } |
| 182 | + timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay); |
| 183 | + } |
| 184 | + break; |
| 185 | + case 9: case 188: // tab or comma |
| 186 | + tab_press = true; |
| 187 | + var i_input = input.val().replace(/(,)/g, ""); |
| 188 | + if(i_input != "" && values_input.val().search(","+i_input+",") < 0 && i_input.length >= opts.minChars){ |
| 189 | + e.preventDefault(); |
| 190 | + var n_data = {}; |
| 191 | + n_data[opts.selectedItemProp] = i_input; |
| 192 | + n_data[opts.selectedValuesProp] = i_input; |
| 193 | + var lis = $("li", selections_holder).length; |
| 194 | + add_selected_item(n_data, "00"+(lis+1)); |
| 195 | + input.val(""); |
| 196 | + } |
| 197 | + case 13: // return |
| 198 | + tab_press = false; |
| 199 | + var active = $("li.active:first", results_holder); |
| 200 | + if(active.length > 0){ |
| 201 | + active.click(); |
| 202 | + results_holder.hide(); |
| 203 | + } |
| 204 | + if(opts.neverSubmit || active.length > 0){ |
| 205 | + e.preventDefault(); |
| 206 | + } |
| 207 | + break; |
| 208 | + default: |
| 209 | + if(opts.showResultList){ |
| 210 | + if(opts.selectionLimit && $("li.as-selection-item", selections_holder).length >= opts.selectionLimit){ |
| 211 | + results_ul.html('<li class="as-message">'+opts.limitText+'</li>'); |
| 212 | + results_holder.show(); |
| 213 | + } else { |
| 214 | + if (timeout){ clearTimeout(timeout); } |
| 215 | + timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay); |
| 216 | + } |
| 217 | + } |
| 218 | + break; |
| 219 | + } |
| 220 | + }); |
| 221 | + |
| 222 | + function keyChange() { |
| 223 | + // ignore if the following keys are pressed: [del] [shift] [capslock] |
| 224 | + if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ){ return results_holder.hide(); } |
| 225 | + var string = input.val().replace(/[\\]+|[\/]+/g,""); |
| 226 | + if (string == prev) return; |
| 227 | + prev = string; |
| 228 | + if (string.length >= opts.minChars) { |
| 229 | + selections_holder.addClass("loading"); |
| 230 | + if(d_type == "string"){ |
| 231 | + var limit = ""; |
| 232 | + if(opts.retrieveLimit){ |
| 233 | + limit = "&limit="+encodeURIComponent(opts.retrieveLimit); |
| 234 | + } |
| 235 | + if(opts.beforeRetrieve){ |
| 236 | + string = opts.beforeRetrieve.call(this, string); |
| 237 | + } |
| 238 | + $.getJSON(req_string+"?"+opts.queryParam+"="+encodeURIComponent(string)+limit+opts.extraParams, function(data){ |
| 239 | + d_count = 0; |
| 240 | + var new_data = opts.retrieveComplete.call(this, data); |
| 241 | + for (k in new_data) if (new_data.hasOwnProperty(k)) d_count++; |
| 242 | + processData(new_data, string); |
| 243 | + }); |
| 244 | + } else { |
| 245 | + if(opts.beforeRetrieve){ |
| 246 | + string = opts.beforeRetrieve.call(this, string); |
| 247 | + } |
| 248 | + processData(org_data, string); |
| 249 | + } |
| 250 | + } else { |
| 251 | + selections_holder.removeClass("loading"); |
| 252 | + results_holder.hide(); |
| 253 | + } |
| 254 | + } |
| 255 | + var num_count = 0; |
| 256 | + function processData(data, query){ |
| 257 | + if (!opts.matchCase){ query = query.toLowerCase(); } |
| 258 | + var matchCount = 0; |
| 259 | + results_holder.html(results_ul.html("")).hide(); |
| 260 | + for(var i=0;i<d_count;i++){ |
| 261 | + var num = i; |
| 262 | + num_count++; |
| 263 | + var forward = false; |
| 264 | + if(opts.searchObjProps == "value") { |
| 265 | + var str = data[num].value; |
| 266 | + } else { |
| 267 | + var str = ""; |
| 268 | + var names = opts.searchObjProps.split(","); |
| 269 | + for(var y=0;y<names.length;y++){ |
| 270 | + var name = $.trim(names[y]); |
| 271 | + str = str+data[num][name]+" "; |
| 272 | + } |
| 273 | + } |
| 274 | + if(str){ |
| 275 | + if (!opts.matchCase){ str = str.toLowerCase(); } |
| 276 | + if(str.search(query) != -1 && values_input.val().search(","+data[num][opts.selectedValuesProp]+",") == -1){ |
| 277 | + forward = true; |
| 278 | + } |
| 279 | + } |
| 280 | + if(forward){ |
| 281 | + var formatted = $('<li class="as-result-item" id="as-result-item-'+num+'"></li>').click(function(){ |
| 282 | + var raw_data = $(this).data("data"); |
| 283 | + var number = raw_data.num; |
| 284 | + if($("#as-selection-"+number, selections_holder).length <= 0 && !tab_press){ |
| 285 | + var data = raw_data.attributes; |
| 286 | + input.val("").focus(); |
| 287 | + prev = ""; |
| 288 | + add_selected_item(data, number); |
| 289 | + opts.resultClick.call(this, raw_data); |
| 290 | + results_holder.hide(); |
| 291 | + } |
| 292 | + tab_press = false; |
| 293 | + }).mousedown(function(){ input_focus = false; }).mouseover(function(){ |
| 294 | + $("li", results_ul).removeClass("active"); |
| 295 | + $(this).addClass("active"); |
| 296 | + }).data("data",{attributes: data[num], num: num_count}); |
| 297 | + var this_data = $.extend({},data[num]); |
| 298 | + if (!opts.matchCase){ |
| 299 | + var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "gi"); |
| 300 | + } else { |
| 301 | + var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "g"); |
| 302 | + } |
| 303 | + |
| 304 | + if(opts.resultsHighlight){ |
| 305 | + this_data[opts.selectedItemProp] = this_data[opts.selectedItemProp].replace(regx,"<em>$1</em>"); |
| 306 | + } |
| 307 | + if(!opts.formatList){ |
| 308 | + formatted = formatted.html(this_data[opts.selectedItemProp]); |
| 309 | + } else { |
| 310 | + formatted = opts.formatList.call(this, this_data, formatted); |
| 311 | + } |
| 312 | + results_ul.append(formatted); |
| 313 | + delete this_data; |
| 314 | + matchCount++; |
| 315 | + if(opts.retrieveLimit && opts.retrieveLimit == matchCount ){ break; } |
| 316 | + } |
| 317 | + } |
| 318 | + selections_holder.removeClass("loading"); |
| 319 | + if(matchCount <= 0){ |
| 320 | + results_ul.html('<li class="as-message">'+opts.emptyText+'</li>'); |
| 321 | + } |
| 322 | + results_ul.css("width", selections_holder.outerWidth()); |
| 323 | + results_holder.show(); |
| 324 | + opts.resultsComplete.call(this); |
| 325 | + } |
| 326 | + |
| 327 | + function add_selected_item(data, num){ |
| 328 | + values_input.val(values_input.val()+data[opts.selectedValuesProp]+","); |
| 329 | + var item = $('<li class="as-selection-item" id="as-selection-'+num+'"></li>').click(function(){ |
| 330 | + opts.selectionClick.call(this, $(this)); |
| 331 | + selections_holder.children().removeClass("selected"); |
| 332 | + $(this).addClass("selected"); |
| 333 | + }).mousedown(function(){ input_focus = false; }); |
| 334 | + var close = $('<a class="as-close">×</a>').click(function(){ |
| 335 | + values_input.val(values_input.val().replace(","+data[opts.selectedValuesProp]+",",",")); |
| 336 | + opts.selectionRemoved.call(this, item); |
| 337 | + input_focus = true; |
| 338 | + input.focus(); |
| 339 | + return false; |
| 340 | + }); |
| 341 | + org_li.before(item.html(data[opts.selectedItemProp]).prepend(close)); |
| 342 | + opts.selectionAdded.call(this, org_li.prev()); |
| 343 | + } |
| 344 | + |
| 345 | + function moveSelection(direction){ |
| 346 | + if($(":visible",results_holder).length > 0){ |
| 347 | + var lis = $("li", results_holder); |
| 348 | + if(direction == "down"){ |
| 349 | + var start = lis.eq(0); |
| 350 | + } else { |
| 351 | + var start = lis.filter(":last"); |
| 352 | + } |
| 353 | + var active = $("li.active:first", results_holder); |
| 354 | + if(active.length > 0){ |
| 355 | + if(direction == "down"){ |
| 356 | + start = active.next(); |
| 357 | + } else { |
| 358 | + start = active.prev(); |
| 359 | + } |
| 360 | + } |
| 361 | + lis.removeClass("active"); |
| 362 | + start.addClass("active"); |
| 363 | + } |
| 364 | + } |
| 365 | + |
| 366 | + }); |
| 367 | + } |
| 368 | + } |
| 369 | +})(jQuery); |
\ No newline at end of file |
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.autoSuggest.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 370 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.mwCoolCats.js |
— | — | @@ -0,0 +1,130 @@ |
| 2 | +( function ( $j ) { $j.fn.mwCoolCats = function( options ) { |
| 3 | + |
| 4 | + var defaults = { |
| 5 | + buttontext: 'Add' |
| 6 | + }; |
| 7 | + |
| 8 | + var settings = $j.extend( {}, defaults, options); |
| 9 | + |
| 10 | + // usually Category:Foo |
| 11 | + var categoryNamespace = wgFormattedNamespaces[wgNamespaceIds['category']]; |
| 12 | + |
| 13 | + var $container; |
| 14 | + return this.each( function() { |
| 15 | + var _this = $j( this ); |
| 16 | + _this.addClass( 'categoryInput' ); |
| 17 | + |
| 18 | + _this.suggestions( { |
| 19 | + 'fetch': _fetchSuggestions, |
| 20 | + 'cancel': function() { |
| 21 | + var req = $j( this ).data( 'request' ); |
| 22 | + if ( req.abort ) { |
| 23 | + req.abort(); |
| 24 | + } |
| 25 | + } |
| 26 | + } ); |
| 27 | + _this.suggestions(); |
| 28 | + |
| 29 | + _this.wrap('<div class="cat-widget"></div>'); |
| 30 | + $container = _this.parent(); // set to the cat-widget class we just wrapped |
| 31 | + $container.append( $j( '<button type="button">'+settings.buttontext+'</button>' ) |
| 32 | + .click( function(e) { |
| 33 | + e.stopPropagation(); |
| 34 | + e.preventDefault(); |
| 35 | + _processInput(); |
| 36 | + return false; |
| 37 | + }) ); |
| 38 | + $container.prepend('<ul class="cat-list pkg"></ul>'); |
| 39 | + |
| 40 | + //XXX ensure this isn't blocking other stuff needed. |
| 41 | + _this.parents('form').submit( function() { |
| 42 | + _processInput(); |
| 43 | + }); |
| 44 | + |
| 45 | + _this.keyup(function(e) { |
| 46 | + if(e.keyCode == 13) { |
| 47 | + e.stopPropagation(); |
| 48 | + e.preventDefault(); |
| 49 | + _processInput(); |
| 50 | + } |
| 51 | + }); |
| 52 | + |
| 53 | + this.getWikiText = function() { |
| 54 | + return _getCats().map( function() { return '[[' + categoryNamespace + ':' + this + ']]'; } ) |
| 55 | + .toArray() |
| 56 | + .join( "\n" ); |
| 57 | + }; |
| 58 | + |
| 59 | + _processInput(); |
| 60 | + }); |
| 61 | + |
| 62 | + function _processInput() { |
| 63 | + var $input = $container.find( 'input' ); |
| 64 | + _insertCat( $input.val().trim() ); |
| 65 | + $input.val(""); |
| 66 | + } |
| 67 | + |
| 68 | + function _insertCat( cat ) { |
| 69 | + if ( mw.isEmpty( cat ) || _containsCat( cat ) ) { |
| 70 | + return; |
| 71 | + } |
| 72 | + var href = _catLink( cat ); |
| 73 | + var $li = $j( '<li class="cat"></li>' ); |
| 74 | + $container.find( 'ul' ).append( $li ); |
| 75 | + $li.append( '<a class="cat" target="_new" href="' + href + '">' + cat +' </a>' ); |
| 76 | + $li.append( $j.fn.removeCtrl( 'mwe-upwiz-category-remove', function() { $li.remove(); } ) ); |
| 77 | + } |
| 78 | + |
| 79 | + function _catLink( cat ) { |
| 80 | + var catLink = |
| 81 | + encodeURIComponent( categoryNamespace ) |
| 82 | + + ':' |
| 83 | + + encodeURIComponent( mw.ucfirst( cat.replace(/ /g, '_' ) ) ); |
| 84 | + |
| 85 | + // wgServer typically like 'http://commons.prototype.wikimedia.org' |
| 86 | + // wgArticlePath typically like '/wiki/$1' |
| 87 | + if ( ! ( mw.isEmpty( wgServer ) && mw.isEmpty( wgArticlePath ) ) ) { |
| 88 | + catLink = wgServer + wgArticlePath.replace( /\$1/, catLink ); |
| 89 | + } |
| 90 | + |
| 91 | + return catLink; |
| 92 | + } |
| 93 | + |
| 94 | + function _getCats() { |
| 95 | + return $container.find('ul li a.cat').map( function() { return $j.trim( this.text ); } ); |
| 96 | + } |
| 97 | + |
| 98 | + function _containsCat( cat ) { |
| 99 | + return _getCats().filter( function() { return this == cat; } ).length !== 0; |
| 100 | + } |
| 101 | + |
| 102 | + function _fetchSuggestions( query ) { |
| 103 | + var _this = this; |
| 104 | + var request = $j.ajax( { |
| 105 | + url: wgScriptPath + '/api.php', |
| 106 | + data: { |
| 107 | + 'action': 'query', |
| 108 | + 'list': 'allpages', |
| 109 | + 'apnamespace': wgNamespaceIds['category'], |
| 110 | + 'apprefix': $j( this ).val(), |
| 111 | + 'format': 'json' |
| 112 | + }, |
| 113 | + dataType: 'json', |
| 114 | + success: function( data ) { |
| 115 | + // Process data.query.allpages into an array of titles |
| 116 | + var pages = data.query.allpages; |
| 117 | + var titleArr = []; |
| 118 | + |
| 119 | + $j.each( pages, function( i, page ) { |
| 120 | + var title = page.title.split( ':', 2 )[1]; |
| 121 | + titleArr.push( title ); |
| 122 | + } ); |
| 123 | + |
| 124 | + $j( _this ).suggestions( 'suggestions', titleArr ); |
| 125 | + } |
| 126 | + } ); |
| 127 | + |
| 128 | + $j( _this ).data( 'request', request ); |
| 129 | + } |
| 130 | + |
| 131 | +}})(jQuery); |
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.mwCoolCats.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 132 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.morphCrossfade.js |
— | — | @@ -0,0 +1,110 @@ |
| 2 | +/** |
| 3 | + * jQuery Morphing Crossfade plugin |
| 4 | + * Copyright Neil Kandalgaonkar, 2010 |
| 5 | + * |
| 6 | + * This work is licensed under the terms of the GNU General Public License, |
| 7 | + * version 2 or later. |
| 8 | + * (see http://www.fsf.org/licensing/licenses/gpl.html). |
| 9 | + * Derivative works and later versions of the code must be free software |
| 10 | + * licensed under the same or a compatible license. |
| 11 | + * |
| 12 | + * There are a lot of cross-fading plugins out there, but most assume that all |
| 13 | + * elements are the same, fixed width. This will also grow or shrink the container |
| 14 | + * vertically while crossfading. This can be useful when (for instance) you have a |
| 15 | + * control panel and you want to switch from a simpler interface to a more advanced |
| 16 | + * version. Or, perhaps you like the way the Mac OS X Preferences panel works, where |
| 17 | + * you click on an icon and get a crossfade effect to another dialog, even if it's one |
| 18 | + * with different dimensions. |
| 19 | + * |
| 20 | + * How to use it: |
| 21 | + * Create some DOM structure where all the panels you want to crossfade are contained in |
| 22 | + * one parent, e.g. |
| 23 | + * |
| 24 | + * <div id="container"> |
| 25 | + * <div id="panel1"/> |
| 26 | + * <div id="panel2"/> |
| 27 | + * <div id="panel3"/> |
| 28 | + * </div> |
| 29 | + * |
| 30 | + * Initialize the crossfader: |
| 31 | + * |
| 32 | + * $( '#container' ).morphCrossfader(); |
| 33 | + * |
| 34 | + * By default, this will hide all elements except the first child (in this case #panel1). |
| 35 | + * |
| 36 | + * Then, whenever you want to crossfade, do something like this. The currently selected panel |
| 37 | + * will fade away, and your selection will fade in. |
| 38 | + * |
| 39 | + * $( '#container' ).morphCrossfade( '#panel2' ); |
| 40 | + * |
| 41 | + */ |
| 42 | + |
| 43 | +( function( $ ) { |
| 44 | + /** |
| 45 | + * Initialize crossfading of the children of an element |
| 46 | + */ |
| 47 | + $.fn.morphCrossfader = function() { |
| 48 | + // the elements that are immediate children are the crossfadables |
| 49 | + // they must all be "on top" of each other, so position them relative |
| 50 | + this.css( { |
| 51 | + position : 'relative', |
| 52 | + overflow : 'hidden', |
| 53 | + scroll: 'none' |
| 54 | + } ); |
| 55 | + this.children().css( { |
| 56 | + position: 'absolute', |
| 57 | + 'top': '0px', |
| 58 | + left : '0px', |
| 59 | + scroll: 'none', |
| 60 | + opacity: 0, |
| 61 | + visibility: 'hidden' |
| 62 | + } ); |
| 63 | + |
| 64 | + // should achieve the same result as crossfade( this.children().first() ) but without |
| 65 | + // animation etc. |
| 66 | + this.morphCrossfade( this.children().first(), 0 ); |
| 67 | + |
| 68 | + return this; |
| 69 | + }; |
| 70 | + |
| 71 | + /** |
| 72 | + * Initialize crossfading of the children of an element |
| 73 | + * @param selector of new thing to show; should be an immediate child of the crossfader element |
| 74 | + * @param speed (optional) how fast to crossfade, in milliseconds |
| 75 | + */ |
| 76 | + $.fn.morphCrossfade = function( newPanelSelector, speed ) { |
| 77 | + var container = this; |
| 78 | + if ( typeof speed === 'undefined' ) { |
| 79 | + speed = 400; |
| 80 | + } |
| 81 | + |
| 82 | + container.css( { 'overflow' : 'hidden' } ); |
| 83 | + |
| 84 | + $oldPanel = $( container.data( 'crossfadeDisplay' ) ); |
| 85 | + if ( $oldPanel ) { |
| 86 | + // remove auto setting of height from container, and |
| 87 | + // make doubly sure that the container height is equal to oldPanel |
| 88 | + container.css( { height: $oldPanel.outerHeight() } ); |
| 89 | + // take it out of the flow |
| 90 | + $oldPanel.css( { position: 'absolute' } ); |
| 91 | + // fade WITHOUT hiding when opacity = 0 |
| 92 | + $oldPanel.animate( { opacity: 0 }, speed, 'linear', function() { |
| 93 | + $oldPanel.css( { visibility: 'hidden'} ) |
| 94 | + } ); |
| 95 | + } |
| 96 | + container.data( 'crossfadeDisplay', newPanelSelector ); |
| 97 | + |
| 98 | + var $newPanel = $( newPanelSelector ); |
| 99 | + $newPanel.css( { visibility: 'visible' } ); |
| 100 | + container.animate( { height: $newPanel.outerHeight() }, speed, 'linear', function() { |
| 101 | + // we place it back into the flow, in case its size changes. |
| 102 | + $newPanel.css( { position: 'relative' } ); |
| 103 | + // and allow the container to grow with it. |
| 104 | + container.css( { height : 'auto' } ); |
| 105 | + } ); |
| 106 | + $newPanel.animate( { opacity: 1 }, speed ); |
| 107 | + |
| 108 | + return container; |
| 109 | + }; |
| 110 | + |
| 111 | +} )( jQuery ); |
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.morphCrossfade.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 112 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.validate.additional-methods.js |
— | — | @@ -0,0 +1,259 @@ |
| 2 | +(function() { |
| 3 | + |
| 4 | + function stripHtml(value) { |
| 5 | + // remove html tags and space chars |
| 6 | + return value.replace(/<.[^<>]*?>/g, ' ').replace(/ | /gi, ' ') |
| 7 | + // remove numbers and punctuation |
| 8 | + .replace(/[0-9.(),;:!?%#$'"_+=\/-]*/g,''); |
| 9 | + } |
| 10 | + jQuery.validator.addMethod("maxWords", function(value, element, params) { |
| 11 | + return this.optional(element) || stripHtml(value).match(/\b\w+\b/g).length < params; |
| 12 | + }, jQuery.validator.format("Please enter {0} words or less.")); |
| 13 | + |
| 14 | + jQuery.validator.addMethod("minWords", function(value, element, params) { |
| 15 | + return this.optional(element) || stripHtml(value).match(/\b\w+\b/g).length >= params; |
| 16 | + }, jQuery.validator.format("Please enter at least {0} words.")); |
| 17 | + |
| 18 | + jQuery.validator.addMethod("rangeWords", function(value, element, params) { |
| 19 | + return this.optional(element) || stripHtml(value).match(/\b\w+\b/g).length >= params[0] && value.match(/bw+b/g).length < params[1]; |
| 20 | + }, jQuery.validator.format("Please enter between {0} and {1} words.")); |
| 21 | + |
| 22 | +})(); |
| 23 | + |
| 24 | +jQuery.validator.addMethod("letterswithbasicpunc", function(value, element) { |
| 25 | + return this.optional(element) || /^[a-z-.,()'\"\s]+$/i.test(value); |
| 26 | +}, "Letters or punctuation only please"); |
| 27 | + |
| 28 | +jQuery.validator.addMethod("alphanumeric", function(value, element) { |
| 29 | + return this.optional(element) || /^\w+$/i.test(value); |
| 30 | +}, "Letters, numbers, spaces or underscores only please"); |
| 31 | + |
| 32 | +jQuery.validator.addMethod("lettersonly", function(value, element) { |
| 33 | + return this.optional(element) || /^[a-z]+$/i.test(value); |
| 34 | +}, "Letters only please"); |
| 35 | + |
| 36 | +jQuery.validator.addMethod("nowhitespace", function(value, element) { |
| 37 | + return this.optional(element) || /^\S+$/i.test(value); |
| 38 | +}, "No white space please"); |
| 39 | + |
| 40 | +jQuery.validator.addMethod("ziprange", function(value, element) { |
| 41 | + return this.optional(element) || /^90[2-5]\d\{2}-\d{4}$/.test(value); |
| 42 | +}, "Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx"); |
| 43 | + |
| 44 | +jQuery.validator.addMethod("integer", function(value, element) { |
| 45 | + return this.optional(element) || /^-?\d+$/.test(value); |
| 46 | +}, "A positive or negative non-decimal number please"); |
| 47 | + |
| 48 | +/** |
| 49 | +* Return true, if the value is a valid vehicle identification number (VIN). |
| 50 | +* |
| 51 | +* Works with all kind of text inputs. |
| 52 | +* |
| 53 | +* @example <input type="text" size="20" name="VehicleID" class="{required:true,vinUS:true}" /> |
| 54 | +* @desc Declares a required input element whose value must be a valid vehicle identification number. |
| 55 | +* |
| 56 | +* @name jQuery.validator.methods.vinUS |
| 57 | +* @type Boolean |
| 58 | +* @cat Plugins/Validate/Methods |
| 59 | +*/ |
| 60 | +jQuery.validator.addMethod( |
| 61 | + "vinUS", |
| 62 | + function(v){ |
| 63 | + if (v.length != 17) |
| 64 | + return false; |
| 65 | + var i, n, d, f, cd, cdv; |
| 66 | + var LL = ["A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"]; |
| 67 | + var VL = [1,2,3,4,5,6,7,8,1,2,3,4,5,7,9,2,3,4,5,6,7,8,9]; |
| 68 | + var FL = [8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2]; |
| 69 | + var rs = 0; |
| 70 | + for(i = 0; i < 17; i++){ |
| 71 | + f = FL[i]; |
| 72 | + d = v.slice(i,i+1); |
| 73 | + if(i == 8){ |
| 74 | + cdv = d; |
| 75 | + } |
| 76 | + if(!isNaN(d)){ |
| 77 | + d *= f; |
| 78 | + } |
| 79 | + else{ |
| 80 | + for(n = 0; n < LL.length; n++){ |
| 81 | + if(d.toUpperCase() === LL[n]){ |
| 82 | + d = VL[n]; |
| 83 | + d *= f; |
| 84 | + if(isNaN(cdv) && n == 8){ |
| 85 | + cdv = LL[n]; |
| 86 | + } |
| 87 | + break; |
| 88 | + } |
| 89 | + } |
| 90 | + } |
| 91 | + rs += d; |
| 92 | + } |
| 93 | + cd = rs % 11; |
| 94 | + if(cd == 10){cd = "X";} |
| 95 | + if(cd == cdv){return true;} |
| 96 | + return false; |
| 97 | + }, |
| 98 | + "The specified vehicle identification number (VIN) is invalid." |
| 99 | +); |
| 100 | + |
| 101 | +/** |
| 102 | + * Return true, if the value is a valid date, also making this formal check dd/mm/yyyy. |
| 103 | + * |
| 104 | + * @example jQuery.validator.methods.date("01/01/1900") |
| 105 | + * @result true |
| 106 | + * |
| 107 | + * @example jQuery.validator.methods.date("01/13/1990") |
| 108 | + * @result false |
| 109 | + * |
| 110 | + * @example jQuery.validator.methods.date("01.01.1900") |
| 111 | + * @result false |
| 112 | + * |
| 113 | + * @example <input name="pippo" class="{dateITA:true}" /> |
| 114 | + * @desc Declares an optional input element whose value must be a valid date. |
| 115 | + * |
| 116 | + * @name jQuery.validator.methods.dateITA |
| 117 | + * @type Boolean |
| 118 | + * @cat Plugins/Validate/Methods |
| 119 | + */ |
| 120 | +jQuery.validator.addMethod( |
| 121 | + "dateITA", |
| 122 | + function(value, element) { |
| 123 | + var check = false; |
| 124 | + var re = /^\d{1,2}\/\d{1,2}\/\d{4}$/; |
| 125 | + if( re.test(value)){ |
| 126 | + var adata = value.split('/'); |
| 127 | + var gg = parseInt(adata[0],10); |
| 128 | + var mm = parseInt(adata[1],10); |
| 129 | + var aaaa = parseInt(adata[2],10); |
| 130 | + var xdata = new Date(aaaa,mm-1,gg); |
| 131 | + if ( ( xdata.getFullYear() == aaaa ) && ( xdata.getMonth () == mm - 1 ) && ( xdata.getDate() == gg ) ) |
| 132 | + check = true; |
| 133 | + else |
| 134 | + check = false; |
| 135 | + } else |
| 136 | + check = false; |
| 137 | + return this.optional(element) || check; |
| 138 | + }, |
| 139 | + "Please enter a correct date" |
| 140 | +); |
| 141 | + |
| 142 | +jQuery.validator.addMethod("dateNL", function(value, element) { |
| 143 | + return this.optional(element) || /^\d\d?[\.\/-]\d\d?[\.\/-]\d\d\d?\d?$/.test(value); |
| 144 | + }, "Vul hier een geldige datum in." |
| 145 | +); |
| 146 | + |
| 147 | +jQuery.validator.addMethod("time", function(value, element) { |
| 148 | + return this.optional(element) || /^([01][0-9])|(2[0123]):([0-5])([0-9])$/.test(value); |
| 149 | + }, "Please enter a valid time, between 00:00 and 23:59" |
| 150 | +); |
| 151 | + |
| 152 | +/** |
| 153 | + * matches US phone number format |
| 154 | + * |
| 155 | + * where the area code may not start with 1 and the prefix may not start with 1 |
| 156 | + * allows '-' or ' ' as a separator and allows parens around area code |
| 157 | + * some people may want to put a '1' in front of their number |
| 158 | + * |
| 159 | + * 1(212)-999-2345 |
| 160 | + * or |
| 161 | + * 212 999 2344 |
| 162 | + * or |
| 163 | + * 212-999-0983 |
| 164 | + * |
| 165 | + * but not |
| 166 | + * 111-123-5434 |
| 167 | + * and not |
| 168 | + * 212 123 4567 |
| 169 | + */ |
| 170 | +jQuery.validator.addMethod("phoneUS", function(phone_number, element) { |
| 171 | + phone_number = phone_number.replace(/\s+/g, ""); |
| 172 | + return this.optional(element) || phone_number.length > 9 && |
| 173 | + phone_number.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/); |
| 174 | +}, "Please specify a valid phone number"); |
| 175 | + |
| 176 | +jQuery.validator.addMethod('phoneUK', function(phone_number, element) { |
| 177 | +return this.optional(element) || phone_number.length > 9 && |
| 178 | +phone_number.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/); |
| 179 | +}, 'Please specify a valid phone number'); |
| 180 | + |
| 181 | +jQuery.validator.addMethod('mobileUK', function(phone_number, element) { |
| 182 | +return this.optional(element) || phone_number.length > 9 && |
| 183 | +phone_number.match(/^((0|\+44)7(5|6|7|8|9){1}\d{2}\s?\d{6})$/); |
| 184 | +}, 'Please specify a valid mobile number'); |
| 185 | + |
| 186 | +// TODO check if value starts with <, otherwise don't try stripping anything |
| 187 | +jQuery.validator.addMethod("strippedminlength", function(value, element, param) { |
| 188 | + return jQuery(value).text().length >= param; |
| 189 | +}, jQuery.validator.format("Please enter at least {0} characters")); |
| 190 | + |
| 191 | +// same as email, but TLD is optional |
| 192 | +jQuery.validator.addMethod("email2", function(value, element, param) { |
| 193 | + return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value); |
| 194 | +}, jQuery.validator.messages.email); |
| 195 | + |
| 196 | +// same as url, but TLD is optional |
| 197 | +jQuery.validator.addMethod("url2", function(value, element, param) { |
| 198 | + return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); |
| 199 | +}, jQuery.validator.messages.url); |
| 200 | + |
| 201 | +// NOTICE: Modified version of Castle.Components.Validator.CreditCardValidator |
| 202 | +// Redistributed under the the Apache License 2.0 at http://www.apache.org/licenses/LICENSE-2.0 |
| 203 | +// Valid Types: mastercard, visa, amex, dinersclub, enroute, discover, jcb, unknown, all (overrides all other settings) |
| 204 | +jQuery.validator.addMethod("creditcardtypes", function(value, element, param) { |
| 205 | + |
| 206 | + if (/[^0-9-]+/.test(value)) |
| 207 | + return false; |
| 208 | + |
| 209 | + value = value.replace(/\D/g, ""); |
| 210 | + |
| 211 | + var validTypes = 0x0000; |
| 212 | + |
| 213 | + if (param.mastercard) |
| 214 | + validTypes |= 0x0001; |
| 215 | + if (param.visa) |
| 216 | + validTypes |= 0x0002; |
| 217 | + if (param.amex) |
| 218 | + validTypes |= 0x0004; |
| 219 | + if (param.dinersclub) |
| 220 | + validTypes |= 0x0008; |
| 221 | + if (param.enroute) |
| 222 | + validTypes |= 0x0010; |
| 223 | + if (param.discover) |
| 224 | + validTypes |= 0x0020; |
| 225 | + if (param.jcb) |
| 226 | + validTypes |= 0x0040; |
| 227 | + if (param.unknown) |
| 228 | + validTypes |= 0x0080; |
| 229 | + if (param.all) |
| 230 | + validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080; |
| 231 | + |
| 232 | + if (validTypes & 0x0001 && /^(51|52|53|54|55)/.test(value)) { //mastercard |
| 233 | + return value.length == 16; |
| 234 | + } |
| 235 | + if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa |
| 236 | + return value.length == 16; |
| 237 | + } |
| 238 | + if (validTypes & 0x0004 && /^(34|37)/.test(value)) { //amex |
| 239 | + return value.length == 15; |
| 240 | + } |
| 241 | + if (validTypes & 0x0008 && /^(300|301|302|303|304|305|36|38)/.test(value)) { //dinersclub |
| 242 | + return value.length == 14; |
| 243 | + } |
| 244 | + if (validTypes & 0x0010 && /^(2014|2149)/.test(value)) { //enroute |
| 245 | + return value.length == 15; |
| 246 | + } |
| 247 | + if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover |
| 248 | + return value.length == 16; |
| 249 | + } |
| 250 | + if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb |
| 251 | + return value.length == 16; |
| 252 | + } |
| 253 | + if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb |
| 254 | + return value.length == 15; |
| 255 | + } |
| 256 | + if (validTypes & 0x0080) { //unknown |
| 257 | + return true; |
| 258 | + } |
| 259 | + return false; |
| 260 | +}, "Please enter a valid credit card number."); |
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.validate.additional-methods.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 261 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery.autoSuggest.css |
— | — | @@ -0,0 +1,218 @@ |
| 2 | +/* AutoSuggest CSS - Version 1.2 */ |
| 3 | + |
| 4 | +ul.as-selections { |
| 5 | + list-style-type: none; |
| 6 | + list-style-image: none; |
| 7 | + border-top: 1px solid #888; |
| 8 | + border-bottom: 1px solid #b6b6b6; |
| 9 | + border-left: 1px solid #aaa; |
| 10 | + border-right: 1px solid #aaa; |
| 11 | + padding: 4px 0 4px 4px; |
| 12 | + margin: 0; |
| 13 | + overflow: auto; |
| 14 | + background-color: #fff; |
| 15 | + box-shadow:inset 0 1px 2px #888; |
| 16 | + -webkit-box-shadow:inset 0 1px 2px #888; |
| 17 | + -moz-box-shadow:inset 0 1px 2px #888; |
| 18 | +} |
| 19 | + |
| 20 | +ul.as-selections.loading { |
| 21 | + background-color: #eee; |
| 22 | +} |
| 23 | + |
| 24 | +ul.as-selections li { |
| 25 | + float: left; |
| 26 | + margin: 1px 4px 1px 0; |
| 27 | +} |
| 28 | + |
| 29 | +ul.as-selections li.as-selection-item { |
| 30 | + color: #2b3840; |
| 31 | + font-size: 13px; |
| 32 | + font-family: "Lucida Grande", arial, sans-serif; |
| 33 | + text-shadow: 0 1px 1px #fff; |
| 34 | + background-color: #ddeefe; |
| 35 | + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#ddeefe), to(#bfe0f1)); |
| 36 | + border: 1px solid #acc3ec; |
| 37 | + border-top-color: #c0d9e9; |
| 38 | + padding: 2px 7px 2px 10px; |
| 39 | + border-radius: 12px; |
| 40 | + -webkit-border-radius: 12px; |
| 41 | + -moz-border-radius: 12px; |
| 42 | + box-shadow: 0 1px 1px #e4edf2; |
| 43 | + -webkit-box-shadow: 0 1px 1px #e4edf2; |
| 44 | + -moz-box-shadow: 0 1px 1px #e4edf2; |
| 45 | +} |
| 46 | + |
| 47 | +ul.as-selections li.as-selection-item:last-child { |
| 48 | + margin-left: 30px; |
| 49 | +} |
| 50 | + |
| 51 | +ul.as-selections li.as-selection-item a.as-close { |
| 52 | + float: right; |
| 53 | + margin: 1px 0 0 7px; |
| 54 | + padding: 0 2px; |
| 55 | + cursor: pointer; |
| 56 | + color: #5491be; |
| 57 | + font-family: "Helvetica", helvetica, arial, sans-serif; |
| 58 | + font-size: 14px; |
| 59 | + font-weight: bold; |
| 60 | + text-shadow: 0 1px 1px #fff; |
| 61 | + -webkit-transition: color .1s ease-in; |
| 62 | +} |
| 63 | + |
| 64 | +ul.as-selections li.as-selection-item.blur { |
| 65 | + color: #666666; |
| 66 | + background-color: #f4f4f4; |
| 67 | + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f4f4f4), to(#d5d5d5)); |
| 68 | + border-color: #bbb; |
| 69 | + border-top-color: #ccc; |
| 70 | + box-shadow: 0 1px 1px #e9e9e9; |
| 71 | + -webkit-box-shadow: 0 1px 1px #e9e9e9; |
| 72 | + -moz-box-shadow: 0 1px 1px #e9e9e9; |
| 73 | +} |
| 74 | + |
| 75 | +ul.as-selections li.as-selection-item.blur a.as-close { |
| 76 | + color: #999; |
| 77 | +} |
| 78 | + |
| 79 | +ul.as-selections li:hover.as-selection-item { |
| 80 | + color: #2b3840; |
| 81 | + background-color: #bbd4f1; |
| 82 | + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#bbd4f1), to(#a3c2e5)); |
| 83 | + border-color: #6da0e0; |
| 84 | + border-top-color: #8bb7ed; |
| 85 | +} |
| 86 | + |
| 87 | +ul.as-selections li:hover.as-selection-item a.as-close { |
| 88 | + color: #4d70b0; |
| 89 | +} |
| 90 | + |
| 91 | +ul.as-selections li.as-selection-item.selected { |
| 92 | + border-color: #1f30e4; |
| 93 | +} |
| 94 | + |
| 95 | +ul.as-selections li.as-selection-item a:hover.as-close { |
| 96 | + color: #1b3c65; |
| 97 | +} |
| 98 | + |
| 99 | +ul.as-selections li.as-selection-item a:active.as-close { |
| 100 | + color: #4d70b0; |
| 101 | +} |
| 102 | + |
| 103 | +ul.as-selections li.as-original { |
| 104 | + margin-left: 0; |
| 105 | +} |
| 106 | + |
| 107 | +ul.as-selections li.as-original input { |
| 108 | + border: none; |
| 109 | + outline: none; |
| 110 | + font-size: 13px; |
| 111 | + width: 120px; |
| 112 | + height: 18px; |
| 113 | + padding-top: 3px; |
| 114 | +} |
| 115 | + |
| 116 | +ul.as-list { |
| 117 | + position: absolute; |
| 118 | + list-style-type: none; |
| 119 | + margin: 2px 0 0 0; |
| 120 | + padding: 0; |
| 121 | + font-size: 14px; |
| 122 | + color: #000; |
| 123 | + font-family: "Lucida Grande", arial, sans-serif; |
| 124 | + background-color: #fff; |
| 125 | + background-color: rgba(255,255,255,0.95); |
| 126 | + z-index: 2; |
| 127 | + box-shadow: 0 2px 12px #222; |
| 128 | + -webkit-box-shadow: 0 2px 12px #222; |
| 129 | + -moz-box-shadow: 0 2px 12px #222; |
| 130 | + border-radius: 5px; |
| 131 | + -webkit-border-radius: 5px; |
| 132 | + -moz-border-radius: 5px; |
| 133 | +} |
| 134 | + |
| 135 | +li.as-result-item, li.as-message { |
| 136 | + margin: 0 0 0 0; |
| 137 | + padding: 5px 12px; |
| 138 | + background-color: transparent; |
| 139 | + border: 1px solid #fff; |
| 140 | + border-bottom: 1px solid #ddd; |
| 141 | + cursor: pointer; |
| 142 | + border-radius: 5px; |
| 143 | + -webkit-border-radius: 5px; |
| 144 | + -moz-border-radius: 5px; |
| 145 | +} |
| 146 | + |
| 147 | +li:first-child.as-result-item { |
| 148 | + margin: 0; |
| 149 | +} |
| 150 | + |
| 151 | +li.as-message { |
| 152 | + margin: 0; |
| 153 | + cursor: default; |
| 154 | +} |
| 155 | + |
| 156 | +li.as-result-item.active { |
| 157 | + background-color: #3668d9; |
| 158 | + background-image: -webkit-gradient(linear, 0% 0%, 0% 64%, from(rgb(110, 129, 245)), to(rgb(62, 82, 242))); |
| 159 | + border-color: #3342e8; |
| 160 | + color: #fff; |
| 161 | + text-shadow: 0 1px 2px #122042; |
| 162 | +} |
| 163 | + |
| 164 | +li.as-result-item em { |
| 165 | + font-style: normal; |
| 166 | + background: #444; |
| 167 | + padding: 0 2px; |
| 168 | + color: #fff; |
| 169 | +} |
| 170 | + |
| 171 | +li.as-result-item.active em { |
| 172 | + background: #253f7a; |
| 173 | + color: #fff; |
| 174 | +} |
| 175 | + |
| 176 | +/* Webkit Hacks */ |
| 177 | +@media screen and (-webkit-min-device-pixel-ratio:0) { |
| 178 | + ul.as-selections { |
| 179 | + border-top-width: 2px; |
| 180 | + } |
| 181 | + ul.as-selections li.as-selection-item { |
| 182 | + padding-top: 3px; |
| 183 | + padding-bottom: 3px; |
| 184 | + } |
| 185 | + ul.as-selections li.as-selection-item a.as-close { |
| 186 | + margin-top: -1px; |
| 187 | + } |
| 188 | + ul.as-selections li.as-original input { |
| 189 | + height: 19px; |
| 190 | + } |
| 191 | +} |
| 192 | + |
| 193 | +/* Opera Hacks */ |
| 194 | +@media all and (-webkit-min-device-pixel-ratio:10000), not all and (-webkit-min-device-pixel-ratio:0) { |
| 195 | + ul.as-list { |
| 196 | + border: 1px solid #888; |
| 197 | + } |
| 198 | + ul.as-selections li.as-selection-item a.as-close { |
| 199 | + margin-left: 4px; |
| 200 | + margin-top: 0; |
| 201 | + } |
| 202 | +} |
| 203 | + |
| 204 | +/* IE Hacks */ |
| 205 | +ul.as-list { |
| 206 | + border: 1px solid #888\9; |
| 207 | +} |
| 208 | +ul.as-selections li.as-selection-item a.as-close { |
| 209 | + margin-left: 4px\9; |
| 210 | + margin-top: 0\9; |
| 211 | +} |
| 212 | + |
| 213 | +/* Firefox 3.0 Hacks */ |
| 214 | +ul.as-list, x:-moz-any-link, x:default { |
| 215 | + border: 1px solid #888; |
| 216 | +} |
| 217 | +BODY:first-of-type ul.as-list, x:-moz-any-link, x:default { /* Target FF 3.5+ */ |
| 218 | + border: none; |
| 219 | +} |
Property changes on: trunk/extensions/UploadWizard/resources/jquery.autoSuggest.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 220 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery.mwCoolCats.css |
— | — | @@ -0,0 +1,63 @@ |
| 2 | +.cat-widget { |
| 3 | + clear: both; |
| 4 | + position: relative; |
| 5 | +} |
| 6 | +.cat-widget ul { |
| 7 | + list-style: none; |
| 8 | + padding-left: 0; |
| 9 | + margin-top: 0; |
| 10 | + margin-left: 0; |
| 11 | +} |
| 12 | +.cat-widget li { |
| 13 | + float: left; |
| 14 | + margin-right: 10px; |
| 15 | + padding: 3px; |
| 16 | +} |
| 17 | +.cat-widget li a { |
| 18 | + text-decoration: none; |
| 19 | +} |
| 20 | +.cat-widget li:hover a { |
| 21 | + text-decoration: none; |
| 22 | + color: white; |
| 23 | +} |
| 24 | +.cat-widget li { |
| 25 | + background-color: #fff; |
| 26 | + border-radius: 4px; |
| 27 | + -moz-border-radius: 4px; |
| 28 | + -webkit-border-radius: 4px; |
| 29 | + border-top: 1px solid #fff; |
| 30 | + border: 1px solid #fff; |
| 31 | + color: black; |
| 32 | + font-size: 12px; |
| 33 | + |
| 34 | +} |
| 35 | +.cat-widget li:hover { |
| 36 | + background-color: #444; |
| 37 | + border-radius: 4px; |
| 38 | + -moz-border-radius: 4px; |
| 39 | + -webkit-border-radius: 4px; |
| 40 | + border-top: 1px solid #111; |
| 41 | + border: 1px solid #222; |
| 42 | + color:white; |
| 43 | +} |
| 44 | + |
| 45 | +.cat-widget li div.mwe-upwiz-remove-ctrl { |
| 46 | + display: inline-block; |
| 47 | +} |
| 48 | + |
| 49 | + |
| 50 | +/* Utilities */ |
| 51 | +.pkg:after, #content-inner:after { |
| 52 | + content: " "; |
| 53 | + display: block; |
| 54 | + visibility: hidden; |
| 55 | + clear: both; |
| 56 | + height: 0.1px; |
| 57 | + font-size: 0.1em; |
| 58 | + line-height: 0; |
| 59 | +} |
| 60 | +.pkg, #content-inner { display: inline-block; } |
| 61 | +/* no ie mac \*/ |
| 62 | +* html .pkg, * html #content-inner { height: 1%; } |
| 63 | +.pkg, #content-inner { display: block; } |
| 64 | +/* */ |
Property changes on: trunk/extensions/UploadWizard/resources/jquery.mwCoolCats.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 65 | + native |
Index: trunk/extensions/UploadWizard/resources/jquery.tipsy.help.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/resources/jquery.tipsy.help.gif |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 66 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/resources/jquery.tipsy.error.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/resources/jquery.tipsy.error.gif |
___________________________________________________________________ |
Added: svn:mime-type |
3 | 67 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/UploadWizard.php |
— | — | @@ -6,7 +6,6 @@ |
7 | 7 | * @ingroup Extensions |
8 | 8 | * |
9 | 9 | * This file contains the include file for UploadWizard |
10 | | - * This is dependent on JS2Support. |
11 | 10 | * |
12 | 11 | * Usage: Include the following line in your LocalSettings.php |
13 | 12 | * require_once( "$IP/extensions/UploadWizard/UploadWizard.php" ); |
— | — | @@ -29,22 +28,21 @@ |
30 | 29 | 'url' => 'http://www.mediawiki.org/wiki/Extension:UploadWizard' |
31 | 30 | ); |
32 | 31 | |
33 | | -// Check for JS2 support |
34 | | -if( ! isset( $wgEnableJS2system ) ){ |
35 | | - throw new MWException( 'UploadWizard requires JS2 Support. Please include the JS2Support extension.'); |
36 | | -} |
37 | 32 | |
38 | | - |
39 | 33 | $dir = dirname(__FILE__) . '/'; |
40 | 34 | |
41 | 35 | $wgExtensionMessagesFiles['UploadWizard'] = $dir . 'UploadWizard.i18n.php'; |
42 | 36 | $wgExtensionAliasesFiles['UploadWizard'] = $dir . 'UploadWizard.alias.php'; |
43 | 37 | |
44 | | -# Add the special page |
| 38 | +# Require modules, includeing the special page |
45 | 39 | $wgAutoloadLocalClasses[ 'SpecialUploadWizard' ] = $dir . 'SpecialUploadWizard.php'; |
| 40 | +$wgAutoloadLocalClasses[ 'UploadWizardMessages' ] = $dir . 'UploadWizardMessages.php'; |
| 41 | + |
| 42 | +# Let the special page be a special center of unique specialness |
46 | 43 | $wgSpecialPages['UploadWizard'] = 'SpecialUploadWizard'; |
47 | 44 | $wgSpecialPageGroups['UploadWizard'] = 'media'; |
48 | 45 | |
| 46 | +// JS2? |
49 | 47 | $wgResourceLoaderNamedPaths[ 'UploadWizardPage' ] = 'extensions/UploadWizard/UploadWizardPage.js'; |
50 | 48 | |
51 | 49 | // Set up the javascript path for the loader and localization file. |
Index: trunk/extensions/UploadWizard/SpecialUploadWizard.php |
— | — | @@ -2,7 +2,7 @@ |
3 | 3 | /** |
4 | 4 | * Special:UploadWizard |
5 | 5 | * |
6 | | - * Usability Initiative multi-file upload page. |
| 6 | + * Easy to use multi-file upload page. |
7 | 7 | * |
8 | 8 | * @file |
9 | 9 | * @ingroup SpecialPage |
— | — | @@ -13,13 +13,9 @@ |
14 | 14 | |
15 | 15 | // $request is the request (usually wgRequest) |
16 | 16 | // $par is everything in the URL after Special:UploadWizard. Not sure what we can use it for |
17 | | - public function __construct( $request=null ) { |
18 | | - global $wgEnableJS2, $wgEnableAPI, $wgRequest; |
| 17 | + public function __construct( $request=null, $par=null ) { |
| 18 | + global $wgEnableAPI, $wgRequest; |
19 | 19 | |
20 | | - if (! $wgEnableJS2) { |
21 | | - // XXX complain |
22 | | - } |
23 | | - |
24 | 20 | if (! $wgEnableAPI) { |
25 | 21 | // XXX complain |
26 | 22 | } |
— | — | @@ -39,60 +35,113 @@ |
40 | 36 | * @param subpage, e.g. the "foo" in Special:UploadWizard/foo. |
41 | 37 | */ |
42 | 38 | public function execute( $subPage ) { |
43 | | - global $wgUser, $wgOut; |
| 39 | + global $wgMessageCache, $wgScriptPath, $wgLang, $wgUser, $wgOut; |
44 | 40 | |
45 | | - # Check uploading enabled |
46 | | - if( !UploadBase::isEnabled() ) { |
47 | | - $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext' ); |
| 41 | + // canUpload and canUserUpload have side effects; |
| 42 | + // if we can't upload, will print error page to wgOut |
| 43 | + // and return false |
| 44 | + if (! ( $this->isUploadAllowed() && $this->isUserUploadAllowed( $wgUser ) ) ) { |
48 | 45 | return; |
49 | 46 | } |
50 | 47 | |
51 | | - # Check permissions |
52 | | - global $wgGroupPermissions; |
53 | | - if( !$wgUser->isAllowed( 'upload' ) ) { |
54 | | - if( !$wgUser->isLoggedIn() && ( $wgGroupPermissions['user']['upload'] |
55 | | - || $wgGroupPermissions['autoconfirmed']['upload'] ) ) { |
56 | | - // Custom message if logged-in users without any special rights can upload |
57 | | - $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); |
58 | | - } else { |
59 | | - $wgOut->permissionRequired( 'upload' ); |
60 | | - } |
61 | | - return; |
62 | | - } |
| 48 | + $langCode = $wgLang->getCode(); |
| 49 | + |
| 50 | + // XXX what does this really do?? |
| 51 | + $wgMessageCache->loadAllMessages(); |
63 | 52 | |
64 | | - # Check blocks |
65 | | - if( $wgUser->isBlocked() ) { |
66 | | - $wgOut->blockedPage(); |
67 | | - return; |
68 | | - } |
| 53 | + $this->setHeaders(); |
| 54 | + $this->outputHeader(); |
69 | 55 | |
70 | | - # Check whether we actually want to allow changing stuff |
71 | | - if( wfReadOnly() ) { |
72 | | - $wgOut->readOnlyPage(); |
73 | | - return; |
| 56 | + /* Doing resource loading the old-fashioned way for now until there's some kind of script-loading |
| 57 | + strategy that everyone agrees on, or is available generally */ |
| 58 | + $scripts = array( |
| 59 | + // jquery is already loaded by vector. |
| 60 | + // "resources/jquery-1.4.2.js", |
| 61 | + |
| 62 | + // jquery standard stuff |
| 63 | + "resources/jquery.ui/ui/ui.core.js", |
| 64 | + "resources/jquery.ui/ui/ui.progressbar.js", |
| 65 | + "resources/jquery.ui/ui/ui.datepicker.js", |
| 66 | + "resources/jquery.autocomplete.js", |
| 67 | + |
| 68 | + // miscellaneous utilities |
| 69 | + "resources/mw.Utilities.js", |
| 70 | + "resources/mw.UtilitiesTime.js", |
| 71 | + "resources/mw.Log.js", |
| 72 | + // "resources/mw.MockUploadHandler.js", |
| 73 | + |
| 74 | + // message parsing and such |
| 75 | + "resources/language/mw.Language.js", |
| 76 | + "resources/language/mw.Parser.js", |
| 77 | + "resources/mw.LanguageUpWiz.js", |
| 78 | + |
| 79 | + // workhorse libraries |
| 80 | + // "resources/mw.UploadApiProcessor.js", |
| 81 | + "resources/mw.IframeTransport.js", |
| 82 | + "resources/mw.ApiUploadHandler.js", |
| 83 | + "resources/mw.DestinationChecker.js", |
| 84 | + |
| 85 | + // interface helping stuff |
| 86 | + "resources/jquery.tipsy.js", |
| 87 | + "resources/jquery.morphCrossfade.js", |
| 88 | + "resources/jquery.validate.js", |
| 89 | + "resources/jquery.arrowSteps.js", |
| 90 | + "resources/jquery.mwCoolCats.js", |
| 91 | + |
| 92 | + // the thing that does most of it |
| 93 | + "resources/mw.UploadWizard.js", |
| 94 | + |
| 95 | + // finally the thing that launches it all |
| 96 | + "UploadWizardPage.js" |
| 97 | + ); |
| 98 | + |
| 99 | + if ($langCode !== 'en' ) { |
| 100 | + $scripts[] = "js/language/classes/Language" . ucfirst( $langCode ) . ".js"; |
74 | 101 | } |
| 102 | + |
| 103 | + $extensionPath = $wgScriptPath . "/extensions/UploadWizard"; |
| 104 | + |
| 105 | + foreach ( $scripts as $script ) { |
| 106 | + $wgOut->addScriptFile( $extensionPath . "/" . $script ); |
| 107 | + } |
| 108 | + // after scripts, get the i18n.php stuff |
| 109 | + $wgOut->addInlineScript( UploadWizardMessages::getMessagesJs( 'UploadWizard', $wgLang ) ); |
75 | 110 | |
| 111 | + $styles = array( |
| 112 | + "resources/jquery.tipsy.css", |
| 113 | + "resources/uploadWizard.css", |
| 114 | + "resources/jquery.arrowSteps.css", |
| 115 | + "resources/jquery.mwCoolCats.css" |
| 116 | + ); |
76 | 117 | |
77 | | - $this->setHeaders(); |
78 | | - $this->outputHeader(); |
79 | | - |
| 118 | + // TODO RTL |
| 119 | + foreach ( $styles as $style ) { |
| 120 | + $wgOut->addStyle( $extensionPath . "/" . $style, '', '', 'ltr' ); |
| 121 | + } |
| 122 | + |
| 123 | + $this->addJsVars( $subPage ); |
| 124 | + |
| 125 | + |
| 126 | + // where the uploadwizard will go |
| 127 | + // TODO import more from UploadWizard itself. |
80 | 128 | $wgOut->addHTML( |
81 | 129 | '<div id="upload-licensing" class="upload-section" style="display: none;">Licensing tutorial</div>' |
82 | 130 | . '<div id="upload-wizard" class="upload-section"><div class="loadingSpinner"></div></div>' |
83 | 131 | ); |
| 132 | + |
84 | 133 | |
| 134 | + // fallback for non-JS |
85 | 135 | $wgOut->addHTML('<noscript>'); |
86 | 136 | $this->simpleForm->show(); |
87 | 137 | $wgOut->addHTML('</noscript>'); |
88 | | - |
89 | | - $this->addJS( $subPage ); |
| 138 | + |
90 | 139 | } |
91 | 140 | |
92 | 141 | /** |
93 | 142 | * Adds some global variables for our use, as well as initializes the UploadWizard |
94 | 143 | * @param subpage, e.g. the "foo" in Special:UploadWizard/foo |
95 | 144 | */ |
96 | | - public function addJS( $subPage ) { |
| 145 | + public function addJsVars( $subPage ) { |
97 | 146 | global $wgUser, $wgOut; |
98 | 147 | global $wgUseAjax, $wgAjaxLicensePreview, $wgEnableAPI; |
99 | 148 | global $wgEnableFirefogg, $wgFileExtensions; |
— | — | @@ -115,27 +164,64 @@ |
116 | 165 | // XXX need to have a better function for testing viability of a filename |
117 | 166 | // 'wgFilenamePrefixBlacklist' => UploadBase::getFilenamePrefixBlacklist() |
118 | 167 | |
119 | | - ) ) |
120 | | - ); |
| 168 | + ) ) ); |
121 | 169 | |
122 | | - // not sure why -- can we even load libraries with an included script, or does that cause things to be out of order? |
123 | | - global $wgScriptPath; |
124 | | - $wgOut->addNamedResource( 'UploadWizardPage', 'page'); |
| 170 | + } |
125 | 171 | |
| 172 | + /** |
| 173 | + * Check if anyone can upload (or if other sitewide config prevents this) |
| 174 | + * Side effect: will print error page to wgOut if cannot upload. |
| 175 | + * @return boolean -- true if can upload |
| 176 | + */ |
| 177 | + private function isUploadAllowed() { |
| 178 | + global $wgOut; |
126 | 179 | |
127 | | - // XXX unlike other vars this is specific to the file being uploaded -- re-upload context, for instance |
128 | | - // Recorded here because we may probably need to |
129 | | - // bring it back in some form later. Reupload forms may be special, only one file allowed |
130 | | - /* |
131 | | - $scriptVars = array( |
132 | | - 'wgUploadAutoFill' => !$this->mForReUpload, |
133 | | - 'wgUploadSourceIds' => $this->mSourceIds, |
134 | | - ); |
135 | | - */ |
| 180 | + // Check uploading enabled |
| 181 | + if( !UploadBase::isEnabled() ) { |
| 182 | + $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext' ); |
| 183 | + return false; |
| 184 | + } |
136 | 185 | |
| 186 | + // Check whether we actually want to allow changing stuff |
| 187 | + if( wfReadOnly() ) { |
| 188 | + $wgOut->readOnlyPage(); |
| 189 | + return false; |
| 190 | + } |
137 | 191 | |
| 192 | + // we got all the way here, so it must be okay to upload |
| 193 | + return true; |
138 | 194 | } |
139 | 195 | |
| 196 | + /** |
| 197 | + * Check if the user can upload |
| 198 | + * Side effect: will print error page to wgOut if cannot upload. |
| 199 | + * @param User |
| 200 | + * @return boolean -- true if can upload |
| 201 | + */ |
| 202 | + private function isUserUploadAllowed( $user ) { |
| 203 | + global $wgOut, $wgGroupPermissions; |
| 204 | + |
| 205 | + if( !$user->isAllowed( 'upload' ) ) { |
| 206 | + if( !$user->isLoggedIn() && ( $wgGroupPermissions['user']['upload'] |
| 207 | + || $wgGroupPermissions['autoconfirmed']['upload'] ) ) { |
| 208 | + // Custom message if logged-in users without any special rights can upload |
| 209 | + $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); |
| 210 | + } else { |
| 211 | + $wgOut->permissionRequired( 'upload' ); |
| 212 | + } |
| 213 | + return false; |
| 214 | + } |
| 215 | + |
| 216 | + // Check blocks |
| 217 | + if( $user->isBlocked() ) { |
| 218 | + $wgOut->blockedPage(); |
| 219 | + return false; |
| 220 | + } |
| 221 | + |
| 222 | + // we got all the way here, so it must be okay to upload |
| 223 | + return true; |
| 224 | + } |
| 225 | + |
140 | 226 | } |
141 | 227 | |
142 | 228 | |
Index: trunk/extensions/UploadWizard/README |
— | — | @@ -1,11 +1,10 @@ |
2 | | -UploadWizard is alpha software, not intended for use live Wikimedia sites yet (or anyone else's). |
| 2 | +Developers should not modify the contents of mwEmbed.i18n.php directly. |
3 | 3 | |
4 | | -UploadWizard dependent on js2 / mwEmbed, you must include JS2Support extension prior to UploadWizard.php |
| 4 | +mwEmbed.i18n.php is updated by maintenance scripts. |
5 | 5 | |
6 | | -# To enable add the following to localSettings.php |
| 6 | +Two scripts write to this file: |
| 7 | +* mwEmbed/includes/maintenance/mergeJavascriptMsg.php |
| 8 | +Copies English messages from every mwEmbed javascript module |
7 | 9 | |
8 | | -# JS2 / mwEmbed Support |
9 | | -require_once( "$IP/extensions/JS2Support/JS2Support.php" ); |
10 | | - |
11 | | -# Upload Wizard |
12 | | -require_once( "$IP/extensions/UploadWizard/UploadWizard.php" ); |
| 10 | +* Translate wiki script |
| 11 | +Copies the translate wiki localised msgs into the file. |
\ No newline at end of file |
Index: trunk/extensions/UploadWizard/UploadWizardPage.js |
— | — | @@ -1,86 +1,87 @@ |
2 | | - |
3 | 2 | /* |
4 | 3 | * This script is run on [[Special:UploadWizard]]. |
5 | 4 | * Creates an interface for uploading files in multiple steps, hence "wizard" |
6 | 5 | */ |
7 | 6 | |
8 | | - |
9 | | - |
10 | 7 | // create UploadWizard |
11 | | -mw.ready( function() { |
| 8 | +mw.UploadWizardPage = function() { |
12 | 9 | // add the discussion link |
13 | | - var discussListItem = addPortletLink( 'p-namespaces', |
14 | | - 'http://usability.wikimedia.org/wiki/Multimedia_talk:Upload_wizard', |
15 | | - 'Discussion', |
16 | | - 'usability_upload_wizard_discussion', |
17 | | - 'Discuss this experimental extension at the Usability wiki'); |
| 10 | + var discussListItem = addPortletLink( 'p-namespaces', |
| 11 | + 'http://usability.wikimedia.org/wiki/Multimedia:Upload_wizard/Questions_%26_Answers', |
| 12 | + 'Q & A', |
| 13 | + 'usability_upload_wizard_qa', |
| 14 | + 'Questions & answers about this experimental extension at the Usability wiki'); |
18 | 15 | var discussLink = discussListItem.getElementsByTagName( 'a' )[0]; |
19 | 16 | discussLink.setAttribute( 'target', 'usability_discussion' ); |
20 | 17 | |
21 | | - mw.load( 'UploadWizard.UploadWizard', function () { |
22 | | - mw.setConfig( 'debug', true ); |
| 18 | + |
| 19 | + var apiUrl = false; |
| 20 | + if ( typeof wgServer != 'undefined' && typeof wgScriptPath != 'undefined' ) { |
| 21 | + apiUrl = wgServer + wgScriptPath + '/api.php'; |
| 22 | + } |
23 | 23 | |
24 | | - mw.setDefaultConfig( 'uploadHandlerClass', null ); |
| 24 | + var config = { |
| 25 | + debug: true, |
| 26 | + userName: wgUserName, |
| 27 | + userLanguage: wgUserLanguage, |
| 28 | + fileExtensions: wgFileExtensions, |
| 29 | + apiUrl: apiUrl, |
25 | 30 | |
26 | | - mw.setConfig( { |
27 | | - debug: true, |
28 | | - userName: wgUserName, |
29 | | - userLanguage: wgUserLanguage, |
30 | | - fileExtensions: wgFileExtensions, |
| 31 | + // XXX this is problematic, if the upload wizard is idle for a long time the token expires |
| 32 | + // should get token just before uploading |
| 33 | + token: wgEditToken, |
| 34 | + |
| 35 | + thumbnailWidth: 120, |
| 36 | + smallThumbnailWidth: 60, |
| 37 | + maxAuthorLength: 50, |
| 38 | + minAuthorLength: 2, |
| 39 | + maxSourceLength: 200, |
| 40 | + minSourceLength: 5, |
| 41 | + maxTitleLength: 200, |
| 42 | + minTitleLength: 5, |
| 43 | + maxDescriptionLength: 4096, |
| 44 | + minDescriptionLength: 5, |
| 45 | + maxOtherInformationLength: 4096, |
| 46 | + maxSimultaneousConnections: 2, |
| 47 | + maxUploads: 10, |
31 | 48 | |
32 | | - // XXX this is problematic, if the upload wizard is idle for a long time the token expires |
33 | | - // should get token just before uploading |
34 | | - token: wgEditToken, |
35 | | - |
36 | | - thumbnailWidth: 120, |
37 | | - smallThumbnailWidth: 60, |
38 | | - maxAuthorLength: 50, |
39 | | - minAuthorLength: 2, |
40 | | - maxSourceLength: 200, |
41 | | - minSourceLength: 5, |
42 | | - maxTitleLength: 200, |
43 | | - minTitleLength: 5, |
44 | | - maxDescriptionLength: 4096, |
45 | | - minDescriptionLength: 5, |
46 | | - maxOtherInformationLength: 4096, |
47 | | - maxSimultaneousConnections: 2, |
48 | | - maxUploads: 10, |
| 49 | + // not for use with all wikis. |
| 50 | + // The ISO 639 code for the language tagalog is "tl". |
| 51 | + // Normally we name templates for languages by the ISO 639 code. |
| 52 | + // Commons already had a template called 'tl: though. |
| 53 | + // so, this workaround will cause tagalog descriptions to be saved with this template instead. |
| 54 | + languageTemplateFixups: { tl: 'tgl' }, |
49 | 55 | |
50 | | - // not for use with all wikis. |
51 | | - // The ISO 639 code for the language tagalog is "tl". |
52 | | - // Normally we name templates for languages by the ISO 639 code. |
53 | | - // Commons already had a template called 'tl: though. |
54 | | - // so, this workaround will cause tagalog descriptions to be saved with this template instead. |
55 | | - languageTemplateFixups: { tl: 'tgl' }, |
| 56 | + // names of all license templates, in order. Case sensitive! |
| 57 | + // n.b. in the future, the licenses for a wiki will probably be defined in PHP or even LocalSettings. |
| 58 | + licenses: [ |
| 59 | + { template: 'Cc-by-sa-3.0', messageKey: 'mwe-upwiz-license-cc-by-sa-3.0', 'default': true }, |
| 60 | + { template: 'Cc-by-3.0', messageKey: 'mwe-upwiz-license-cc-by-3.0', 'default': false }, |
| 61 | + { template: 'Cc-zero', messageKey: 'mwe-upwiz-license-cc-zero', 'default': false }, |
| 62 | + // n.b. the PD-US is only for testing purposes, obviously we need some geographical discrimination here... |
| 63 | + { template: 'PD-US', messageKey: 'mwe-upwiz-license-pd-us', 'default': false }, |
| 64 | + { template: 'GFDL', messageKey: 'mwe-upwiz-license-gfdl', 'default': false } |
| 65 | + ] |
56 | 66 | |
57 | | - // names of all license templates, in order. Case sensitive! |
58 | | - // n.b. in the future, the licenses for a wiki will probably be defined in PHP or even LocalSettings. |
59 | | - licenses: [ |
60 | | - { template: 'Cc-by-sa-3.0', messageKey: 'mwe-upwiz-license-cc-by-sa-3.0', 'default': true }, |
61 | | - { template: 'Cc-by-3.0', messageKey: 'mwe-upwiz-license-cc-by-3.0', 'default': false }, |
62 | | - { template: 'Cc-zero', messageKey: 'mwe-upwiz-license-cc-zero', 'default': false }, |
63 | | - // n.b. the PD-US is only for testing purposes, obviously we need some geographical discrimination here... |
64 | | - { template: 'PD-US', messageKey: 'mwe-upwiz-license-pd-us', 'default': false }, |
65 | | - { template: 'GFDL', messageKey: 'mwe-upwiz-license-gfdl', 'default': false } |
66 | | - ], |
| 67 | + // XXX this is horribly confusing -- some file restrictions are client side, others are server side |
| 68 | + // the filename prefix blacklist is at least server side -- all this should be replaced with PHP regex config |
| 69 | + // or actually, in an ideal world, we'd have some way to reliably detect gibberish, rather than trying to |
| 70 | + // figure out what is bad via individual regexes, we'd detect badness. Might not be too hard. |
| 71 | + // |
| 72 | + // we can export these to JS if we so want. |
| 73 | + // filenamePrefixBlacklist: wgFilenamePrefixBlacklist, |
| 74 | + // |
| 75 | + // filenameRegexBlacklist: [ |
| 76 | + // /^(test|image|img|bild|example?[\s_-]*)$/, // test stuff |
| 77 | + // /^(\d{10}[\s_-][0-9a-f]{10}[\s_-][a-z])$/ // flickr |
| 78 | + // ] |
| 79 | + }; |
67 | 80 | |
68 | | - // XXX this is horribly confusing -- some file restrictions are client side, others are server side |
69 | | - // the filename prefix blacklist is at least server side -- all this should be replaced with PHP regex config |
70 | | - // or actually, in an ideal world, we'd have some way to reliably detect gibberish, rather than trying to |
71 | | - // figure out what is bad via individual regexes, we'd detect badness. Might not be too hard. |
72 | | - // |
73 | | - // we can export these to JS if we so want. |
74 | | - // filenamePrefixBlacklist: wgFilenamePrefixBlacklist, |
75 | | - // |
76 | | - // filenameRegexBlacklist: [ |
77 | | - // /^(test|image|img|bild|example?[\s_-]*)$/, // test stuff |
78 | | - // /^(\d{10}[\s_-][0-9a-f]{10}[\s_-][a-z])$/ // flickr |
79 | | - // ] |
80 | | - }); |
| 81 | + var uploadWizard = new mw.UploadWizard( config ); |
| 82 | + uploadWizard.createInterface( '#upload-wizard' ); |
81 | 83 | |
82 | | - var uploadWizard = new mw.UploadWizard(); |
83 | | - uploadWizard.createInterface( '#upload-wizard' ); |
84 | | - |
85 | | - } ); |
| 84 | +} |
| 85 | + |
| 86 | +$j( document ).ready( function() { |
| 87 | + mw.UploadWizardPage(); |
86 | 88 | } ); |
87 | | - |
Index: trunk/extensions/UploadWizard/loader.js |
— | — | @@ -60,36 +60,71 @@ |
61 | 61 | |
62 | 62 | var libraries = [ |
63 | 63 | [ |
64 | | - '$j.ui', |
65 | | - '$j.ui.progressbar', |
66 | | - '$j.ui.dialog', |
67 | | - '$j.ui.draggable', |
68 | | - '$j.ui.datepicker', |
69 | | - '$j.effects', |
70 | | - '$j.effects.slide', |
71 | | - //'$j.effects.pulsate', |
72 | | - '$j.fn.autocomplete', |
73 | | - '$j.fn.tipsy', |
74 | | - 'mw.style.tipsy', |
75 | | - '$j.fn.morphCrossfade', |
76 | | - '$j.fn.validate', |
77 | | - '$j.fn.arrowSteps', |
78 | | - '$j.fn.mwCoolCats', |
79 | | - 'mw.style.arrowSteps', |
80 | | - 'mw.style.autocomplete', |
| 64 | + '$j.ui' |
| 65 | + ], |
| 66 | + [ |
| 67 | + '$j.ui.progressbar' |
| 68 | + ], |
| 69 | + [ |
| 70 | + '$j.ui.datepicker' |
| 71 | + ], |
| 72 | + [ |
| 73 | + '$j.effects' |
| 74 | + ], |
| 75 | + [ |
| 76 | + '$j.effects.slide' |
| 77 | + ], |
| 78 | + [ |
| 79 | + 'mw.style.autocomplete' |
| 80 | + ], |
| 81 | + [ |
| 82 | + '$j.fn.autocomplete' |
| 83 | + ], |
| 84 | + [ |
| 85 | + 'mw.style.tipsy' |
| 86 | + ], |
| 87 | + [ |
| 88 | + '$j.fn.tipsy' |
| 89 | + ], |
| 90 | + [ |
| 91 | + '$j.fn.morphCrossfade' |
| 92 | + ], |
| 93 | + [ |
| 94 | + '$j.fn.validate' |
| 95 | + ], |
| 96 | + [ |
| 97 | + 'mw.style.arrowSteps' |
| 98 | + ], |
| 99 | + [ |
| 100 | + '$j.fn.arrowSteps' |
| 101 | + ], |
| 102 | + [ |
81 | 103 | 'mw.style.mwCoolCats' |
82 | 104 | ], |
83 | 105 | [ |
84 | | - 'mw.LanguageUpWiz', |
85 | | - 'mw.IframeTransport', |
86 | | - 'mw.ApiUploadHandler', |
87 | | - 'mw.DestinationChecker', |
88 | | - 'mw.UploadWizard', |
| 106 | + '$j.fn.mwCoolCats' |
| 107 | + ], |
| 108 | + [ |
| 109 | + 'mw.LanguageUpWiz' |
| 110 | + ], |
| 111 | + [ |
| 112 | + 'mw.IframeTransport' |
| 113 | + ], |
| 114 | + [ |
| 115 | + 'mw.ApiUploadHandler' |
| 116 | + ], |
| 117 | + [ |
| 118 | + 'mw.DestinationChecker' |
| 119 | + ], |
| 120 | + [ |
89 | 121 | 'mw.style.uploadWizard' |
90 | 122 | ], |
| 123 | + [ |
| 124 | + 'mw.UploadWizard' |
| 125 | + ] |
91 | 126 | ]; |
92 | 127 | |
93 | | - var testLibraries = libraries.slice( 0 ) |
| 128 | + var testLibraries = libraries.slice( 0 ); |
94 | 129 | testLibraries.push( [ 'mw.MockUploadHandler' ] ); |
95 | 130 | |
96 | 131 | /** |
Index: trunk/extensions/UploadWizard/styles/inactive-arrow-divider.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Index: trunk/extensions/UploadWizard/styles/toggle-open.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Index: trunk/extensions/UploadWizard/styles/arrow-tail.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Index: trunk/extensions/UploadWizard/styles/toggle.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Index: trunk/extensions/UploadWizard/styles/arrow-head.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Index: trunk/extensions/UploadWizard/styles/calendar.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Index: trunk/extensions/UploadWizard/styles/jquery.tipsy.css |
— | — | @@ -1,30 +0,0 @@ |
2 | | -.tipsy { padding: 5px; font-size: small; position: absolute; z-index: 100000; } |
3 | | - .tipsy-inner { padding: 5px 8px 4px 8px; background-color: black; color: white; max-width: 200px; text-align: center; } |
4 | | - .tipsy-inner { border-radius: 3px; -moz-border-radius:3px; -webkit-border-radius:3px; } |
5 | | - .tipsy-arrow { position: absolute; background: url('jquery.tipsy.gif') no-repeat top left; width: 9px; height: 5px; } |
6 | | - .tipsy-n .tipsy-arrow { top: 0; left: 50%; margin-left: -4px; } |
7 | | - .tipsy-nw .tipsy-arrow { top: 0; left: 10px; } |
8 | | - .tipsy-ne .tipsy-arrow { top: 0; right: 10px; } |
9 | | - .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -4px; background-position: bottom left; } |
10 | | - .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; background-position: bottom left; } |
11 | | - .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; background-position: bottom left; } |
12 | | - .tipsy-e .tipsy-arrow { top: 50%; margin-top: -4px; right: 0; width: 5px; height: 9px; background-position: top right; } |
13 | | - .tipsy-w .tipsy-arrow { top: 50%; margin-top: -4px; left: 0; width: 5px; height: 9px; } |
14 | | - |
15 | | - |
16 | | -.tipsy-help .tipsy-inner { background-color: #96d8d9; color: black; } |
17 | | -.tipsy-help .tipsy-arrow { background: url('jquery.tipsy.help.gif') } |
18 | | - |
19 | | -.tipsy-error .tipsy-inner { background-color: #f89c90; color: black; } |
20 | | -.tipsy-error .tipsy-arrow { background: url('jquery.tipsy.error.gif') } |
21 | | - |
22 | | -.shadow { |
23 | | - /* offset left, top, thickness, color with alpha */ |
24 | | - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); |
25 | | - -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); |
26 | | - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); |
27 | | - /* IE */ |
28 | | - filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray'); |
29 | | - /* slightly different syntax for IE8 */ |
30 | | - -ms-filter:"progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray')"; |
31 | | -} |
Index: trunk/extensions/UploadWizard/styles/jquery.arrowSteps.css |
— | — | @@ -1,40 +0,0 @@ |
2 | | -.arrowSteps { |
3 | | - list-style-type: none; |
4 | | - list-style-image: none; |
5 | | - border: 1px solid #666666; |
6 | | - position: relative; |
7 | | -} |
8 | | - |
9 | | -.arrowSteps li { |
10 | | - float: left; |
11 | | - padding: 0px; |
12 | | - margin: 0px; |
13 | | - border: 0 none; |
14 | | -} |
15 | | - |
16 | | -.arrowSteps li div { |
17 | | - padding: 0.5em; |
18 | | - text-align: center; |
19 | | - white-space: nowrap; |
20 | | - overflow: hidden; |
21 | | -} |
22 | | - |
23 | | -.arrowSteps li.arrow div { |
24 | | - background: url(inactive-arrow-divider.png) no-repeat right center; |
25 | | -} |
26 | | - |
27 | | -/* applied to the element preceding the highlighted step */ |
28 | | -.arrowSteps li.arrow.tail div { |
29 | | - background: url(arrow-tail.png) no-repeat right center; |
30 | | -} |
31 | | - |
32 | | -/* this applies to all highlighted, including the last */ |
33 | | -.arrowSteps li.head div { |
34 | | - background: url(arrow-head.png) no-repeat left center; |
35 | | - font-weight: bold; |
36 | | -} |
37 | | - |
38 | | -/* this applies to all highlighted arrows except the last */ |
39 | | -.arrowSteps li.arrow.head div { |
40 | | - background: url(arrow-head.png) no-repeat right center; |
41 | | -} |
Index: trunk/extensions/UploadWizard/styles/jquery.autoSuggest.css |
— | — | @@ -1,218 +0,0 @@ |
2 | | -/* AutoSuggest CSS - Version 1.2 */ |
3 | | - |
4 | | -ul.as-selections { |
5 | | - list-style-type: none; |
6 | | - list-style-image: none; |
7 | | - border-top: 1px solid #888; |
8 | | - border-bottom: 1px solid #b6b6b6; |
9 | | - border-left: 1px solid #aaa; |
10 | | - border-right: 1px solid #aaa; |
11 | | - padding: 4px 0 4px 4px; |
12 | | - margin: 0; |
13 | | - overflow: auto; |
14 | | - background-color: #fff; |
15 | | - box-shadow:inset 0 1px 2px #888; |
16 | | - -webkit-box-shadow:inset 0 1px 2px #888; |
17 | | - -moz-box-shadow:inset 0 1px 2px #888; |
18 | | -} |
19 | | - |
20 | | -ul.as-selections.loading { |
21 | | - background-color: #eee; |
22 | | -} |
23 | | - |
24 | | -ul.as-selections li { |
25 | | - float: left; |
26 | | - margin: 1px 4px 1px 0; |
27 | | -} |
28 | | - |
29 | | -ul.as-selections li.as-selection-item { |
30 | | - color: #2b3840; |
31 | | - font-size: 13px; |
32 | | - font-family: "Lucida Grande", arial, sans-serif; |
33 | | - text-shadow: 0 1px 1px #fff; |
34 | | - background-color: #ddeefe; |
35 | | - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#ddeefe), to(#bfe0f1)); |
36 | | - border: 1px solid #acc3ec; |
37 | | - border-top-color: #c0d9e9; |
38 | | - padding: 2px 7px 2px 10px; |
39 | | - border-radius: 12px; |
40 | | - -webkit-border-radius: 12px; |
41 | | - -moz-border-radius: 12px; |
42 | | - box-shadow: 0 1px 1px #e4edf2; |
43 | | - -webkit-box-shadow: 0 1px 1px #e4edf2; |
44 | | - -moz-box-shadow: 0 1px 1px #e4edf2; |
45 | | -} |
46 | | - |
47 | | -ul.as-selections li.as-selection-item:last-child { |
48 | | - margin-left: 30px; |
49 | | -} |
50 | | - |
51 | | -ul.as-selections li.as-selection-item a.as-close { |
52 | | - float: right; |
53 | | - margin: 1px 0 0 7px; |
54 | | - padding: 0 2px; |
55 | | - cursor: pointer; |
56 | | - color: #5491be; |
57 | | - font-family: "Helvetica", helvetica, arial, sans-serif; |
58 | | - font-size: 14px; |
59 | | - font-weight: bold; |
60 | | - text-shadow: 0 1px 1px #fff; |
61 | | - -webkit-transition: color .1s ease-in; |
62 | | -} |
63 | | - |
64 | | -ul.as-selections li.as-selection-item.blur { |
65 | | - color: #666666; |
66 | | - background-color: #f4f4f4; |
67 | | - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f4f4f4), to(#d5d5d5)); |
68 | | - border-color: #bbb; |
69 | | - border-top-color: #ccc; |
70 | | - box-shadow: 0 1px 1px #e9e9e9; |
71 | | - -webkit-box-shadow: 0 1px 1px #e9e9e9; |
72 | | - -moz-box-shadow: 0 1px 1px #e9e9e9; |
73 | | -} |
74 | | - |
75 | | -ul.as-selections li.as-selection-item.blur a.as-close { |
76 | | - color: #999; |
77 | | -} |
78 | | - |
79 | | -ul.as-selections li:hover.as-selection-item { |
80 | | - color: #2b3840; |
81 | | - background-color: #bbd4f1; |
82 | | - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#bbd4f1), to(#a3c2e5)); |
83 | | - border-color: #6da0e0; |
84 | | - border-top-color: #8bb7ed; |
85 | | -} |
86 | | - |
87 | | -ul.as-selections li:hover.as-selection-item a.as-close { |
88 | | - color: #4d70b0; |
89 | | -} |
90 | | - |
91 | | -ul.as-selections li.as-selection-item.selected { |
92 | | - border-color: #1f30e4; |
93 | | -} |
94 | | - |
95 | | -ul.as-selections li.as-selection-item a:hover.as-close { |
96 | | - color: #1b3c65; |
97 | | -} |
98 | | - |
99 | | -ul.as-selections li.as-selection-item a:active.as-close { |
100 | | - color: #4d70b0; |
101 | | -} |
102 | | - |
103 | | -ul.as-selections li.as-original { |
104 | | - margin-left: 0; |
105 | | -} |
106 | | - |
107 | | -ul.as-selections li.as-original input { |
108 | | - border: none; |
109 | | - outline: none; |
110 | | - font-size: 13px; |
111 | | - width: 120px; |
112 | | - height: 18px; |
113 | | - padding-top: 3px; |
114 | | -} |
115 | | - |
116 | | -ul.as-list { |
117 | | - position: absolute; |
118 | | - list-style-type: none; |
119 | | - margin: 2px 0 0 0; |
120 | | - padding: 0; |
121 | | - font-size: 14px; |
122 | | - color: #000; |
123 | | - font-family: "Lucida Grande", arial, sans-serif; |
124 | | - background-color: #fff; |
125 | | - background-color: rgba(255,255,255,0.95); |
126 | | - z-index: 2; |
127 | | - box-shadow: 0 2px 12px #222; |
128 | | - -webkit-box-shadow: 0 2px 12px #222; |
129 | | - -moz-box-shadow: 0 2px 12px #222; |
130 | | - border-radius: 5px; |
131 | | - -webkit-border-radius: 5px; |
132 | | - -moz-border-radius: 5px; |
133 | | -} |
134 | | - |
135 | | -li.as-result-item, li.as-message { |
136 | | - margin: 0 0 0 0; |
137 | | - padding: 5px 12px; |
138 | | - background-color: transparent; |
139 | | - border: 1px solid #fff; |
140 | | - border-bottom: 1px solid #ddd; |
141 | | - cursor: pointer; |
142 | | - border-radius: 5px; |
143 | | - -webkit-border-radius: 5px; |
144 | | - -moz-border-radius: 5px; |
145 | | -} |
146 | | - |
147 | | -li:first-child.as-result-item { |
148 | | - margin: 0; |
149 | | -} |
150 | | - |
151 | | -li.as-message { |
152 | | - margin: 0; |
153 | | - cursor: default; |
154 | | -} |
155 | | - |
156 | | -li.as-result-item.active { |
157 | | - background-color: #3668d9; |
158 | | - background-image: -webkit-gradient(linear, 0% 0%, 0% 64%, from(rgb(110, 129, 245)), to(rgb(62, 82, 242))); |
159 | | - border-color: #3342e8; |
160 | | - color: #fff; |
161 | | - text-shadow: 0 1px 2px #122042; |
162 | | -} |
163 | | - |
164 | | -li.as-result-item em { |
165 | | - font-style: normal; |
166 | | - background: #444; |
167 | | - padding: 0 2px; |
168 | | - color: #fff; |
169 | | -} |
170 | | - |
171 | | -li.as-result-item.active em { |
172 | | - background: #253f7a; |
173 | | - color: #fff; |
174 | | -} |
175 | | - |
176 | | -/* Webkit Hacks */ |
177 | | -@media screen and (-webkit-min-device-pixel-ratio:0) { |
178 | | - ul.as-selections { |
179 | | - border-top-width: 2px; |
180 | | - } |
181 | | - ul.as-selections li.as-selection-item { |
182 | | - padding-top: 3px; |
183 | | - padding-bottom: 3px; |
184 | | - } |
185 | | - ul.as-selections li.as-selection-item a.as-close { |
186 | | - margin-top: -1px; |
187 | | - } |
188 | | - ul.as-selections li.as-original input { |
189 | | - height: 19px; |
190 | | - } |
191 | | -} |
192 | | - |
193 | | -/* Opera Hacks */ |
194 | | -@media all and (-webkit-min-device-pixel-ratio:10000), not all and (-webkit-min-device-pixel-ratio:0) { |
195 | | - ul.as-list { |
196 | | - border: 1px solid #888; |
197 | | - } |
198 | | - ul.as-selections li.as-selection-item a.as-close { |
199 | | - margin-left: 4px; |
200 | | - margin-top: 0; |
201 | | - } |
202 | | -} |
203 | | - |
204 | | -/* IE Hacks */ |
205 | | -ul.as-list { |
206 | | - border: 1px solid #888\9; |
207 | | -} |
208 | | -ul.as-selections li.as-selection-item a.as-close { |
209 | | - margin-left: 4px\9; |
210 | | - margin-top: 0\9; |
211 | | -} |
212 | | - |
213 | | -/* Firefox 3.0 Hacks */ |
214 | | -ul.as-list, x:-moz-any-link, x:default { |
215 | | - border: 1px solid #888; |
216 | | -} |
217 | | -BODY:first-of-type ul.as-list, x:-moz-any-link, x:default { /* Target FF 3.5+ */ |
218 | | - border: none; |
219 | | -} |
Index: trunk/extensions/UploadWizard/styles/jquery.mwCoolCats.css |
— | — | @@ -1,63 +0,0 @@ |
2 | | -.cat-widget { |
3 | | - clear: both; |
4 | | - position: relative; |
5 | | -} |
6 | | -.cat-widget ul { |
7 | | - list-style: none; |
8 | | - padding-left: 0; |
9 | | - margin-top: 0; |
10 | | - margin-left: 0; |
11 | | -} |
12 | | -.cat-widget li { |
13 | | - float: left; |
14 | | - margin-right: 10px; |
15 | | - padding: 3px; |
16 | | -} |
17 | | -.cat-widget li a { |
18 | | - text-decoration: none; |
19 | | -} |
20 | | -.cat-widget li:hover a { |
21 | | - text-decoration: none; |
22 | | - color: white; |
23 | | -} |
24 | | -.cat-widget li { |
25 | | - background-color: #fff; |
26 | | - border-radius: 4px; |
27 | | - -moz-border-radius: 4px; |
28 | | - -webkit-border-radius: 4px; |
29 | | - border-top: 1px solid #fff; |
30 | | - border: 1px solid #fff; |
31 | | - color: black; |
32 | | - font-size: 12px; |
33 | | - |
34 | | -} |
35 | | -.cat-widget li:hover { |
36 | | - background-color: #444; |
37 | | - border-radius: 4px; |
38 | | - -moz-border-radius: 4px; |
39 | | - -webkit-border-radius: 4px; |
40 | | - border-top: 1px solid #111; |
41 | | - border: 1px solid #222; |
42 | | - color:white; |
43 | | -} |
44 | | - |
45 | | -.cat-widget li div.mwe-upwiz-remove-ctrl { |
46 | | - display: inline-block; |
47 | | -} |
48 | | - |
49 | | - |
50 | | -/* Utilities */ |
51 | | -.pkg:after, #content-inner:after { |
52 | | - content: " "; |
53 | | - display: block; |
54 | | - visibility: hidden; |
55 | | - clear: both; |
56 | | - height: 0.1px; |
57 | | - font-size: 0.1em; |
58 | | - line-height: 0; |
59 | | -} |
60 | | -.pkg, #content-inner { display: inline-block; } |
61 | | -/* no ie mac \*/ |
62 | | -* html .pkg, * html #content-inner { height: 1%; } |
63 | | -.pkg, #content-inner { display: block; } |
64 | | -/* */ |
Index: trunk/extensions/UploadWizard/styles/jquery.tipsy.help.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Index: trunk/extensions/UploadWizard/styles/jquery.tipsy.error.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Index: trunk/extensions/UploadWizard/styles/jquery.tipsy.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Index: trunk/extensions/UploadWizard/styles/spinner-orange.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Index: trunk/extensions/UploadWizard/styles/checkmark.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Index: trunk/extensions/UploadWizard/styles/uploadWizard.css |
— | — | @@ -1,558 +0,0 @@ |
2 | | - |
3 | | - |
4 | | -/* min-width is about 550px, maybe about 52em |
5 | | -/* max-width is about 875px about 87em? |
6 | | -#upload-wizard { |
7 | | - |
8 | | -} |
9 | | -*/ |
10 | | - |
11 | | - |
12 | | -form.mwe-upwiz-form { |
13 | | - display: inline; |
14 | | -} |
15 | | - |
16 | | -#upload-wizard { |
17 | | - margin-top: 18px; |
18 | | - min-width: 32em; |
19 | | - max-width: 64em; |
20 | | -} |
21 | | - |
22 | | -/* |
23 | | -.upload-section { |
24 | | - padding: 1em; |
25 | | - margin-bottom: 0.5em; |
26 | | - margin-top: 0.5em; |
27 | | - border: 1px solid #e0e0e0; |
28 | | -} |
29 | | -*/ |
30 | | - |
31 | | - |
32 | | -.mwe-upwiz-clearing { |
33 | | - clear: left; |
34 | | - width: 100%; |
35 | | -} |
36 | | - |
37 | | - |
38 | | -#mwe-upwiz-content { |
39 | | - padding: 1em; |
40 | | -} |
41 | | - |
42 | | -.mwe-upwiz-add-files-0, #mwe-upwiz-files { |
43 | | - margin-top: 3em; |
44 | | - margin-bottom: 3em; |
45 | | -} |
46 | | - |
47 | | -.mwe-upwiz-add-files-0 { |
48 | | - text-align: center; |
49 | | - font-size: large; |
50 | | -} |
51 | | - |
52 | | -#mwe-upwiz-add-file { |
53 | | -} |
54 | | - |
55 | | -/* NOT a pseudoclass */ |
56 | | -#mwe-upwiz-add-file.hover { |
57 | | - text-decoration: underline; |
58 | | -} |
59 | | - |
60 | | -/* perhaps a general class for links that are actually "buttons" */ |
61 | | -#mwe-upwiz-add-file, .mwe-upwiz-remove-ctrl, .mwe-upwiz-more-options { |
62 | | - outline: none; |
63 | | - cursor: pointer; |
64 | | -} |
65 | | - |
66 | | -/* CSS styling hack for file inputs - http://www.quirksmode.org/dom/inputfile.html */ |
67 | | -.mwe-upwiz-file-ctrl-container { |
68 | | - position: absolute; |
69 | | - overflow: hidden; |
70 | | -} |
71 | | - |
72 | | -.mwe-upwiz-file-input, .mwe-upwiz-visible-file { |
73 | | - cursor: pointer; |
74 | | -} |
75 | | - |
76 | | -/* file inputs are freakishly large to overflow the containing div -- we get a div |
77 | | - that can act as a more styleable single-click file input */ |
78 | | -.mwe-upwiz-file-input, .disabler { |
79 | | - font-size: 100px; |
80 | | - -moz-opacity: 0.3; |
81 | | - filter:alpha(opacity: 0); |
82 | | - opacity: 0; |
83 | | - z-index: 2; |
84 | | -} |
85 | | - |
86 | | -.mwe-upwiz-file.filled { |
87 | | - position: relative; |
88 | | -} |
89 | | - |
90 | | -.mwe-upwiz-file.odd .mwe-upwiz-visible-file { |
91 | | - background: #f5f5f5; |
92 | | -} |
93 | | - |
94 | | - |
95 | | - |
96 | | -.mwe-upwiz-remove-ctrl { |
97 | | -} |
98 | | - |
99 | | -/* XXX this is highly specific to our installation |
100 | | -.mwe-upwiz-remove-ctrl.hover { |
101 | | -.ui-icon { background-image: url(/w/extensions/UsabilityInitiative/css/vector/images/ui-icons_cd0a0a_256x240.png) } |
102 | | -} |
103 | | -*/ |
104 | | - |
105 | | -.mwe-upwiz-visible-file .mwe-upwiz-remove-ctrl { |
106 | | - float: right; |
107 | | - visibility: hidden; |
108 | | - margin: 0.25em; |
109 | | - padding: 0.25em; |
110 | | -} |
111 | | - |
112 | | -.mwe-upwiz-file-indicator, .mwe-upwiz-count { |
113 | | - float: right; |
114 | | - margin-left: 0.5em; |
115 | | - padding: 0.5em 0.5em 0.5em 20px; /* 20px for the icon */ |
116 | | -} |
117 | | - |
118 | | -.mwe-upwiz-visible-file .mwe-upwiz-file-indicator { |
119 | | - visibility: hidden; |
120 | | -} |
121 | | - |
122 | | -.mwe-upwiz-visible-file { |
123 | | - display: none; |
124 | | -} |
125 | | - |
126 | | -.mwe-upwiz-file.filled .mwe-upwiz-visible-file { |
127 | | - display: block; |
128 | | -} |
129 | | - |
130 | | -.mwe-upwiz-visible-file-filename { |
131 | | - padding: 0.5em; |
132 | | - margin-right: 40px; |
133 | | - overflow: hidden; |
134 | | -} |
135 | | - |
136 | | -.mwe-upwiz-progress-bar-etr-container { |
137 | | - /* needed ? */ |
138 | | -} |
139 | | - |
140 | | - |
141 | | -.mwe-upwiz-add-files-n { |
142 | | - float: left; |
143 | | - margin-top: 5px; |
144 | | - margin-left: 4px; |
145 | | -} |
146 | | - |
147 | | -#mwe-upwiz-add-file-container.mwe-upwiz-add-files-n, .mwe-upwiz-progress-bar-etr { |
148 | | - width: 300px; |
149 | | - padding-left: 5px; |
150 | | -} |
151 | | - |
152 | | - |
153 | | -#mwe-upwiz-add-file-container.mwe-upwiz-add-files-n { |
154 | | - float: left; |
155 | | -} |
156 | | - |
157 | | -.mwe-upwiz-visible-file { |
158 | | - width: 100%; /* of containing mwe-upwiz-file */ |
159 | | - white-space: nowrap; |
160 | | - overflow: hidden; |
161 | | -} |
162 | | - |
163 | | -.mwe-upwiz-file.hover .mwe-upwiz-visible-file { |
164 | | - background: #e0f0ff !important; |
165 | | -} |
166 | | - |
167 | | -.mwe-upwiz-file.hover .mwe-upwiz-remove-ctrl { |
168 | | - visibility: visible; |
169 | | -} |
170 | | - |
171 | | -/* XXX we probably have a standard for this */ |
172 | | -.helper { |
173 | | - color: #cccccc; /* or whatever we do for greyed out text */ |
174 | | - font-variant: italic, oblique; |
175 | | - text-align: center; |
176 | | -} |
177 | | - |
178 | | - |
179 | | -#mwe-upwiz-files { |
180 | | - margin-right: 8em; |
181 | | - margin-left: 8em; |
182 | | -} |
183 | | - |
184 | | -.mwe-upwiz-file { |
185 | | -} |
186 | | - |
187 | | -#mwe-upwiz-upload-ctrls { |
188 | | - margin-top: 1em; |
189 | | -} |
190 | | - |
191 | | -#mwe-upwiz-add-file-container { |
192 | | - /* empty; this changes a lot */ |
193 | | -} |
194 | | - |
195 | | -.mwe-upwiz-details-descriptions .mwe-upwiz-remove-ctrl { |
196 | | - vertical-align: top; |
197 | | - display: inline-block; |
198 | | -} |
199 | | - |
200 | | -a[disabled=true] { |
201 | | - color: #999999; |
202 | | - text-decoration: none; |
203 | | - cursor: default; |
204 | | -} |
205 | | - |
206 | | -a[disabled=true]:hover { |
207 | | - text-decoration: none; |
208 | | -} |
209 | | - |
210 | | -.mwe-upwiz-status-progress { |
211 | | - background: url(spinner-orange.gif) no-repeat left center; |
212 | | - font-weight: bold; |
213 | | - color: #ff9900; |
214 | | -} |
215 | | - |
216 | | -.mwe-upwiz-status-completed { |
217 | | - background: url(checkmark.gif) no-repeat left center; |
218 | | - font-weight: bold; |
219 | | - color: #009900; |
220 | | -} |
221 | | - |
222 | | -.mwe-upwiz-progress { |
223 | | - margin-top: 15px; |
224 | | -} |
225 | | - |
226 | | -.mwe-upwiz-progress-bar-etr { |
227 | | - float: left; |
228 | | -} |
229 | | - |
230 | | -.mwe-upwiz-etr { |
231 | | - text-align: center; |
232 | | -} |
233 | | - |
234 | | - |
235 | | -.mwe-upwiz-upload-warning { |
236 | | - background: #ffffe0; |
237 | | -} |
238 | | - |
239 | | -.mwe-upwiz-details-error { |
240 | | - display: none; |
241 | | - background: #ffffe0; |
242 | | -} |
243 | | - |
244 | | -.mwe-upwiz-thumbnail, .mwe-upwiz-thumbnail-small { |
245 | | - border: 1px solid #cccccc; |
246 | | - text-align: center; |
247 | | - background: #ffffff; |
248 | | -} |
249 | | - |
250 | | -.mwe-upwiz-thumbnail { |
251 | | - padding: 0.5em; |
252 | | - width: 120px; |
253 | | -} |
254 | | - |
255 | | -.mwe-upwiz-thumbnail-side { |
256 | | - float: left; |
257 | | - margin-bottom: 1em; |
258 | | - margin-right: 1em; |
259 | | -} |
260 | | - |
261 | | -.mwe-upwiz-thumbnail-small { |
262 | | - padding: 0.25em; |
263 | | - width: 60px; |
264 | | -} |
265 | | - |
266 | | -#mwe-upwiz-deeds-thumbnails { |
267 | | - text-align: center; |
268 | | - margin: 1em 0; |
269 | | - background: #f0f0f0; |
270 | | -} |
271 | | - |
272 | | -#mwe-upwiz-deeds-thumbnails .mwe-upwiz-thumbnail-small { |
273 | | - display: inline-block; |
274 | | - margin: 1em; |
275 | | - vertical-align: middle; |
276 | | -} |
277 | | - |
278 | | -/* I don't like that this has to have width, to ensure that all the floats work out correctly.*/ |
279 | | -.mwe-upwiz-data { |
280 | | - float: left; |
281 | | - width: 46em; |
282 | | -} |
283 | | - |
284 | | - |
285 | | -/* XXX add spinner gif instead, maybe */ |
286 | | -.busy { |
287 | | - /* background: yellow; */ |
288 | | -} |
289 | | - |
290 | | -.mwe-upwiz-stepdiv { |
291 | | - height: 0px; |
292 | | - overflow: hidden; |
293 | | -} |
294 | | - |
295 | | - |
296 | | - |
297 | | -.shim { |
298 | | - float:right; |
299 | | - width: 1px; |
300 | | -} |
301 | | - |
302 | | -.clearShim { |
303 | | - clear: both; |
304 | | - height: 1px; |
305 | | - overflow: hidden; |
306 | | -} |
307 | | - |
308 | | -.mwe-checkbox-hang-indent { |
309 | | - float: left; |
310 | | - width: 24px; |
311 | | - margin-top: 6px; |
312 | | -} |
313 | | - |
314 | | -.mwe-checkbox-hang-indent-text { |
315 | | - margin-left: 24px; |
316 | | -} |
317 | | - |
318 | | -.mwe-small-print { |
319 | | - font-size: x-small; |
320 | | -} |
321 | | - |
322 | | -.mwe-upwiz-deed { |
323 | | - margin-left: 24px; |
324 | | -} |
325 | | - |
326 | | -.mwe-upwiz-deed.selected .mwe-upwiz-deed-name { |
327 | | - font-weight: bold; |
328 | | -} |
329 | | - |
330 | | -.mwe-more-options, .mwe-upwiz-macro-deeds-return, .mwe-upwiz-deed-header-link { |
331 | | - cursor: pointer; |
332 | | -} |
333 | | - |
334 | | -.mwe-more-options { |
335 | | - padding-bottom: 4px; |
336 | | -} |
337 | | - |
338 | | -.mwe-upwiz-deed-license { |
339 | | - margin-left: 24px; |
340 | | -} |
341 | | - |
342 | | -#mwe-upwiz-macro-deeds { |
343 | | - margin-top: 12px; |
344 | | - margin-bottom: 24px; |
345 | | -} |
346 | | - |
347 | | -#mwe-upwiz-macro-files { |
348 | | - margin-top: 12px; |
349 | | -} |
350 | | - |
351 | | -.mwe-upwiz-info-file { |
352 | | - margin-bottom: 1em; |
353 | | -} |
354 | | - |
355 | | -.mwe-upwiz-details-input { |
356 | | - width: 33em; |
357 | | - float: left; |
358 | | -} |
359 | | - |
360 | | -.mwe-upwiz-details-fieldname { |
361 | | - width: 10em; |
362 | | - padding-top: 0.5em; |
363 | | - float: left; |
364 | | -} |
365 | | - |
366 | | -.mwe-upwiz-details-input-error { |
367 | | - padding-left: 10em; |
368 | | - /* XXX the following rules pop it open at a fixed width so layout doesn't change :( */ |
369 | | - /* min-height: 3em; |
370 | | - position: relative; */ |
371 | | -} |
372 | | - |
373 | | -/* XXX the following rule keeps errors on the details page at the bottom of a fixed-height space, see above */ |
374 | | -/* |
375 | | -.mwe-upwiz-details-input-error label.mwe-validator-error, |
376 | | -.mwe-upwiz-details-input-error label.mwe-error { |
377 | | - position: absolute; |
378 | | - bottom: 0px; |
379 | | -} |
380 | | -*/ |
381 | | - |
382 | | - |
383 | | -.mwe-upwiz-desc-lang-select { |
384 | | - width: 11em; |
385 | | - font-family: sans-serif; |
386 | | - font-size: small; |
387 | | -} |
388 | | - |
389 | | -.mwe-upwiz-desc-lang-text { |
390 | | - width: 20em; |
391 | | - overflow: hidden; |
392 | | - font-family: sans-serif; /* XXX is this right? */ |
393 | | - font-size: small; |
394 | | -} |
395 | | - |
396 | | -.mwe-upwiz-details-descriptions-add { |
397 | | - margin-left: 10em; /* width of mwe-upwiz-details-fieldname */ |
398 | | -} |
399 | | - |
400 | | -.mwe-upwiz-details-descriptions-add { |
401 | | - margin-bottom: 1em; |
402 | | -} |
403 | | - |
404 | | -.mwe-grow-textarea, .mwe-long-textarea { |
405 | | - overflow: hidden; |
406 | | - font-family: sans-serif; /* XXX is this right? */ |
407 | | - font-size: small; |
408 | | -} |
409 | | - |
410 | | -.mwe-long-textarea { |
411 | | - width: 31em; |
412 | | -} |
413 | | - |
414 | | -fieldset .mwe-long-textarea { |
415 | | - width: 17em; |
416 | | -} |
417 | | - |
418 | | - |
419 | | -.mwe-upwiz-details-fieldname-input { |
420 | | - margin-bottom: 1em; |
421 | | -} |
422 | | - |
423 | | -.mwe-upwiz-details-filename. { |
424 | | - overflow: hidden; |
425 | | - width: 350px; |
426 | | -} |
427 | | - |
428 | | -.mwe-upwiz-other-textarea { |
429 | | - /* width: 40.3em; */ |
430 | | -} |
431 | | - |
432 | | -fieldset.mwe-fieldset { |
433 | | - width: 450px; |
434 | | - border: 1px solid #cccccc; |
435 | | - padding-left: 12px; |
436 | | - padding-right: 12px; |
437 | | -} |
438 | | - |
439 | | -legend.mwe-legend { |
440 | | - padding: 0.5em 0.5em 0.5em 0.33em; |
441 | | - color: #666666; |
442 | | -} |
443 | | - |
444 | | -.masked-hidden { |
445 | | - visibility: hidden !important; |
446 | | -} |
447 | | - |
448 | | -.mwe-upwiz-thirdparty-fields { |
449 | | - margin-bottom: .5em; |
450 | | -} |
451 | | - |
452 | | -.mwe-upwiz-thirdparty-fields label { |
453 | | - width: 9em; |
454 | | - display: inline-block; |
455 | | - padding-bottom: .5em; |
456 | | -} |
457 | | - |
458 | | -.mwe-upwiz-thirdparty-fields textarea { |
459 | | - margin: 0 |
460 | | -} |
461 | | - |
462 | | -.mwe-upwiz-thirdparty-license { |
463 | | - margin-top: 8px; |
464 | | -} |
465 | | - |
466 | | -.mwe-upwiz-deed-form-internal { |
467 | | - padding: 0.5em 0 1.5em 3em; |
468 | | -} |
469 | | - |
470 | | - |
471 | | -.mwe-upwiz-copyright-info label { |
472 | | - display: inline-block; |
473 | | - padding-left: 15px; |
474 | | - text-indent: -15px; |
475 | | - margin-right: 15px; |
476 | | -} |
477 | | - |
478 | | -/* the hanging indent checkboxes */ |
479 | | -.mwe-upwiz-copyright-info input.mwe-accept-deed { |
480 | | - width: 13px; |
481 | | - height: 13px; |
482 | | - padding: 0; |
483 | | - margin: 0px 3px 0px 0px; |
484 | | - vertical-align: bottom; |
485 | | - position: relative; |
486 | | - top: -1px; |
487 | | -} |
488 | | - |
489 | | - |
490 | | -.mwe-upwiz-custom-deed { |
491 | | - margin-top: 5px; |
492 | | -} |
493 | | - |
494 | | -.mwe-upwiz-buttons { |
495 | | - margin-top: 1em; |
496 | | - padding-top: 10px; |
497 | | - border-top: 1px solid #e0e0e0; |
498 | | - text-align: right; /* works for now, only one 'next' button */ |
499 | | -} |
500 | | - |
501 | | -a.mwe-upwiz-tooltip-link { |
502 | | - cursor: pointer; |
503 | | -} |
504 | | - |
505 | | -.mwe-error, .mwe-validator-error { |
506 | | - color: #ff0000; |
507 | | -} |
508 | | - |
509 | | -.mwe-upwiz-deed-thirdparty .mwe-upwiz-deed-form-internal label.mwe-error, |
510 | | -.mwe-upwiz-deed-thirdparty .mwe-upwiz-deed-form-internal label.mwe-validator-error { |
511 | | - margin-left: 9em; |
512 | | -} |
513 | | - |
514 | | -/* XXX fix this later -- we get non-rounded corners somehow */ |
515 | | -input[type='text'].mwe-error, textarea.mwe-error, |
516 | | -input[type='text'].mwe-validator-error, textarea.mwe-validator-error { |
517 | | - color: black; |
518 | | - border-color: #ff0000; |
519 | | -} |
520 | | - |
521 | | -.mwe-upwiz-toggler { |
522 | | - margin-bottom: 0; |
523 | | - padding: 4px 0 3px 18px; |
524 | | - background: url('toggle.png') no-repeat left center; |
525 | | -} |
526 | | - |
527 | | -.mwe-upwiz-toggler-open { |
528 | | - background: url('toggle-open.png') no-repeat left center; |
529 | | -} |
530 | | - |
531 | | -.mwe-upwiz-toggled { |
532 | | - margin-top: 1em; |
533 | | -} |
534 | | - |
535 | | -.mwe-upwiz-thanks { |
536 | | - margin-bottom: 2em; |
537 | | -} |
538 | | - |
539 | | -/* may change in RTL */ |
540 | | -.mwe-upwiz-required-field { |
541 | | - /* font-weight: bold; */ |
542 | | -} |
543 | | - |
544 | | -.mwe-upwiz-required-marker { |
545 | | - color: #0099cc; |
546 | | -} |
547 | | - |
548 | | -.mwe-readonly { |
549 | | - background-color: #ffffff; |
550 | | -} |
551 | | - |
552 | | -.mwe-date-display { |
553 | | - width: 100%; |
554 | | - background: #ffffff url('calendar.gif') no-repeat right center; |
555 | | -} |
556 | | - |
557 | | -.ui-datepicker-current-day a.ui-state-active { |
558 | | - background: #ffff99; |
559 | | -} |