r63405 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r63404‎ | r63405 | r63406 >
Date:18:35, 8 March 2010
Author:neilk
Status:deferred
Tags:
Comment:
Initial commit of UploadWizard, multiple-file media uploader.
Alpha quality.
http://usability.wikimedia.org/wiki/Multimedia:NewUpload
Modified paths:
  • /branches/js2-work/phase3/js/mwEmbed/loader.js (modified) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/README (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/css (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/css/uploadWizard.css (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/loader.js (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.ApiUploadHandler.js (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.IframeTransport.js (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.MockUploadHandler.js (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.UploadApiProcessor.js (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.UploadWizard.js (added) (history)
  • /branches/js2-work/phase3/js/mwEmbed/remotes/mediaWiki.js (modified) (history)
  • /branches/js2-work/phase3/js/uploadWizard.js (deleted) (history)
  • /branches/js2-work/phase3/js/uploadWizardPage.js (added) (history)

Diff [purge]

Index: branches/js2-work/phase3/js/uploadWizard.js
@@ -1 +0,0 @@
2 -console.log('uploadwizard loaded');
Index: branches/js2-work/phase3/js/mwEmbed/loader.js
@@ -32,7 +32,8 @@
3333 'EmbedPlayer',
3434 'ApiProxy',
3535 'Sequencer',
36 - 'TimedText'
 36+ 'TimedText',
 37+ 'UploadWizard'
3738 ];
3839
3940 /**
@@ -162,4 +163,4 @@
163164 "$j.ui.draggable" : "jquery/jquery.ui/ui/ui.draggable.js",
164165 "$j.ui.selectable" : "jquery/jquery.ui/ui/ui.selectable.js"
165166
166 -} );
\ No newline at end of file
 167+} );
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/loader.js
@@ -0,0 +1,87 @@
 2+/*
 3+* Loader for UploadWizard module:
 4+*/
 5+
 6+// Scope everythign in "mw" ( keeps the global namespace clean )
 7+( function( mw ) {
 8+
 9+ mw.addMessages( {
 10+ "mwe-loading-upwiz" : "Loading upload wizard"
 11+ });
 12+
 13+ // Add class file paths ( From ROOT )
 14+ mw.addClassFilePaths( {
 15+
 16+ // "mw.UploadHandler" : "modules/UploadWizard/mw.UploadHandler.js",
 17+ "mw.UploadWizard" : "modules/UploadWizard/mw.UploadWizard.js",
 18+ "mw.UploadApiProcessor" : "modules/UploadWizard/mw.UploadApiProcessor.js",
 19+ "mw.IframeTransport" : "modules/UploadWizard/mw.IframeTransport.js",
 20+ "mw.ApiUploadHandler" : "modules/UploadWizard/mw.ApiUploadHandler.js",
 21+
 22+ "mw.MockUploadHandler" : "modules/UploadWizard/mw.MockUploadHandler.js"
 23+
 24+ });
 25+
 26+ mw.addClassStyleSheets( {
 27+ 'mw.UploadWizard' : 'modules/UploadWizard/css/uploadWizard.css'
 28+ } );
 29+
 30+ //Set a variable for the base upload interface for easy inclution
 31+ var baseUploadlibs = [
 32+ [
 33+ 'mw.UploadHandler',
 34+ 'mw.UploadInterface',
 35+ '$j.ui'
 36+ ],
 37+ [
 38+ '$j.ui.progressbar',
 39+ '$j.ui.dialog',
 40+ '$j.ui.draggable'
 41+ ]
 42+ ];
 43+
 44+ var mwBaseFirefoggReq = baseUploadlibs.slice( 0 )
 45+ mwBaseFirefoggReq[0].push('mw.Firefogg');
 46+
 47+
 48+ /**
 49+ * Note: We should move relevant parts of these style sheets to the addMedia/css folder
 50+ * phase 2: We should separate out sheet sets per sub-module:
 51+ */
 52+
 53+ mw.addModuleLoader( 'UploadWizard.UploadWizard', function( callback ) {
 54+ //Clone the array:
 55+ var request = mwBaseFirefoggReq.slice( 0 ) ;
 56+
 57+ //Add uploadwizard classes to a new "request" var:
 58+ request.push( [
 59+ 'mw.IframeTransport',
 60+ 'mw.ApiUploadHandler',
 61+ 'mw.UploadWizard'
 62+ ] );
 63+
 64+ mw.load( request, function() {
 65+ callback( 'UploadWizard.UploadWizard' );
 66+ });
 67+
 68+ } );
 69+
 70+ mw.addModuleLoader( 'UploadWizard.UploadWizardTest', function( callback ) {
 71+ //Clone the array:
 72+ var request = mwBaseFirefoggReq.slice( 0 ) ;
 73+
 74+ //Add uploadwizard classes to a new "request" var:
 75+ request.push( [
 76+ 'mw.IframeTransport',
 77+ 'mw.ApiUploadHandler',
 78+ 'mw.MockUploadHandler',
 79+ 'mw.UploadWizard'
 80+ ] );
 81+
 82+ mw.load( request, function() {
 83+ callback( 'UploadWizard.UploadWizardTest' );
 84+ });
 85+
 86+ } );
 87+
 88+} )( window.mw );
Property changes on: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/loader.js
___________________________________________________________________
Name: svn:eol-style
189 + native
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.UploadWizard.js
@@ -0,0 +1,943 @@
 2+mw.addMessages({
 3+ 'mwe-upwiz-tab-file': 'Step 1',
 4+ 'mwe-upwiz-tab-metadata': 'Step 2',
 5+ 'mwe-upwiz-tab-thanks': 'Step 3',
 6+ 'mwe-upwiz-intro': 'Introductory text (short)',
 7+ 'mwe-upwiz-select-files': 'Select files:',
 8+ 'mwe-upwiz-add-file-n': 'Add another file',
 9+ 'mwe-upwiz-add-file-0': 'Add a file',
 10+ 'mwe-upwiz-browse': 'Browse...',
 11+ 'mwe-upwiz-completed': 'OK',
 12+ 'mwe-upwiz-uploading': 'uploading...',
 13+ 'mwe-upwiz-remove-upload': 'Remove this upload',
 14+ 'mwe-upwiz-upload': 'Upload',
 15+ 'mwe-upwiz-upload-count': '$1 of $2 files uploaded',
 16+ 'mwe-upwiz-progressbar-uploading': 'uploading',
 17+ 'mwe-upwiz-remaining': '$1 remaining',
 18+ 'mwe-upwiz-intro-metadata': 'Thank you for uploading your works! Now we need some basic information in order to complete your upload.',
 19+ 'mwe-upwiz-provenance-ownwork': 'They are entirely your own work.',
 20+ 'mwe-upwiz-provenance-ownwork-assert': 'I, $1, the copyright holder of this work, hereby grant anyone the right to use these works for any purpose, as long as they credit me and share derivative work under the same terms.',
 21+ 'mwe-upwiz-provenance-ownwork-assert-note': 'This means you release your work under a double Creative Commons Attribution ShareAlike and GFDL license.',
 22+ 'mwe-upwiz-provenance-permission': 'Their author gave you explicit permission to upload them',
 23+ 'mwe-upwiz-provenance-website': 'They come from a website',
 24+ 'mwe-upwiz-provenance-custom': 'Did you know? You can <a href="$1">customize</a> the default options you see here.',
 25+ 'mwe-upwiz-more-options': 'more options...',
 26+ 'mwe-upwiz-desc-lang': 'Description in',
 27+ 'mwe-upwiz-desc-lang-another': 'add a description in another language',
 28+ 'mwe-upwiz-title': 'Title',
 29+ 'mwe-upwiz-categories-intro': 'Help people find your works by adding categories',
 30+ 'mwe-upwiz-categories-another': 'Add other categories',
 31+ 'mwe-upwiz-previously-uploaded': 'This file was previously uploaded to $1 and is already available <a href="$2">here</a>.',
 32+ 'mwe-upwiz-about-this-work': 'About this work',
 33+ 'mwe-upwiz-media-type': 'Media type',
 34+ 'mwe-upwiz-date-created': 'Date created',
 35+ 'mwe-upwiz-geotag': 'Location',
 36+ 'mwe-upwiz-copyright-info': 'Copyright information',
 37+ 'mwe-upwiz-author': 'Author',
 38+ 'mwe-upwiz-license': 'License',
 39+ 'mwe-upwiz-about-format': 'About the file',
 40+ 'mwe-upwiz-autoconverted': 'This file was automatically converted to the $1 format',
 41+ 'mwe-upwiz-filename-tag': 'File name:',
 42+ 'mwe-upwiz-other': 'Other information',
 43+ 'mwe-upwiz-other-prefill': 'Free wikitext field',
 44+ 'mwe-upwiz-showall': 'show all',
 45+ 'mwe-upwiz-desc-lang': 'Description in', // caution: FRAGMENT -- bad for i18n
 46+
 47+ 'mwe-upwiz-upload-error-bad-filename-extension': 'This wiki does not accept filenames with the extension "$1".',
 48+ 'mwe-upwiz-upload-error-duplicate': 'This file was previously uploaded to this wiki.',
 49+ 'mwe-upwiz-upload-error-stashed-anyway': 'Post anyway?',
 50+ 'mwe-upwiz-ok': 'OK',
 51+ 'mwe-upwiz-cancel': 'Cancel',
 52+
 53+
 54+ // available licenses should be a configuration of the MediaWiki instance,
 55+ // not hardcoded here.
 56+ // but, MediaWiki has no real concept of a License as a first class object -- there are templates and then specially-parsed
 57+ // texts to create menus -- hack on top of hacks -- a bit too much to deal with ATM
 58+ 'mwe-lic-pd': 'Public domain',
 59+ 'mwe-lic-cc-0': 'Creative Commons Zero waiver',
 60+ 'mwe-lic-cc-by-3.0': 'Creative Commons Attribution 3.0',
 61+ 'mwe-lic-cc-by-sa-3.0': 'Creative Commons Attribution ShareAlike 3.0',
 62+ 'mwe-lic-gfdl': 'GFDL'
 63+});
 64+
 65+
 66+
 67+// this interface only works for the wizard... should say so in class name
 68+mw.UploadWizardUploadInterface = function(filenameAcceptedCb) {
 69+ var _this = this;
 70+
 71+ _this.filenameAcceptedCb = filenameAcceptedCb;
 72+
 73+ // may need to collaborate with the particular upload type sometimes
 74+ // for the interface, as well as the uploadwizard. OY.
 75+ _this.div = $j('<div></div>').get(0);
 76+
 77+ _this.fileInputCtrl = $j('<input size=40 class="mwe-upwiz-file" name="file" type="file"/>').get(0);
 78+
 79+ // XXX not sure if we will have a filename here -- we may want to autogenerate a "stashed" filename,
 80+ // with this flow
 81+ _this.filenameCtrl = $j('<input type="hidden" name="filename" value=""/>').get(0);
 82+
 83+ _this.form = $j('<form class="mwe-upwiz-form"></form>').append(_this.fileInputCtrl).append(_this.filenameCtrl).get(0);
 84+
 85+ _this.progressMessage = $j('<span class="mwe-upwiz-status-message" style="display: none"></span>').get(0);
 86+
 87+ $j(_this.fileInputCtrl).change( function() { _this.fileChanged() } );
 88+
 89+ _this.errorDiv = $j('<div class="mwe-upwiz-upload-error" style="display: none;"></div>').get(0);
 90+
 91+
 92+ $j(_this.div).append(_this.form)
 93+ .append(_this.removeCtrl)
 94+ .append(_this.progressMessage)
 95+ .append(_this.errorDiv);
 96+
 97+ // _this.progressBar = (no progress bar for individual uploads yet)
 98+ // add a metadata thing to metadata
 99+};
 100+
 101+mw.UploadWizardUploadInterface.prototype = {
 102+ /* start! */
 103+ start: function() {
 104+ var _this = this;
 105+ $j(_this.removeCtrl).hide();
 106+ },
 107+
 108+ /* generically busy, but not a fraction. Encoding, or transports that don't know progress */
 109+ busy: function() {
 110+ var _this = this;
 111+ // for now we implement this as looking like "100% progress"
 112+ // e.g. an animated bar that takes up all the space
 113+ _this.progress(1.0);
 114+ },
 115+
 116+ /* show progress with a certain fraction */
 117+ progress: function(fraction) {
 118+ var _this = this;
 119+ $j(_this.progressMessage).addClass('mwe-upwiz-status-progress')
 120+ .html(gM( 'mwe-upwiz-uploading' ))
 121+ .show();
 122+ // update individual progress bar with fraction?
 123+ },
 124+
 125+ // this is just completed in the sense that it's all uploaded. There may be other errors?
 126+ // really need to rethink the UI / metadata separation
 127+ completed: function(result) {
 128+ var _this = this;
 129+ $j(_this.progressMessage).removeClass('mwe-upwiz-status-progress')
 130+ .addClass('mwe-upwiz-status-completed')
 131+ .html(gM( 'mwe-upwiz-completed' ));
 132+ },
 133+
 134+ fileChanged: function() {
 135+ var _this = this;
 136+ _this.clearErrors();
 137+ var ext = _this.getExtension();
 138+ if (_this.isGoodExtension(ext)) {
 139+ _this.updateFilename();
 140+ } else {
 141+ _this.error('bad-filename-extension', ext);
 142+ }
 143+ },
 144+
 145+ updateFilename: function() {
 146+ var _this = this;
 147+ var path = $j(_this.fileInputCtrl).attr('value');
 148+ var filename = _this.convertPathToFilename(path);
 149+ // XXX store the "desired" filename for later, when we rename the file
 150+ // this is a hack to get a temporary file guaranteed unique -- will change perhaps, later
 151+ filename = mw.getConfig('userName') + "-" + (new Date()).getTime() + "-" + filename;
 152+ $j(_this.filenameCtrl).attr('value', filename);
 153+ _this.filenameAcceptedCb();
 154+ },
 155+
 156+ clearErrors: function() {
 157+ var _this = this;
 158+ // XXX this should be changed to something Theme compatible
 159+ $j(_this.div).removeClass('mwe-upwiz-upload-error');
 160+ $j(_this.errorDiv).hide().empty();
 161+ },
 162+
 163+ error: function() {
 164+ var _this = this;
 165+ var args = Array.prototype.slice.call(arguments); // copies arguments into a real array
 166+ var msg = 'mwe-upwiz-upload-error-' + args[0];
 167+ $j(_this.errorDiv).append($j('<p class="mwe-upwiz-upload-error">' + gM(msg, args.slice(1)) + '</p>'));
 168+ // apply a error style to entire did
 169+ $j(_this.div).addClass('mwe-upwiz-upload-error');
 170+ $j(_this.errorDiv).show();
 171+ },
 172+
 173+ // arguably should be about filename, not path?
 174+ // may check for common bad patterns here, like DSC_NNNNN, filenames too short, too long, etc.
 175+
 176+ getExtension: function() {
 177+ var _this = this;
 178+ var path = $j(_this.fileInputCtrl).attr('value');
 179+ return path.substr( path.lastIndexOf( '.' ) + 1 ).toLowerCase();
 180+ },
 181+
 182+ // XXX this is common utility code
 183+ // used when converting contents of a file input and coming up with a suitable "filename" for mediawiki
 184+ // test: what if path is length 0
 185+ // what if path is all separators
 186+ // what if path ends with a separator character
 187+ // what if it ends with multiple separator characters
 188+ convertPathToFilename: function(path) {
 189+ if (path === undefined || path == '') {
 190+ return '';
 191+ }
 192+
 193+ var lastFileSeparatorIdx = Math.max(path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ));
 194+ // lastFileSeparatorIdx is now -1 if no separator found, or some index in the string.
 195+ // so, +1, that is either 0 (beginning of string) or the character after last separator.
 196+ // caution! could go past end of string... need to be more careful
 197+ var filename = path.substring( lastFileSeparatorIdx + 1, 10000 );
 198+
 199+ // Capitalise first letter and replace spaces by underscores
 200+ filename = filename.charAt( 0 ).toUpperCase() + filename.substring( 1, 10000 );
 201+ filename.replace(/ /g, '_' );
 202+ return filename;
 203+ },
 204+
 205+ // XXX this is common utility code
 206+ // XXX unused, copied because we'll probably need it... stripped from old doDestinationFill
 207+ // this is used when checking for "bad" extensions in a filename.
 208+ isGoodExtension: function(ext) {
 209+ var _this = this;
 210+ var found = false;
 211+ var extensions = mw.getConfig('fileExtensions');
 212+ if (extensions) {
 213+ for ( var i = 0; i < extensions.length; i++ ) {
 214+ if ( extensions[i].toLowerCase() == ext ) {
 215+ found = true;
 216+ }
 217+ }
 218+ }
 219+ return found;
 220+ }
 221+
 222+};
 223+
 224+
 225+mw.UploadWizard = function() {
 226+
 227+ // this detect upload mode checks the API. Slow!
 228+ // works for the add media wizard since it is talking to a remote server
 229+ // we need to make it configure itself based on local globals if local server, remote only if remote.
 230+ // UploadHandler.detectUploadMode();
 231+
 232+ // XXX does not work yet
 233+
 234+ this.uploadHandlerClass = mw.getConfig('uploadHandlerClass') || this.getUploadHandlerClass();
 235+ this.isCompleted = false;
 236+
 237+ this.uploads = [];
 238+ // leading underline for privacy. DO NOT TAMPER.
 239+ this._uploadsQueued = [];
 240+ this._uploadsInProgress = [];
 241+ this._uploadsCompleted = [];
 242+
 243+ this.uploadsBeginTime = null;
 244+
 245+ return this;
 246+};
 247+
 248+mw.UploadWizardDescription = function(languageCode) {
 249+ var _this = this;
 250+
 251+ // XXX obtain list of real languages from config
 252+ var languageMenu = $j('<select name="lang" class="mwe-upwiz-desc-lang"></select>');
 253+
 254+ // this could be cached, and cloned as necessary...?
 255+ var languages = mw.getConfig('languages');
 256+ for (var i = 0; i < languages.length; i++) {
 257+ var language = languages[i];
 258+ var selected = "";
 259+ if (language.code == languageCode) {
 260+ selected = " selected";
 261+ }
 262+ var option = $j('<option value="' + language.code + selected + ">" + language.name + '</option>');
 263+ languageMenu.append(option);
 264+ }
 265+
 266+
 267+ _this.languageMenu = languageMenu.get(0);
 268+ _this.description = $j('<input name="desc" class="mwe-upwiz-desc-lang-text" type="text" size="40"/>').get(0);
 269+
 270+ _this.removeCtrl = $j('<a class="mwe-upwiz-desc-lang-remove" href="#">x</a>').get(0);
 271+ _this.removeCtrl.click = function () { _this.remove() };
 272+
 273+ _this.div = $j('<div class="mwe-upwiz-desc-lang"></div>')
 274+ .append(_this.languageMenu)
 275+ .append(_this.description)
 276+ .append(_this.removeCtrl);
 277+
 278+};
 279+
 280+mw.UploadWizardDescription.prototype = {
 281+ remove: function() {
 282+ // XXX todo
 283+ },
 284+
 285+ getWikiText: function() {
 286+ return '{{' + _this.languageMenu.value() + '|' + _this.description.value() + '}}'
 287+ }
 288+};
 289+
 290+mw.UploadWizardMetadata = function(containerDiv) {
 291+
 292+ var _this = this;
 293+ _this.descriptions = [];
 294+
 295+ _this.div = $j('<div class="mwe-upwiz-metadata-file"></div>');
 296+
 297+ _this.thumbnail = $j('<img class="mwe-upwiz-thumbnail"/>');
 298+ var thumbnailDiv = $j('<div class="mwe-upwiz-thumbnail"></div>').append(_this.thumbnail).get(0);
 299+
 300+ _this.errorDiv = $j('<div class="mwe-upwiz-metadata-error"></div>');
 301+
 302+ _this.dataDiv = $j('<div class="mwe-upwiz-metadata-data"></div>');
 303+
 304+ _this.descriptionsDiv = $j('<div class="mwe-upwiz-metadata-descriptions"></div>');
 305+
 306+ _this.descriptionsContainerDiv =
 307+ $j('<div class="mwe-upwiz-metadata-descriptions-container"></div>')
 308+ .append( $j('<div class="mwe-upwiz-metadata-descriptions-title">' + gM('mwe-upwiz-desc-lang') + '</div>') )
 309+ .append(_this.descriptionsDiv)
 310+ .append( $j('<div class="mwe-upwiz-metadata-descriptions-add"></div>')
 311+ .append( $j('<a href="#">' + gM('mwe-upwiz-desc-lang-another') + '</a>').click( function() { _this.addDescription() } ) )
 312+ );
 313+
 314+
 315+ $j(_this.div)
 316+ .append(_this.thumbnailDiv)
 317+ .append(_this.errorDiv)
 318+ .append($j(_this.dataDiv)
 319+ .append(_this.descriptionsContainerDiv));
 320+
 321+
 322+
 323+ // create the basic HTML
 324+ // thumbnail
 325+
 326+
 327+ // description in [ English ]
 328+ // description field
 329+ // title
 330+
 331+ // about this work
 332+ // media type
 333+ // date created
 334+ // location widget
 335+
 336+ // copyright info <--- THIS IS THE IMPORTANT BIT
 337+ // Author
 338+ // License
 339+
 340+ // About the file...
 341+
 342+ // Other info
 343+
 344+ _this.addDescription();
 345+ $j(containerDiv).append(_this.div);
 346+
 347+
 348+};
 349+
 350+mw.UploadWizardMetadata.prototype = {
 351+
 352+ addDescription: function(languageCode) {
 353+ var _this = this;
 354+ if (languageCode === undefined) {
 355+ languageCode = mw.getConfig('userLanguage');
 356+ } else {
 357+ // is languageCodeuage sane?
 358+ // if not, raise some kind of error
 359+ }
 360+
 361+ // we assume we always add new descriptions in the user's languageCodeuage
 362+ var description = new mw.UploadWizardDescription(languageCode);
 363+ $j(_this.descriptionsDiv).append(description.div);
 364+ _this.descriptions.push(description);
 365+ },
 366+
 367+ // this is a lot like upload ui's error -- should merge
 368+ error: function() {
 369+ var _this = this;
 370+ var args = Array.prototype.slice.call(arguments); // copies arguments into a real array
 371+ var msg = 'mwe-upwiz-upload-error-' + args[0];
 372+ $j(_this.errorDiv).append($j('<p class="mwe-upwiz-upload-error">' + gM(msg, args.slice(1)) + '</p>'));
 373+ // apply a error style to entire did
 374+ $j(_this.div).addClass('mwe-upwiz-upload-error');
 375+ $j(_this.dataDiv).hide();
 376+ $j(_this.errorDiv).show();
 377+ },
 378+
 379+ // just like error but with ok/cancel
 380+ errorDuplicate: function(sessionKey, duplicates) {
 381+ /*
 382+ TODO - do something clever to get page URLs and image URLs
 383+ var duplicatePageTitles = result.upload.warnings.duplicate;
 384+ var duplicates = [];
 385+ for (var i = 0; i < duplicates.length; i++) {
 386+ imageInfo = mw.getJSON(undefined,
 387+ {'titles' : duplicatePageTitles[i], 'prop' : 'imageinfo'})
 388+ function() { _this.renderUploads() });
 389+ duplicates.push({
 390+ // ?? async, so we should insert later...
 391+ })
 392+ }
 393+ */
 394+ _this.error('duplicate');
 395+ // add placeholder spinners to div, and then fetch the thumbnails and so on async
 396+ // meanwhile...
 397+ //$j(_this.errorDiv).append(
 398+ // $j('<form></form>');
 399+ // same as normal error but you get to ok/cancel, which resubmits with ignore warnings
 400+ },
 401+
 402+ // given the API result pull some info into the form (for instance, extracted from EXIF, desired filename)
 403+ populateFromResult: function(result) {
 404+ var upload = result.upload;
 405+ _this.setThumbnail(upload.imageinfo, mw.getConfig('thumbnailWidth'));
 406+ _this.setFilename(upload.filename);
 407+ _this.setDescription(); // is there anything worthwhile here? image comment?
 408+ _this.setDate(upload.metadata);
 409+ _this.setLocation(upload.metadata); // we could be VERY clever with location sensing...
 410+ //_this.setProvenance(result);
 411+ //_this.setAuthor(_this.config.user, upload.exif.Copyright);
 412+ },
 413+
 414+ // transform the url of the image into a suitable thumbnail url
 415+ // XXX THIS IS EVIL AND WRONG TO DO ON THE CLIENT SIDE
 416+ // the API should be returning a "thumbnail template" of sorts, as has been proposed before
 417+ // desiredWidth is in pixels
 418+ setThumbnail: function(imageInfo, desiredWidth) {
 419+
 420+ var url = imageInfo.url;
 421+
 422+ // may be off-by-ones if the image scaler on the server rounds differently
 423+ var desiredHeight = desiredWidth; // assume square
 424+ if (imageInfo.height && imageInfo.width) {
 425+ desiredHeight = parseInt(imageInfo.height * (desiredWidth / imageInfo.width));
 426+ }
 427+
 428+ if (url === undefined || url === '') {
 429+ // XXX what the hell?
 430+ }
 431+
 432+ // the image url looks like: http://host/path/to/images/filename.jpg
 433+ // a thumbnail url looks like: http://host/path/to/images/filename.jpg/120px-filename.jpg
 434+ // filename.jpg must be identical in both cases
 435+
 436+ var basename = url.exec(/\/([^\/]+)$/)[1];
 437+
 438+ if (basename == undefined || basename == '') {
 439+ // XXX what the hell?
 440+ }
 441+
 442+ var thumbnailUrl = url + '/' + desiredWidth + 'px-' + basename;
 443+
 444+ // this should already be true
 445+ _this.thumbnail.width = desiredWidth;
 446+
 447+ // but this might be new info
 448+ _this.thumbnail.height = desiredHeight;
 449+ _this.thumbnail.src = thumbnailUrl;
 450+
 451+ },
 452+
 453+ getWikiText: function() {
 454+ wikiText = '';
 455+
 456+
 457+ // http://commons.wikimedia.org/wiki/Template:Information
 458+
 459+ // can we be more slick and do this with maps, applys, joins?
 460+ var information = {
 461+ 'description' : '', // {{lang|description in lang}}* required
 462+ 'date' : '', // YYYY, YYYY-MM, or YYYY-MM-DD required - use jquery but allow editing, then double check for sane date.
 463+ 'source' : '', // {{own}} or wikitext optional
 464+ 'author' : '', // any wikitext, but particularly {{Creator:Name Surname}} required
 465+ 'permission' : '', // leave blank; by default will be "see below" optional
 466+ 'other_versions' : '', // pipe separated list, other versions optional
 467+ 'other_fields' : '' // ??? additional table fields
 468+ };
 469+
 470+ // sanity check the descriptions -- do not have two in the same lang
 471+ // all should be a known lang
 472+ for (var i = 0; i < _this.descriptions.length; i++) {
 473+ // XXX trim the descriptions here, remove leading or trailing whitespace
 474+ information['Description'] += _this.descriptions[i].getWikiText() + "\n";
 475+ }
 476+
 477+ var info = '';
 478+ for (var key in information) {
 479+ info += '|' + key/ + '=' + information[key];
 480+ }
 481+
 482+ wikiText += "=={int:filedesc}==\n";
 483+
 484+ return '{{Information ' + info + '}}';
 485+
 486+ wikiText += "=={int:license}==\n";
 487+ // XXX get the real one -- usually dual license GFDL/cc-by-sa
 488+ wikiText += "{{cc-by-sa-3.0}}\n";
 489+ // http://commons.wikimedia.org/wiki/Template:Information
 490+
 491+
 492+ return wikiText;
 493+ },
 494+
 495+ submit: function() {
 496+ // XXX check state of metadata for okayness (license selected, at least one desc, sane filename)
 497+ var wikiText = _this.getWikiText();
 498+ console.log(wikiText);
 499+ // do some api call to edit the info
 500+
 501+ // api.php  ? action=edit & title=Talk:Main_Page & section=new &  summary=Hello%20World & text=Hello%20everyone! & watch &  basetimestamp=2008-03-20T17:26:39Z &  token=cecded1f35005d22904a35cc7b736e18%2B%5C
 502+ // caution this may result in a captcha response, which user will have to solve
 503+ //
 504+
 505+ // then, if the filename was changed, do another api call to move the page
 506+ // 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
 507+ // we need an "incomplete" upload status
 508+ // we are presuming this File page is brand new, so let's not bother with the whole redirection deal. ('noredirect')
 509+
 510+ /*
 511+ Note: In this example, all parameters are passed in a GET request just for the sake of simplicity. However, action=move requires POST requests; GET requests will cause an error. Moving Main Pgae (sic) and its talk page to Main Page, without creating a redirect
 512+ api.php  ? action=move & from=Main%20Pgae & to=Main%20Page &  reason=Oops,%20misspelling & movetalk & noredirect &  token=58b54e0bab4a1d3fd3f7653af38e75cb%2B\
 513+ */
 514+
 515+
 516+ }
 517+};
 518+
 519+
 520+mw.UploadWizard.prototype = {
 521+ maxUploads: 10, // arbitrary
 522+ maxSimultaneousUploads: 2, // arbitrary
 523+ tabs: [ 'file', 'metadata', 'thanks' ],
 524+
 525+ /*
 526+ // list possible upload handlers in order of preference
 527+ // these should all be in the mw.* namespace
 528+ // hardcoded for now. maybe some registry system might work later, like, all
 529+ // things which subclass off of UploadHandler
 530+ uploadHandlers: [
 531+ 'FirefoggUploadHandler',
 532+ 'XhrUploadHandler',
 533+ 'ApiIframeUploadHandler',
 534+ 'SimpleUploadHandler',
 535+ 'NullUploadHandler'
 536+ ],
 537+
 538+ // let's figure out exactly what we can use.
 539+ //
 540+ getUploadHandlerClass: function() {
 541+ var _this = this;
 542+ for (var i = 0; i < uploadHandlers.length; i++) {
 543+ var klass = mw[uploadHandlers[i]];
 544+ if (klass != undefined && klass.canRun(this.config)) {
 545+ return klass;
 546+ }
 547+ }
 548+ // this should never happen; NullUploadHandler should always work
 549+ return null;
 550+ },
 551+ */
 552+
 553+ getUploadHandlerClass: function() {
 554+ return mw.ApiUploadHandler;
 555+ },
 556+
 557+ // create the basic interface to make an upload in this div
 558+ createInterface: function(div) {
 559+ var _this = this;
 560+ div.innerHTML =
 561+
 562+ '<div id="mwe-upwiz-tabs" class="mwe-upwiz-tabs">'
 563+ + '<ul>'
 564+ + '<li id="mwe-upwiz-tab-file">' + gM('mwe-upwiz-tab-file') + '</li>'
 565+ + '<li id="mwe-upwiz-tab-metadata">' + gM('mwe-upwiz-tab-metadata') + '</li>'
 566+ + '<li id="mwe-upwiz-tab-thanks">' + gM('mwe-upwiz-tab-thanks') + '</li>'
 567+ + '</ul>'
 568+ + '</div>'
 569+
 570+
 571+ + '<div id="mwe-upwiz-content">'
 572+ + '<div id="mwe-upwiz-tabdiv-file">'
 573+ + '<div id="mwe-upwiz-intro">' + gM('mwe-upwiz-intro') + '</div>'
 574+ + '<div id="mwe-upwiz-select-files">' + gM('mwe-upwiz-select-files') + '</div>'
 575+ + '<div id="mwe-upwiz-files"></div>'
 576+ + '<div><a id="mwe-upwiz-add-file">' + gM("mwe-upwiz-add-file-0") + '</a></div>'
 577+ + '<div><button id="mwe-upwiz-upload-ctrl" disabled="disabled">' + gM("mwe-upwiz-upload") + '</button></div>'
 578+ + '<div id="mwe-upwiz-progress" style="display:none">'
 579+ + '<div>'
 580+ + '<div id="mwe-upwiz-progress-bar"></div>'
 581+ + '<div id="mwe-upwiz-etr"></div>'
 582+ + '</div>'
 583+ + '<div id="mwe-upwiz-count"></div>'
 584+ + '</div>'
 585+ + '<div style="clear: left;"></div>'
 586+ + '</div>'
 587+ + '</div>'
 588+ + '<div id="mwe-upwiz-tabdiv-metadata">'
 589+ + '<div id="mwe-upwiz-metadata-macro"></div>'
 590+ + '<div id="mwe-upwiz-metadata-files"></div>'
 591+ + '</div>'
 592+ + '<div id="mwe-upwiz-tabdiv-thanks">'
 593+ + '<div id="mwe-upwiz-thanks"></div>'
 594+ + '</div>'
 595+ +'</div>'
 596+
 597+ + '<div id="mwe-upwiz-clearing"></div>';
 598+
 599+ // within FILE tab div
 600+ // select files:
 601+ // place for file interfaces
 602+ $j('#mwe-upwiz-add-file').click( function() { _this.addUpload() } );
 603+ $j('#mwe-upwiz-upload-ctrl').click( function() { _this.startUploads() } );
 604+
 605+ // Create global progress bar
 606+ $j( '#mwe-upwiz-progress-bar' ).progressbar({
 607+ value: 0
 608+ });
 609+
 610+ // add one to start
 611+ _this.addUpload();
 612+
 613+ // "select" the first tab - highlight, make it visible, hide all others
 614+ _this.moveToTab('file');
 615+ },
 616+
 617+ moveToTab: function(selectedTabName) {
 618+ var _this = this;
 619+ for (var i=0; i < _this.tabs.length; i++) {
 620+ tabName = _this.tabs[i];
 621+ var tabDiv = $j('#mwe-upwiz-tabdiv-' + tabName);
 622+ var tab = $j('#mwe-upwiz-tab-' + tabName);
 623+ if (selectedTabName == tabName) {
 624+ tabDiv.show();
 625+ tab.addClass('mwe-upwiz-tab-highlight');
 626+ } else {
 627+ //tabDiv.hide();
 628+ tab.removeClass('mwe-upwiz-tab-highlight');
 629+ }
 630+ }
 631+ // XXX possibly select appropriate form field to begin work
 632+ },
 633+
 634+ // add an Upload, with controls.
 635+ // XXX study what Mdale is doing to create this file form controls... he has full control over CSS etc and the browsing button.
 636+ addUpload: function() {
 637+ var _this = this;
 638+ var idx = _this.uploads.length; // or?
 639+ if (idx + 1 > _this.maxUploads) {
 640+ return false;
 641+ }
 642+
 643+ // could (should?) be an object, but so far it doesn't have its own methods really.
 644+ // plus, here in UploadWizard, we have additional concepts like metadata...
 645+ var upload = {};
 646+
 647+ // API
 648+ // XXX hardcoded for now. Maybe passed through config or guessed at here.
 649+ // upload.api = new mw.UploadApiProcessor(function(result) { _this.uploadCompleted);
 650+
 651+
 652+ // UI
 653+ var filenameAcceptedCb = function() {
 654+ _this.updateFileCounts();
 655+ };
 656+ var ui = new mw.UploadWizardUploadInterface(filenameAcceptedCb);
 657+ ui.removeCtrl = $j('<a title="' + gM( 'mwe-upwiz-remove-upload') + '" href="#" class="mwe-upwiz-file-remove">x</a>')
 658+ .click( function() { _this.removeUpload(upload) } )
 659+ .get(0);
 660+ $j(ui.div).append(ui.removeCtrl);
 661+
 662+ upload.ui = ui;
 663+
 664+ // handler -- usually ApiUploadHandler
 665+ upload.handler = new _this.uploadHandlerClass(upload.ui);
 666+
 667+ // this is for UI only...
 668+ upload.handler.addProgressCb( function(fraction) { _this.uploadProgress(upload, fraction) } );
 669+
 670+ // this is only the UI one, so is the result even going to be there?
 671+ upload.handler.addCompletedCb( function(result) { _this.uploadCompleted(upload, result) } );
 672+
 673+ // not sure about this...UI only?
 674+ // this will tell us that at least one of our uploads has had an error -- may change messaging,
 675+ // like, please fix below
 676+ upload.handler.addErrorCb( function(error) { _this.uploadError(upload, error) } );
 677+
 678+ // metadata
 679+ upload.metadata = new mw.UploadWizardMetadata($j('#mwe-upwiz-metadata-files'));
 680+
 681+
 682+
 683+ _this.uploads.push(upload);
 684+
 685+ $j("#mwe-upwiz-files").append(upload.ui.div);
 686+
 687+ // update the uploadUi to add files - we may be over limit
 688+ _this.updateFileCounts();
 689+
 690+ // the next thing we probably want to do is to get the file, so let's save them a click.
 691+ // XXX why doesn't this work?
 692+ // $j(ui.fileInputCtrl).trigger('click');
 693+ },
 694+
 695+ /* Remove an upload from our array of uploads, and the HTML UI
 696+ We can remove the HTML UI directly, as jquery will just get the parent.
 697+ We need to grep through the array of uploads, since we don't know the current index. */
 698+ removeUpload: function(upload) {
 699+ var _this = this;
 700+ $j(upload.ui.div).remove();
 701+ $j(upload.metadata.div).remove();
 702+ _this.removeItem(_this.uploads, upload);
 703+ _this.updateFileCounts();
 704+ },
 705+
 706+ // using a second array to iterate, because we will be splicing the main one, _this.uploads
 707+ removeEmptyUploads: function() {
 708+ var _this = this;
 709+ var toRemove = [];
 710+ for (var i = 0; i < _this.uploads.length; i++) {
 711+ if (_this.uploads[i].ui.fileInputCtrl.value == "") {
 712+ toRemove.push(_this.uploads[i]);
 713+ }
 714+ };
 715+ for (var i = 0; i < toRemove.length; i++) {
 716+ _this.removeUpload(toRemove[i]);
 717+ }
 718+ },
 719+
 720+ startUploads: function() {
 721+ var _this = this;
 722+ _this.removeEmptyUploads();
 723+ // remove the upload button, and the add file button
 724+ $j('#mwe-upwiz-upload-ctrl').hide();
 725+ $j('#mwe-upwiz-add-file').hide();
 726+
 727+ // remove ability to change files
 728+ // ideally also hide the "button"... but then we require styleable file input CSS trickery
 729+ // although, we COULD do this just for files already in progress...
 730+
 731+ // XXX we just want to VISUALLY lock it in -- disabling this seems to remove it from form post
 732+ // $j('.mwe-upwiz-file').attr('disabled', 'disabled');
 733+
 734+ // add the upload progress bar, with ETA
 735+ // add in the upload count
 736+ $j('#mwe-upwiz-progress').show();
 737+
 738+ _this.uploadsBeginTime = (new Date()).getTime();
 739+
 740+ var canGetFileSize = (_this.uploadHandlerClass.prototype.getFileSize !== undefined);
 741+
 742+ // queue the uploads
 743+ _this.totalWeight = 0;
 744+ for (var i = 0; i < _this.uploads.length; i++) {
 745+ var upload = _this.uploads[i];
 746+
 747+ // we may want to do something clever here to detect
 748+ // whether this is a real or dummy weight
 749+ if (canGetFileSize) {
 750+ upload.weight = upload.getFileSize();
 751+ } else {
 752+ upload.weight = 1;
 753+ }
 754+ _this.totalWeight += upload.weight;
 755+
 756+ _this._uploadsQueued.push(upload);
 757+ }
 758+ setTimeout( function () { _this._startUploadsQueued(); }, 0 );
 759+ },
 760+
 761+ // making this another thread of execution, because we want to avoid any race condition
 762+ // this way, this is the only "thread" that can start uploads
 763+ // it may miss a newly completed upload but it will get it eventually
 764+ _startUploadsQueued: function() {
 765+ var _this = this;
 766+ var uploadsToStart = Math.min(_this.maxSimultaneousUploads - _this._uploadsInProgress.length, _this._uploadsQueued.length);
 767+ console.log("_startUploadsQueued: should start " + uploadsToStart + " uploads");
 768+ while (uploadsToStart--) {
 769+ var upload = _this._uploadsQueued.shift();
 770+ _this._uploadsInProgress.push(upload);
 771+ upload.handler.start();
 772+ }
 773+ if (_this._uploadsQueued.length) {
 774+ setTimeout( function () { _this._startUploadsQueued(); }, 1000 );
 775+ }
 776+ },
 777+
 778+
 779+ // could be a spinning loop by itself, but this is annoying to debug
 780+ // and then we'd have to be careful to note the state-transition to completed once and only once. Race conditions.
 781+ showProgress: function() {
 782+ var _this = this;
 783+ if (_this.isCompleted) {
 784+ return;
 785+ }
 786+
 787+ //var updateFileCounts = false;
 788+ var fraction = 0;
 789+ for (var i = 0; i < _this.uploads.length; i++) {
 790+ var upload = _this.uploads[i];
 791+ console.log("progress of " + upload.ui.fileInputCtrl.value + " = " + upload.progress);
 792+ fraction += upload.progress * (upload.weight / _this.totalWeight);
 793+ }
 794+ _this.showProgressBar(fraction);
 795+
 796+ var remainingTime = _this.getRemainingTime(_this.uploadsBeginTime, fraction);
 797+ if (remainingTime !== null) {
 798+ _this.showRemainingTime(remainingTime);
 799+ }
 800+ },
 801+
 802+ // show the progress bar
 803+ showProgressBar: function(fraction) {
 804+ $j( '#mwe-upwiz-progress-bar' ).progressbar( 'value', parseInt( fraction * 100 ) );
 805+ },
 806+
 807+ // show remaining time for all the uploads
 808+ // remainingTime is in milliseconds
 809+ // XXX should be localized - x hours, x minutes, x seconds
 810+ showRemainingTime: function(remainingTime) {
 811+ $j( '#mwe-upwiz-etr' ).html( gM( 'mwe-upwiz-remaining', mw.seconds2npt(parseInt(remainingTime / 1000)) ) );
 812+ },
 813+
 814+
 815+ // XXX should be refactored with the very similar code in mw.UploadInterface.js:updateProgress
 816+ // returns time in whatever units getTime() returns; presumed milliseconds
 817+ getRemainingTime: function (beginTime, fractionCompleted) {
 818+ if ( beginTime ) {
 819+ var elapsedTime = ( new Date() ).getTime() - beginTime;
 820+ if (fractionCompleted > 0.0 && elapsedTime > 0) { // or some other minimums for good data
 821+ var rate = fractionCompleted / elapsedTime;
 822+ return parseInt( ( 1.0 - fractionCompleted ) / rate );
 823+ }
 824+ }
 825+ return null;
 826+ },
 827+
 828+ // okay we are in a confusing state here -- are we asking for progress to be stored in the uploadhandler for our perusal or
 829+ // to be explicitly forwarded to us
 830+ uploadProgress: function(upload, progress) {
 831+ console.log("upload progress is " + progress);
 832+ var _this = this;
 833+ //debugger;
 834+ upload.progress = progress;
 835+ _this.showProgress();
 836+ },
 837+
 838+ uploadCompleted: function(upload, result) {
 839+ var _this = this;
 840+ _this._uploadsCompleted.push(upload);
 841+ _this.removeItem(_this._uploadsInProgress, upload);
 842+
 843+ if ( result.upload && result.upload.imageinfo && result.upload.imageinfo.descriptionurl ) {
 844+ setTimeout( function() { upload.metadata.populateFromResult(result); }, 0 );
 845+
 846+ } else if (result.upload && result.upload.sessionkey) {
 847+ // there was a warning-type error which prevented it from adding the result to the db
 848+ if (result.upload.warnings.duplicate) {
 849+ var duplicates = result.upload.warnings.duplicate;
 850+ _this.metadata.errorDuplicate(result.upload.sessionkey, duplicates);
 851+ }
 852+
 853+ // XXX namespace collision
 854+ // and other errors that result in a stash
 855+ } else if (0 /* actual failure */) {
 856+ // we may want to tag or otherwise queue it as an upload to retry
 857+ }
 858+
 859+ _this.updateFileCounts();
 860+ },
 861+
 862+
 863+ // depending on number of file upoads, change link text (and/or disable it)
 864+ // change button disabled/enabled
 865+ updateFileCounts: function() {
 866+ console.log("update counts");
 867+ var _this = this;
 868+ var link = $j('#mwe-upwiz-add-file').get(0);
 869+ link.innerHTML = gM(
 870+ 'mwe-upwiz-add-file-' + (_this.uploads.length === 0 ? '0' : 'n')
 871+ );
 872+ if (_this.uploads.length < _this.maxUploads) {
 873+ link.removeAttribute('disabled');
 874+ } else {
 875+ link.setAttribute('disabled', true);
 876+ }
 877+
 878+ var hasFile;
 879+ for (var i = 0; i < _this.uploads.length; i++) {
 880+ var upload = _this.uploads[i];
 881+ if (upload.ui.fileInputCtrl.value != "") {
 882+ hasFile = true;
 883+ }
 884+ }
 885+ if (hasFile) {
 886+ $j('#mwe-upwiz-upload-ctrl').removeAttr('disabled');
 887+ } else {
 888+ $j('#mwe-upwiz-upload-ctrl').attr('disabled', 'disabled');
 889+ }
 890+
 891+
 892+ $j('#mwe-upwiz-count').html( gM('mwe-upwiz-upload-count', [ _this._uploadsCompleted.length, _this.uploads.length ]) );
 893+
 894+ if (_this._uploadsCompleted.length == _this.uploads.length) {
 895+ // is this enough to stop the progress monitor?
 896+ _this.isCompleted = true;
 897+ // set progress to 100%
 898+ _this.showProgressBar(1);
 899+ _this.showRemainingTime(0);
 900+
 901+ // XXX then should make the progress bar not have the animated lines when done. Solid blue, or fade away or something.
 902+ // likewise, the remaining time should disappear, fadeout maybe.
 903+
 904+ // do some sort of "all done" thing for the UI - advance to next tab maybe.
 905+ alert("all done!");
 906+ _this.moveToTab('metadata');
 907+ }
 908+
 909+ },
 910+
 911+
 912+ pause: function() {
 913+
 914+ },
 915+
 916+ stop: function() {
 917+
 918+ },
 919+
 920+ //
 921+ // entire METADATA TAB
 922+ //
 923+
 924+ createMetadata: function() {
 925+
 926+ },
 927+
 928+ submitMetadata: function() {
 929+
 930+ },
 931+
 932+ // utility function. not an object method
 933+ removeItem: function(items, item) {
 934+ for (var i = 0; i < items.length; i++) {
 935+ if (items[i] === item) {
 936+ items.splice(i, 1);
 937+ }
 938+ }
 939+ },
 940+
 941+
 942+
 943+};
 944+
Property changes on: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.UploadWizard.js
___________________________________________________________________
Name: svn:eol-style
1945 + native
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/css/uploadWizard.css
@@ -0,0 +1,117 @@
 2+/* XXX all these absolute pixel floats are evil, will bite us when i18n-ized */
 3+/* XXX mwe-upwiz-add-file jumps to the right when all file inputs are removed */
 4+
 5+form.mwe-upwiz-form {
 6+ display: inline;
 7+}
 8+
 9+.upload-section {
 10+ padding: 1em;
 11+ margin-bottom: 0.5em;
 12+ margin-top: 0.5em;
 13+ border: 1px solid #e0e0e0;
 14+}
 15+
 16+#mwe-upwiz-tabs {
 17+ float: left;
 18+ width: 100px;
 19+}
 20+
 21+#mwe-upwiz-content {
 22+ margin-left: 100px;
 23+}
 24+
 25+#mwe-upwiz-clearing {
 26+ clear: left;
 27+ width: 100%;
 28+}
 29+
 30+#mwe-upwiz-tabs ul {
 31+ list-style-type: none;
 32+ list-style-image: none;
 33+}
 34+
 35+#mwe-upwiz-tabs ul li {
 36+ padding: 0.5em;
 37+ margin: 0px;
 38+ color: #999999;
 39+ border-color: #e0e0e0;
 40+ border-width: 1px 0px 1px 1px;
 41+ border-style: solid;
 42+}
 43+
 44+
 45+#mwe-upwiz-tabs ul li.mwe-upwiz-tab-highlight {
 46+ font-weight: bold;
 47+ color: black;
 48+ border-color: #999999;
 49+}
 50+
 51+#mwe-upwiz-content {
 52+ padding: 1em;
 53+}
 54+
 55+/* perhaps a general class for links that are actually "buttons" */
 56+#mwe-upwiz-add-file, .mwe-upwiz-file-remove {
 57+ cursor: pointer;
 58+}
 59+
 60+#mwe-upwiz-select-files {
 61+ float: left;
 62+ width: 100px;
 63+}
 64+
 65+#mwe-upwiz-files {
 66+ margin-left: 100px;
 67+}
 68+
 69+#mwe-upwiz-add-file {
 70+ margin-left: 100px;
 71+}
 72+
 73+.mwe-upwiz-file-remove {
 74+ margin-left: 5px;
 75+ padding: 5px;
 76+}
 77+
 78+a.mwe-upwiz-file-remove:link,
 79+a.mwe-upwiz-file-remove:active,
 80+a.mwe-upwiz-file-remove:visited {
 81+ color: #999999;
 82+ text-decoration: none;
 83+}
 84+
 85+a.mwe-upwiz-file-remove:hover {
 86+ color: #ff0000;
 87+ text-decoration: none;
 88+}
 89+
 90+a[disabled=true] {
 91+ color: #999999;
 92+ text-decoration: none;
 93+ cursor: default;
 94+}
 95+
 96+a[disabled=true]:hover {
 97+ text-decoration: none;
 98+}
 99+
 100+.mwe-upwiz-status-progress {
 101+ font-weight: bold;
 102+ color: #ff9900;
 103+}
 104+
 105+/* XXX add background of checkmark, with left-padding offset */
 106+.mwe-upwiz-status-completed {
 107+ font-weight: bold;
 108+ color: #009900;
 109+}
 110+
 111+.mwe-upwiz-upload-warning {
 112+ background: #ffffe0;
 113+}
 114+
 115+.mwe-upwiz-metadata-error {
 116+ display: none;
 117+ background: #ffffe0;
 118+}
Property changes on: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/css/uploadWizard.css
___________________________________________________________________
Name: svn:eol-style
1119 + native
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.ApiUploadHandler.js
@@ -0,0 +1,135 @@
 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+mw.ApiUploadHandler = function(ui) {
 11+ var _this = this;
 12+
 13+ _this.ui = ui;
 14+
 15+ var form = _this.ui.form;
 16+
 17+ _this.completedCallbacks = [];
 18+ _this.progressCallbacks = [];
 19+ _this.errorCallbacks = [];
 20+
 21+ _this.configureForm();
 22+
 23+ // hardcoded for now
 24+ // can also use Xhr Binary depending on config
 25+ _this.transport = new mw.IframeTransport(
 26+ _this.ui.form,
 27+ function(fraction){ _this.progress(fraction) },
 28+ function(result) { _this.completed(result) }
 29+ );
 30+
 31+};
 32+
 33+mw.ApiUploadHandler.prototype = {
 34+ addProgressCb: function(fn) {
 35+ var _this = this;
 36+ _this.progressCallbacks.push(function(progress) { fn(progress) });
 37+ },
 38+
 39+
 40+ addCompletedCb: function(fn) {
 41+ var _this = this;
 42+ _this.completedCallbacks.push(function() { fn() });
 43+ },
 44+
 45+ addErrorCb: function(fn) {
 46+ var _this = this;
 47+ _this.errorCallbacks.push(function() { fn(error) });
 48+ },
 49+
 50+ configureForm: function() {
 51+ var apiUrl = mw.getLocalApiUrl(); // XXX or? throw new Error("configuration", "no API url");
 52+ if (! (mw.getConfig('token') ) ) {
 53+ throw new Error("configuration", "no edit token");
 54+ }
 55+
 56+ var _this = this;
 57+ console.log("configuring form for Upload API");
 58+
 59+ // Set the form action
 60+ try {
 61+ $j(_this.ui.form)
 62+ .attr('action', apiUrl)
 63+ .attr('method', 'POST')
 64+ .attr('enctype', 'multipart/form-data');
 65+ } catch ( e ) {
 66+ alert("oops, form modification didn't work in ApiUploadHandler");
 67+ mw.log("IE for some reason error's out when you change the action");
 68+ // well, if IE fucks this up perhaps we should do something to make sure it writes correctly
 69+ // from the outset?
 70+ }
 71+
 72+ _this.addFormInputIfMissing('token', mw.getConfig('token'));
 73+ _this.addFormInputIfMissing('action', 'upload');
 74+ _this.addFormInputIfMissing('format', 'jsonfm');
 75+ },
 76+
 77+ addFormInputIfMissing: function(name, value) {
 78+ var _this = this;
 79+ var $jForm = $j(_this.ui.form);
 80+ if ( $jForm.find( "[name='" + name + "']" ).length == 0 ) {
 81+ $jForm.append(
 82+ $j('<input />')
 83+ .attr({
 84+ 'type': "hidden",
 85+ 'name' : name,
 86+ 'value' : value
 87+ })
 88+ );
 89+ }
 90+ },
 91+
 92+ start: function() {
 93+ var _this = this;
 94+ console.log("api: upload start!")
 95+ _this.beginTime = (new Date()).getTime();
 96+ _this.ui.start();
 97+ _this.ui.busy();
 98+ $j(this.ui.form).submit();
 99+ },
 100+
 101+ progress: function(fraction) {
 102+ console.log("api: upload progress!")
 103+ var _this = this;
 104+ _this.ui.progress(fraction);
 105+ for (var i = 0; i < _this.progressCallbacks.length; i++) {
 106+ debugger;
 107+ _this.progressCallbacks[i](fraction);
 108+ }
 109+ },
 110+
 111+ // this is not quite the right place for all this code
 112+ // perhaps should be abstract to any uploadHandler, or not
 113+ // in this at all
 114+ completed: function(result) {
 115+ console.log("api: upload completed!")
 116+ var _this = this;
 117+
 118+ _this.ui.completed();
 119+
 120+ for (var i = 0; i < _this.completedCallbacks.length; i++) {
 121+ _this.completedCallbacks[i](result);
 122+ }
 123+ },
 124+
 125+ error: function(error) {
 126+ console.log("api: error!");
 127+ var _this = this;
 128+ _this.ui.error(error);
 129+ for (var i = 0; i < _this.errorCallbacks.length; i++) {
 130+ _this.errorCallbacks[i](error);
 131+ }
 132+ }
 133+};
 134+
 135+
 136+
Property changes on: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.ApiUploadHandler.js
___________________________________________________________________
Name: svn:eol-style
1137 + native
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.MockUploadHandler.js
@@ -0,0 +1,75 @@
 2+// Currently this doesn't at all follow the interface that Mdale made in UploadHandler
 3+// will have to figure this out.
 4+
 5+// this should be loaded with test suites when appropriate. separate file.
 6+mw.MockUploadHandler = function(ui) {
 7+ this.ui = ui;
 8+ this.nextState = null;
 9+ this.progress = 0.0;
 10+ this.isCompleted = false;
 11+
 12+ _this.completedCallbacks = [];
 13+ _this.progressCallbacks = [];
 14+ _this.warningCallbacks = [];
 15+
 16+ // if we ever get a pause control,
 17+ // may need to turn this into an array of pairs, start & stops
 18+ this.beginTime = null;
 19+};
 20+
 21+mw.MockUploadHandler.prototype = {
 22+ addProgressCb: function(fn) {
 23+ var _this = this;
 24+ _this.progressCallbacks.push(function(progress) { fn(_this, progress) });
 25+ },
 26+
 27+
 28+ addCompletedCb: function(fn) {
 29+ var _this = this;
 30+ _this.completedCallbacks.push(function() { fn(_this) });
 31+ },
 32+
 33+ addWarningCb: function(fn) {
 34+ var _this = this;
 35+ _this.warningCallbacks.push(function() { fn(_this, warning) });
 36+ },
 37+
 38+
 39+ start: function () {
 40+ var _this = this;
 41+ _this.beginTime = (new Date()).getTime();
 42+ _this.nextState = _this.cont;
 43+ _this.ui.start();
 44+ _this.nextState();
 45+ },
 46+ cont: function () {
 47+ var _this = this;
 48+ var delta = 0.0001; // static?
 49+ _this.progress += 0.1;
 50+ _this.ui.progress(_this.progress);
 51+ _this.progressCb(_this, _this.progress);
 52+ if (1.0 - _this.progress < delta) {
 53+ _this.completed();
 54+ } else {
 55+ setTimeout( function() { _this.nextState() }, 1000 );
 56+ }
 57+ },
 58+
 59+ completed: function() {
 60+ var _this = this;
 61+ _this.isCompleted = true;
 62+ _this.ui.completed();
 63+ _this.completedCb(_this);
 64+ },
 65+ stop: function () {
 66+ // tell the interface that we're stopped
 67+ },
 68+
 69+ //pause: function () { // },
 70+ //resume: function () { // this.nextState = this.stop(); },
 71+ remove: function () {
 72+ // ??
 73+ }
 74+};
 75+
 76+
Property changes on: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.MockUploadHandler.js
___________________________________________________________________
Name: svn:eol-style
177 + native
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/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' ).html( mw.loading_spinner() );
 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: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.UploadApiProcessor.js
___________________________________________________________________
Name: svn:eol-style
1298 + native
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/README
@@ -0,0 +1,66 @@
 2+What this is
 3+============
 4+
 5+It's the first stab at a more usable multimedia uploader, particularly for Wikimedia Commons.
 6+
 7+Main features:
 8+ - Multiple simultaneous file uploads
 9+ - Batch assertion of license ("all these files were CC-BY-SA")
 10+ - "Incomplete" uploads, where the data can be uploaded and metadata edited without really adding the file to the collection.
 11+ - (Eventually) Gee-whiz technology like HTML5 drag & drop from the desktop
 12+
 13+http://usability.wikimedia.org/wiki/Multimedia:NewUpload
 14+
 15+
 16+General disclaimer
 17+===================
 18+
 19+For the moment, (March 2010) UploadWizard is all alpha quality code.
 20+It often does not conform to the MediaWiki coding standards. This will have to be fixed before it gets merged into trunk.
 21+
 22+It uses MwEmbed because it was found to be very difficult to separate it from the framework.
 23+Furthermore, it is somewhat redundant with the AddMedia module. AddMedia has a lot of useful code but
 24+not factored in the way that would be most convenient for this module. Michael Dale (mdale) and myself (NeilK)
 25+are attempting to refactor things and meet in the middle.
 26+
 27+Presently (March 2010) we are sprinting to get a hacky interface up to conduct a usability test on our
 28+basic ideas about updating media uploads for commons. Nothing here is expected to stay here permanently.
 29+
 30+Stay tuned, or, talk to NeilK about this, if anything here bothers you.
 31+
 32+
 33+How to use it
 34+=============
 35+
 36+This is *very* complicated at present. Obviously we want to make this simpler in the future.
 37+
 38+Currently mdale tests with the live Commons site by loading in code from a JS2-enabled prototype.
 39+This is accomplished with a gadget on Commons (or other additions to the skin).
 40+
 41+Here's how you might do such a thing:
 42+
 43+- Set up an instance of MediaWiki from trunk; or, if feeling brave[1], use a real site like Commons itself.
 44+- Set up an instance of MediaWiki from js-work branch, which your browser can access.
 45+- On this trunk-based Mediawiki, install a gadget which is invoked on all pages to load some code from the JS2-enabled site. Since
 46+ this is all accomplished in your browser, you could use a local hostname. (See below).
 47+- Point your browser to the upload page on the trunk-based mediawiki, with a special parameter: Special:Upload?uploadWizard=1 .
 48+
 49+
 50+[1] Currently I (NeilK) use a local MediaWiki, since we are uploading stuff *without* the normal checks
 51+ that the Commons people have carefully added.
 52+
 53+[2] The gadget might look like this:
 54+
 55+----- cut here ----
 56+// Once mwEmbed is "on" we can use mw.setConfig calls:
 57+if( typeof mwAddMediaConfig == 'undefined'){
 58+mwAddMediaConfig = {};
 59+}
 60+mwAddMediaConfig['enabled_providers'] = [ 'wiki_commons', 'upload' ]
 61+
 62+importScriptURI('http://js2-work.yoursite.local/js/mwEmbed/remotes/mediaWiki.js?&uselang=en&debug=true' );
 63+----- cut here ----
 64+
 65+
 66+
 67+
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.IframeTransport.js
@@ -0,0 +1,96 @@
 2+mw.IframeTransport = function(form, progressCb, completedCb) {
 3+ var _this = this;
 4+
 5+ _this.form = form;
 6+ _this.progressCb = progressCb;
 7+ _this.completedCb = completedCb;
 8+
 9+ _this.iframeId = 'f_' + ( $j( 'iframe' ).length + 1 );
 10+
 11+ //IE only works if you "create element with the name" (not jquery style)
 12+ var iframe;
 13+ try {
 14+ iframe = document.createElement( '<iframe name="' + _this.iframeId + '">' );
 15+ } catch (ex) {
 16+ iframe = document.createElement('iframe');
 17+ }
 18+
 19+ // we configure form on load, because the first time it loads, it's blank
 20+ // then we configure it to deal with an API submission
 21+ $j( iframe )
 22+ .attr({ 'src' : 'javascript:false;',
 23+ 'id' : _this.iframeId,
 24+ 'name' : _this.iframeId })
 25+ .load(function() { _this.configureForm() })
 26+ .css('display', 'none');
 27+
 28+ $j( "body" ).append( iframe );
 29+};
 30+
 31+mw.IframeTransport.prototype = {
 32+ configureForm: function() {
 33+ console.log("configuring form for iframe transport");
 34+ var _this = this;
 35+ // Set the form target to the iframe
 36+ var $jForm = $j(_this.form);
 37+ $jForm.attr( 'target', _this.iframeId );
 38+
 39+ // attach an additional handler to the form, so, when submitted, it starts showing the progress
 40+ // XXX this is lame .. there should be a generic way to indicate busy status...
 41+ $jForm.submit( function() {
 42+ console.log("submitting to iframe...");
 43+ _this.progressCb(1.0);
 44+ return true;
 45+ } );
 46+
 47+ // Set up the completion callback
 48+ $j( '#' + _this.iframeId ).load( function() {
 49+ console.log("received result in iframe");
 50+ _this.processIframeResult( $j( this ).get( 0 ) );
 51+ });
 52+ },
 53+
 54+ /**
 55+ * Process the result of the form submission, returned to an iframe.
 56+ * This is the iframe's onload event.
 57+ *
 58+ * @param {Element} iframe iframe to extract result from
 59+ */
 60+ processIframeResult: function( iframe ) {
 61+ var _this = this;
 62+ var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document;
 63+ // Fix for Opera 9.26
 64+ if ( doc.readyState && doc.readyState != 'complete' ) {
 65+ console.log("not complete");
 66+ return;
 67+ }
 68+
 69+ // Fix for Opera 9.64
 70+ if ( doc.body && doc.body.innerHTML == "false" ) {
 71+ console.log("no innerhtml");
 72+ return;
 73+ }
 74+ var response;
 75+ if ( doc.XMLDocument ) {
 76+ // The response is a document property in IE
 77+ response = doc.XMLDocument;
 78+ } else if ( doc.body ) {
 79+ // Get the json string
 80+ // XXX wait... why are we grepping it out of an HTML doc? We requested jsonfm, why?
 81+ json = $j( doc.body ).find( 'pre' ).text();
 82+ mw.log( 'iframe:json::' + json)
 83+ if ( json ) {
 84+ response = window["eval"]( "(" + json + ")" );
 85+ } else {
 86+ response = {};
 87+ }
 88+ } else {
 89+ // Response is a xml document
 90+ response = doc;
 91+ }
 92+ // Process the API result
 93+ _this.completedCb( response );
 94+ }
 95+};
 96+
 97+
Property changes on: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.IframeTransport.js
___________________________________________________________________
Name: svn:eol-style
198 + native
Index: branches/js2-work/phase3/js/mwEmbed/remotes/mediaWiki.js
@@ -5,7 +5,6 @@
66 var urlparts = getRemoteEmbedPath();
77 var mwEmbedHostPath = urlparts[0];
88 var mwRemoteVersion = 'r104';
9 -var mwUseScriptLoader = true;
109
1110 // Log the mwRemote version ( will determine what version of js we get )
1211 if( window.console ){
@@ -17,10 +16,13 @@
1817 var mwReqParam = { };
1918 for ( var i = 0; i < reqParts.length; i++ ) {
2019 var p = reqParts[i].split( '=' );
21 - if ( p.length == 2 )
 20+ if ( p.length == 2 ) {
2221 mwReqParam[ p[0] ] = p[1];
 22+ }
2323 }
2424
 25+var mwUseScriptLoader = (mwReqParam['debug'] != 'true');
 26+
2527 // Use wikibits onLoad hook: ( since we don't have js2 / mw object loaded )
2628 addOnloadHook( function() {
2729 doPageSpecificRewrite();
@@ -96,11 +98,15 @@
9799
98100 // Upload page -> Firefogg / upload API / uploadWizard integration
99101 if ( wgPageName == "Special:Upload" ) {
100 - var scriptName = 'uploadPage.js';
101 - if ( location.search.indexOf('uploadWizard=1') != -1 ) {
102 - scriptName = 'uploadWizard.js';
103 - }
104 - loadMwEmbed( [
 102+ var scriptUrl = null;
 103+ var scriptName = null;
 104+ var libraries = [];
 105+ if ( location.search.indexOf('uploadWizard=1') !== -1 ) {
 106+ scriptName = 'uploadWizardPage.js';
 107+ libraries = [];
 108+ } else {
 109+ scriptName = 'uploadPage.js';
 110+ libraries = [
105111 'mw.UploadHandler',
106112 'mw.UploadInterface',
107113 'mw.Firefogg',
@@ -108,10 +114,10 @@
109115 '$j.ui.progressbar',
110116 '$j.ui.dialog',
111117 '$j.ui.draggable'
112 - ], function() {
113 - mw.load( mwEmbedHostPath + '/' + scriptName + '?' + mwGetReqArgs() );
114 - }
115 - );
 118+ ];
 119+ }
 120+ var scriptUrl = mwEmbedHostPath + '/' + scriptName + '?' + mwGetReqArgs()
 121+ loadMwEmbed(libraries, function() { mw.load( scriptUrl ) } );
116122 }
117123
118124 // Special api proxy page
Index: branches/js2-work/phase3/js/uploadWizardPage.js
@@ -0,0 +1,40 @@
 2+/*
 3+ * This script is run on [[Special:Upload]].
 4+ * Creates an interface for uploading files in multiple steps, hence "wizard"
 5+ */
 6+
 7+
 8+mw.ready( function() {
 9+
 10+ // steal the edit token from the existing page
 11+ var token = $j( "#wpEditToken" ).val();
 12+
 13+ var licensingDiv = $j('<div id="upload-licensing" class="upload-section">Licensing tutorial</div>').get(0);
 14+ var wizardDiv = $j('<div id="upload-wizard" class="upload-section"></div>').get(0);
 15+ $j('#bodyContent').empty().append(licensingDiv).append(wizardDiv);
 16+
 17+ // change title on page too -- this doesn't have to be internationalized yet since this is just for testing
 18+ // when it has its own page it will have a real title
 19+ $j('#firstHeading').html("Upload wizard");
 20+
 21+ mw.load( 'UploadWizard.UploadWizardTest', function () {
 22+
 23+ mw.setDefaultConfig('uploadHandlerClass', null);
 24+ mw.setConfig('userName', wgUserName);
 25+ mw.setConfig('userLanguage', wgUserLanguage);
 26+ mw.setConfig('fileExtensions', wgFileExtensions);
 27+ mw.setConfig('token', token);
 28+ mw.setConfig('languages', [
 29+ { code: 'en', name: 'English' },
 30+ { code: 'fr', name: 'Fran&ccedil;ais' },
 31+ { code: 'es', name: 'Espagnol' },
 32+ ]);
 33+ mw.setConfig('thumbnailWidth', 220); // new standard size
 34+
 35+ var uploadWizard = new mw.UploadWizard();
 36+ uploadWizard.createInterface(wizardDiv);
 37+
 38+ });
 39+
 40+
 41+} );
Property changes on: branches/js2-work/phase3/js/uploadWizardPage.js
___________________________________________________________________
Name: svn:eol-style
142 + native

Status & tagging log