[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/form/ -> dndupload.js (source)

   1  // This file is part of Moodle - http://moodle.org/
   2  //
   3  // Moodle is free software: you can redistribute it and/or modify
   4  // it under the terms of the GNU General Public License as published by
   5  // the Free Software Foundation, either version 3 of the License, or
   6  // (at your option) any later version.
   7  //
   8  // Moodle is distributed in the hope that it will be useful,
   9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11  // GNU General Public License for more details.
  12  //
  13  // You should have received a copy of the GNU General Public License
  14  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  15  
  16  /**
  17   * Javascript library for enableing a drag and drop upload interface
  18   *
  19   * @package    moodlecore
  20   * @subpackage form
  21   * @copyright  2011 Davo Smith
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  M.form_dndupload = {}
  26  
  27  M.form_dndupload.init = function(Y, options) {
  28      var dnduploadhelper = {
  29          // YUI object.
  30          Y: null,
  31          // URL for upload requests
  32          url: M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload',
  33          // options may include: itemid, acceptedtypes, maxfiles, maxbytes, clientid, repositoryid, author, contextid
  34          options: {},
  35          // itemid used for repository upload
  36          itemid: null,
  37          // accepted filetypes accepted by this form passed to repository
  38          acceptedtypes: [],
  39          // maximum size of files allowed in this form
  40          maxbytes: 0,
  41          // Maximum combined size of files allowed in this form. {@link FILE_AREA_MAX_BYTES_UNLIMITED}
  42          areamaxbytes: -1,
  43          // unqiue id of this form field used for html elements
  44          clientid: '',
  45          // upload repository id, used for upload
  46          repositoryid: 0,
  47          // container which holds the node which recieves drag events
  48          container: null,
  49          // filemanager element we are working with
  50          filemanager: null,
  51          // callback  to filepicker element to refesh when uploaded
  52          callback: null,
  53          // Nasty hack to distinguish between dragenter(first entry),
  54          // dragenter+dragleave(moving between child elements) and dragleave (leaving element)
  55          entercount: 0,
  56          pageentercount: 0,
  57          // Holds the progress bar elements for each file.
  58          progressbars: {},
  59  
  60          /**
  61           * Initalise the drag and drop upload interface
  62           * Note: one and only one of options.filemanager and options.formcallback must be defined
  63           *
  64           * @param Y the YUI object
  65           * @param object options {
  66           *            itemid: itemid used for repository upload in this form
  67           *            acceptdtypes: accepted filetypes by this form
  68           *            maxfiles: maximum number of files this form allows
  69           *            maxbytes: maximum size of files allowed in this form
  70           *            areamaxbytes: maximum combined size of files allowed in this form
  71           *            clientid: unqiue id of this form field used for html elements
  72           *            contextid: id of the current cotnext
  73           *            containerid: htmlid of container
  74           *            repositories: array of repository objects passed from filepicker
  75           *            filemanager: filemanager element we are working with
  76           *            formcallback: callback  to filepicker element to refesh when uploaded
  77           *          }
  78           */
  79          init: function(Y, options) {
  80              this.Y = Y;
  81  
  82              if (!this.browser_supported()) {
  83                  Y.one('body').addClass('dndnotsupported');
  84                  return; // Browser does not support the required functionality
  85              }
  86  
  87              // try and retrieve enabled upload repository
  88              this.repositoryid = this.get_upload_repositoryid(options.repositories);
  89  
  90              if (!this.repositoryid) {
  91                  Y.one('body').addClass('dndnotsupported');
  92                  return; // no upload repository is enabled to upload to
  93              }
  94  
  95              Y.one('body').addClass('dndsupported');
  96  
  97              this.options = options;
  98              this.acceptedtypes = options.acceptedtypes;
  99              this.clientid = options.clientid;
 100              this.maxbytes = options.maxbytes;
 101              this.areamaxbytes = options.areamaxbytes;
 102              this.itemid = options.itemid;
 103              this.author = options.author;
 104              this.container = this.Y.one('#'+options.containerid);
 105  
 106              if (options.filemanager) {
 107                  // Needed to tell the filemanager to redraw when files uploaded
 108                  // and to check how many files are already uploaded
 109                  this.filemanager = options.filemanager;
 110              } else if (options.formcallback) {
 111  
 112                  // Needed to tell the filepicker to update when a new
 113                  // file is uploaded
 114                  this.callback = options.formcallback;
 115              } else {
 116                  if (M.cfg.developerdebug) {
 117                      alert('dndupload: Need to define either options.filemanager or options.formcallback');
 118                  }
 119                  return;
 120              }
 121  
 122              this.init_events();
 123              this.init_page_events();
 124          },
 125  
 126          /**
 127           * Check the browser has the required functionality
 128           * @return true if browser supports drag/drop upload
 129           */
 130          browser_supported: function() {
 131  
 132              if (typeof FileReader == 'undefined') {
 133                  return false;
 134              }
 135              if (typeof FormData == 'undefined') {
 136                  return false;
 137              }
 138              return true;
 139          },
 140  
 141          /**
 142           * Get upload repoistory from array of enabled repositories
 143           *
 144           * @param array repositories repository objects passed from filepicker
 145           * @param returns int id of upload repository or false if not found
 146           */
 147          get_upload_repositoryid: function(repositories) {
 148              for (var i in repositories) {
 149                  if (repositories[i].type == "upload") {
 150                      return repositories[i].id;
 151                  }
 152              }
 153  
 154              return false;
 155          },
 156  
 157          /**
 158           * Initialise drag events on node container, all events need
 159           * to be processed for drag and drop to work
 160           */
 161          init_events: function() {
 162              this.Y.on('dragenter', this.drag_enter, this.container, this);
 163              this.Y.on('dragleave', this.drag_leave, this.container, this);
 164              this.Y.on('dragover',  this.drag_over,  this.container, this);
 165              this.Y.on('drop',      this.drop,      this.container, this);
 166          },
 167  
 168          /**
 169           * Initialise whole-page events (to show / hide the 'drop files here'
 170           * message)
 171           */
 172          init_page_events: function() {
 173              this.Y.on('dragenter', this.drag_enter_page, 'body', this);
 174              this.Y.on('dragleave', this.drag_leave_page, 'body', this);
 175          },
 176  
 177          /**
 178           * Check if the filemanager / filepicker is disabled
 179           * @return bool - true if disabled
 180           */
 181          is_disabled: function() {
 182              return (this.container.ancestor('.fitem.disabled') != null);
 183          },
 184  
 185          /**
 186           * Show the 'drop files here' message when file(s) are dragged
 187           * onto the page
 188           */
 189          drag_enter_page: function(e) {
 190              if (this.is_disabled()) {
 191                  return false;
 192              }
 193              if (!this.has_files(e)) {
 194                  return false;
 195              }
 196  
 197              this.pageentercount++;
 198              if (this.pageentercount >= 2) {
 199                  this.pageentercount = 2;
 200                  return false;
 201              }
 202  
 203              this.show_drop_target();
 204  
 205              return false;
 206          },
 207  
 208          /**
 209           * Hide the 'drop files here' message when file(s) are dragged off
 210           * the page again
 211           */
 212          drag_leave_page: function(e) {
 213              this.pageentercount--;
 214              if (this.pageentercount == 1) {
 215                  return false;
 216              }
 217              this.pageentercount = 0;
 218  
 219              this.hide_drop_target();
 220  
 221              return false;
 222          },
 223  
 224          /**
 225           * Check if the drag contents are valid and then call
 226           * preventdefault / stoppropagation to let the browser know
 227           * we will handle this drag/drop
 228           *
 229           * @param e event object
 230           * @return boolean true if a valid file drag event
 231           */
 232          check_drag: function(e) {
 233              if (this.is_disabled()) {
 234                  return false;
 235              }
 236              if (!this.has_files(e)) {
 237                  return false;
 238              }
 239  
 240              e.preventDefault();
 241              e.stopPropagation();
 242  
 243              return true;
 244          },
 245  
 246          /**
 247           * Handle a dragenter event, highlight the destination node
 248           * when a suitable drag event occurs
 249           */
 250          drag_enter: function(e) {
 251              if (!this.check_drag(e)) {
 252                  return true;
 253              }
 254  
 255              this.entercount++;
 256              if (this.entercount >= 2) {
 257                  this.entercount = 2; // Just moved over a child element - nothing to do
 258                  return false;
 259              }
 260  
 261              // These lines are needed if the user has dragged something directly
 262              // from application onto the 'fileupload' box, without crossing another
 263              // part of the page first
 264              this.pageentercount = 2;
 265              this.show_drop_target();
 266  
 267              this.show_upload_ready();
 268              return false;
 269          },
 270  
 271          /**
 272           * Handle a dragleave event, Remove the highlight if dragged from
 273           * node
 274           */
 275          drag_leave: function(e) {
 276              if (!this.check_drag(e)) {
 277                  return true;
 278              }
 279  
 280              this.entercount--;
 281              if (this.entercount == 1) {
 282                  return false; // Just moved over a child element - nothing to do
 283              }
 284  
 285              this.entercount = 0;
 286              this.hide_upload_ready();
 287              return false;
 288          },
 289  
 290          /**
 291           * Handle a dragover event. Required to intercept to prevent the browser from
 292           * handling the drag and drop event as normal
 293           */
 294          drag_over: function(e) {
 295              if (!this.check_drag(e)) {
 296                  return true;
 297              }
 298  
 299              return false;
 300          },
 301  
 302          /**
 303           * Handle a drop event.  Remove the highlight and then upload each
 304           * of the files (until we reach the file limit, or run out of files)
 305           */
 306          drop: function(e) {
 307              if (!this.check_drag(e, true)) {
 308                  return true;
 309              }
 310  
 311              this.entercount = 0;
 312              this.pageentercount = 0;
 313              this.hide_upload_ready();
 314              this.hide_drop_target();
 315  
 316              var files = e._event.dataTransfer.files;
 317              if (this.filemanager) {
 318                  var options = {
 319                      files: files,
 320                      options: this.options,
 321                      repositoryid: this.repositoryid,
 322                      currentfilecount: this.filemanager.filecount, // All files uploaded.
 323                      currentfiles: this.filemanager.options.list, // Only the current folder.
 324                      callback: Y.bind('update_filemanager', this),
 325                      callbackprogress: Y.bind('update_progress', this),
 326                      callbackcancel:Y.bind('hide_progress', this)
 327                  };
 328                  this.clear_progress();
 329                  this.show_progress();
 330                  var uploader = new dnduploader(options);
 331                  uploader.start_upload();
 332              } else {
 333                  if (files.length >= 1) {
 334                      options = {
 335                          files:[files[0]],
 336                          options: this.options,
 337                          repositoryid: this.repositoryid,
 338                          currentfilecount: 0,
 339                          currentfiles: [],
 340                          callback: Y.bind('update_filemanager', this),
 341                          callbackprogress: Y.bind('update_progress', this),
 342                          callbackcancel:Y.bind('hide_progress', this)
 343                      };
 344                      this.clear_progress();
 345                      this.show_progress();
 346                      uploader = new dnduploader(options);
 347                      uploader.start_upload();
 348                  }
 349              }
 350  
 351              return false;
 352          },
 353  
 354          /**
 355           * Check to see if the drag event has any files in it
 356           *
 357           * @param e event object
 358           * @return boolean true if event has files
 359           */
 360          has_files: function(e) {
 361              // In some browsers, dataTransfer.types may be null for a
 362              // 'dragover' event, so ensure a valid Array is always
 363              // inspected.
 364              var types = e._event.dataTransfer.types || [];
 365              for (var i=0; i<types.length; i++) {
 366                  if (types[i] == 'Files') {
 367                      return true;
 368                  }
 369              }
 370              return false;
 371          },
 372  
 373          /**
 374           * Highlight the area where files could be dropped
 375           */
 376          show_drop_target: function() {
 377              this.container.addClass('dndupload-ready');
 378          },
 379  
 380          hide_drop_target: function() {
 381              this.container.removeClass('dndupload-ready');
 382          },
 383  
 384          /**
 385           * Highlight the destination node (ready to drop)
 386           */
 387          show_upload_ready: function() {
 388              this.container.addClass('dndupload-over');
 389          },
 390  
 391          /**
 392           * Remove highlight on destination node
 393           */
 394          hide_upload_ready: function() {
 395              this.container.removeClass('dndupload-over');
 396          },
 397  
 398          /**
 399           * Show the element showing the upload in progress
 400           */
 401          show_progress: function() {
 402              this.container.addClass('dndupload-inprogress');
 403          },
 404  
 405          /**
 406           * Hide the element showing upload in progress
 407           */
 408          hide_progress: function() {
 409              this.container.removeClass('dndupload-inprogress');
 410          },
 411  
 412          /**
 413           * Tell the attached filemanager element (if any) to refresh on file
 414           * upload
 415           */
 416          update_filemanager: function(params) {
 417              this.hide_progress();
 418              if (this.filemanager) {
 419                  // update the filemanager that we've uploaded the files
 420                  this.filemanager.filepicker_callback();
 421              } else if (this.callback) {
 422                  this.callback(params);
 423              }
 424          },
 425  
 426          /**
 427           * Clear the current progress bars
 428           */
 429          clear_progress: function() {
 430              var filename;
 431              for (filename in this.progressbars) {
 432                  if (this.progressbars.hasOwnProperty(filename)) {
 433                      this.progressbars[filename].progressouter.remove(true);
 434                      delete this.progressbars[filename];
 435                  }
 436              }
 437          },
 438  
 439          /**
 440           * Show the current progress of the uploaded file
 441           */
 442          update_progress: function(filename, percent) {
 443              if (this.progressbars[filename] === undefined) {
 444                  var dispfilename = filename;
 445                  if (dispfilename.length > 50) {
 446                      dispfilename = dispfilename.substr(0, 49)+'&hellip;';
 447                  }
 448                  var progressouter = this.container.create('<span>'+dispfilename+': <span class="dndupload-progress-outer"><span class="dndupload-progress-inner">&nbsp;</span></span><br /></span>');
 449                  var progressinner = progressouter.one('.dndupload-progress-inner');
 450                  var progresscontainer = this.container.one('.dndupload-progressbars');
 451                  progresscontainer.appendChild(progressouter);
 452  
 453                  this.progressbars[filename] = {
 454                      progressouter: progressouter,
 455                      progressinner: progressinner
 456                  };
 457              }
 458  
 459              this.progressbars[filename].progressinner.setStyle('width', percent + '%');
 460          }
 461      };
 462  
 463      var dnduploader = function(options) {
 464          dnduploader.superclass.constructor.apply(this, arguments);
 465      };
 466  
 467      Y.extend(dnduploader, Y.Base, {
 468          // The URL to send the upload data to.
 469          api: M.cfg.wwwroot+'/repository/repository_ajax.php',
 470          // Options passed into the filemanager/filepicker element.
 471          options: {},
 472          // The function to call when all uploads complete.
 473          callback: null,
 474          // The function to call as the upload progresses
 475          callbackprogress: null,
 476          // The function to call if the upload is cancelled
 477          callbackcancel: null,
 478          // The list of files dropped onto the element.
 479          files: null,
 480          // The ID of the 'upload' repository.
 481          repositoryid: 0,
 482          // Array of files already in the current folder (to check for name clashes).
 483          currentfiles: null,
 484          // Total number of files already uploaded (to check for exceeding limits).
 485          currentfilecount: 0,
 486          // Total size of the files present in the area.
 487          currentareasize: 0,
 488          // The list of files to upload.
 489          uploadqueue: [],
 490          // This list of files with name clashes.
 491          renamequeue: [],
 492          // Size of the current queue.
 493          queuesize: 0,
 494          // Set to true if the user has clicked on 'overwrite all'.
 495          overwriteall: false,
 496          // Set to true if the user has clicked on 'rename all'.
 497          renameall: false,
 498  
 499          /**
 500           * Initialise the settings for the dnduploader
 501           * @param object params - includes:
 502           *                     options (copied from the filepicker / filemanager)
 503           *                     repositoryid - ID of the upload repository
 504           *                     callback - the function to call when uploads are complete
 505           *                     currentfiles - the list of files already in the current folder in the filemanager
 506           *                     currentfilecount - the total files already in the filemanager
 507           *                     files - the list of files to upload
 508           * @return void
 509           */
 510          initializer: function(params) {
 511              this.options = params.options;
 512              this.repositoryid = params.repositoryid;
 513              this.callback = params.callback;
 514              this.callbackprogress = params.callbackprogress;
 515              this.callbackcancel = params.callbackcancel;
 516              this.currentfiles = params.currentfiles;
 517              this.currentfilecount = params.currentfilecount;
 518              this.currentareasize = 0;
 519  
 520              // Retrieve the current size of the area.
 521              for (var i = 0; i < this.currentfiles.length; i++) {
 522                  this.currentareasize += this.currentfiles[i].size;
 523              };
 524  
 525              if (!this.initialise_queue(params.files)) {
 526                  if (this.callbackcancel) {
 527                      this.callbackcancel();
 528                  }
 529              }
 530          },
 531  
 532          /**
 533           * Entry point for starting the upload process (starts by processing any
 534           * renames needed)
 535           */
 536          start_upload: function() {
 537              this.process_renames(); // Automatically calls 'do_upload' once renames complete.
 538          },
 539  
 540          /**
 541           * Display a message in a popup
 542           * @param string msg - the message to display
 543           * @param string type - 'error' or 'info'
 544           */
 545          print_msg: function(msg, type) {
 546              var header = M.util.get_string('error', 'moodle');
 547              if (type != 'error') {
 548                  type = 'info'; // one of only two types excepted
 549                  header = M.util.get_string('info', 'moodle');
 550              }
 551              if (!this.msg_dlg) {
 552                  this.msg_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.message);
 553                  this.msg_dlg_node.generateID();
 554  
 555                  this.msg_dlg = new Y.Panel({
 556                      srcNode      : this.msg_dlg_node,
 557                      zIndex       : 8000,
 558                      centered     : true,
 559                      modal        : true,
 560                      visible      : false,
 561                      render       : true
 562                  });
 563                  this.msg_dlg.plug(Y.Plugin.Drag,{handles:['#'+this.msg_dlg_node.get('id')+' .yui3-widget-hd']});
 564                  this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) {
 565                      e.preventDefault();
 566                      this.msg_dlg.hide();
 567                  }, this);
 568              }
 569  
 570              this.msg_dlg.set('headerContent', header);
 571              this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type)
 572              this.msg_dlg_node.one('.fp-msg-text').setContent(msg);
 573              this.msg_dlg.show();
 574          },
 575  
 576          /**
 577           * Check the size of each file and add to either the uploadqueue or, if there
 578           * is a name clash, the renamequeue
 579           * @param FileList files - the files to upload
 580           * @return void
 581           */
 582          initialise_queue: function(files) {
 583              this.uploadqueue = [];
 584              this.renamequeue = [];
 585              this.queuesize = 0;
 586  
 587              // Loop through the files and find any name clashes with existing files.
 588              var i;
 589              for (i=0; i<files.length; i++) {
 590                  if (this.options.maxbytes > 0 && files[i].size > this.options.maxbytes) {
 591                      // Check filesize before attempting to upload.
 592                      var maxbytesdisplay = this.display_size(this.options.maxbytes);
 593                      this.print_msg(M.util.get_string('maxbytesfile', 'error', {
 594                              file: files[i].name,
 595                              size: maxbytesdisplay
 596                          }), 'error');
 597                      this.uploadqueue = []; // No uploads if one file is too big.
 598                      return;
 599                  }
 600  
 601                  if (this.has_name_clash(files[i].name)) {
 602                      this.renamequeue.push(files[i]);
 603                  } else {
 604                      if (!this.add_to_upload_queue(files[i], files[i].name, false)) {
 605                          return false;
 606                      }
 607                  }
 608                  this.queuesize += files[i].size;
 609              }
 610              return true;
 611          },
 612  
 613          /**
 614           * Generate the display for file size
 615           * @param int size The size to convert to human readable form
 616           * @return string
 617           */
 618          display_size: function(size) {
 619              // This is snippet of code (with some changes) is from the display_size function in moodlelib.
 620              var gb = M.util.get_string('sizegb', 'moodle'),
 621                  mb = M.util.get_string('sizemb', 'moodle'),
 622                  kb = M.util.get_string('sizekb', 'moodle'),
 623                  b  = M.util.get_string('sizeb', 'moodle');
 624  
 625              if (size >= 1073741824) {
 626                  size = Math.round(size / 1073741824 * 10) / 10 + gb;
 627              } else if (size >= 1048576) {
 628                  size = Math.round(size / 1048576 * 10) / 10 + mb;
 629              } else if (size >= 1024) {
 630                  size = Math.round(size / 1024 * 10) / 10 + kb;
 631              } else {
 632                  size = parseInt(size, 10) + ' ' + b;
 633              }
 634  
 635              return size;
 636          },
 637  
 638          /**
 639           * Add a single file to the uploadqueue, whilst checking the maxfiles limit
 640           * @param File file - the file to add
 641           * @param string filename - the name to give the file on upload
 642           * @param bool overwrite - true to overwrite the existing file
 643           * @return bool true if added successfully
 644           */
 645          add_to_upload_queue: function(file, filename, overwrite) {
 646              if (!overwrite) {
 647                  this.currentfilecount++;
 648              }
 649              // The value for "unlimited files" is -1, so 0 should mean 0.
 650              if (this.options.maxfiles >= 0 && this.currentfilecount > this.options.maxfiles) {
 651                  // Too many files - abort entire upload.
 652                  this.uploadqueue = [];
 653                  this.renamequeue = [];
 654                  this.print_msg(M.util.get_string('maxfilesreached', 'moodle', this.options.maxfiles), 'error');
 655                  return false;
 656              }
 657              // The new file will cause the area to reach its limit, we cancel the upload of all files.
 658              // -1 is the value defined by FILE_AREA_MAX_BYTES_UNLIMITED.
 659              if (this.options.areamaxbytes > -1) {
 660                  var sizereached = this.currentareasize + this.queuesize + file.size;
 661                  if (sizereached > this.options.areamaxbytes) {
 662                      this.uploadqueue = [];
 663                      this.renamequeue = [];
 664                      this.print_msg(M.util.get_string('maxareabytesreached', 'moodle'), 'error');
 665                      return false;
 666                  }
 667              }
 668              this.uploadqueue.push({file:file, filename:filename, overwrite:overwrite});
 669              return true;
 670          },
 671  
 672          /**
 673           * Take the next file from the renamequeue and ask the user what to do with
 674           * it. Called recursively until the queue is empty, then calls do_upload.
 675           * @return void
 676           */
 677          process_renames: function() {
 678              if (this.renamequeue.length == 0) {
 679                  // All rename processing complete - start the actual upload.
 680                  this.do_upload();
 681                  return;
 682              }
 683              var multiplefiles = (this.renamequeue.length > 1);
 684  
 685              // Get the next file from the rename queue.
 686              var file = this.renamequeue.shift();
 687              // Generate a non-conflicting name for it.
 688              var newname = this.generate_unique_name(file.name);
 689  
 690              // If the user has clicked on overwrite/rename ALL then process
 691              // this file, as appropriate, then process the rest of the queue.
 692              if (this.overwriteall) {
 693                  this.add_to_upload_queue(file, file.name, true);
 694                  this.process_renames();
 695                  return;
 696              }
 697              if (this.renameall) {
 698                  this.add_to_upload_queue(file, newname, false);
 699                  this.process_renames();
 700                  return;
 701              }
 702  
 703              // Ask the user what to do with this file.
 704              var self = this;
 705  
 706              var process_dlg_node;
 707              if (multiplefiles) {
 708                  process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfilemultiple);
 709              } else {
 710                  process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfile);
 711              }
 712              var node = process_dlg_node;
 713              node.generateID();
 714              var process_dlg = new Y.Panel({
 715                  srcNode      : node,
 716                  headerContent: M.util.get_string('fileexistsdialogheader', 'repository'),
 717                  zIndex       : 8000,
 718                  centered     : true,
 719                  modal        : true,
 720                  visible      : false,
 721                  render       : true,
 722                  buttons      : {}
 723              });
 724              process_dlg.plug(Y.Plugin.Drag,{handles:['#'+node.get('id')+' .yui3-widget-hd']});
 725  
 726              // Overwrite original.
 727              node.one('.fp-dlg-butoverwrite').on('click', function(e) {
 728                  e.preventDefault();
 729                  process_dlg.hide();
 730                  self.add_to_upload_queue(file, file.name, true);
 731                  self.process_renames();
 732              }, this);
 733  
 734              // Rename uploaded file.
 735              node.one('.fp-dlg-butrename').on('click', function(e) {
 736                  e.preventDefault();
 737                  process_dlg.hide();
 738                  self.add_to_upload_queue(file, newname, false);
 739                  self.process_renames();
 740              }, this);
 741  
 742              // Cancel all uploads.
 743              node.one('.fp-dlg-butcancel').on('click', function(e) {
 744                  e.preventDefault();
 745                  process_dlg.hide();
 746                  if (self.callbackcancel) {
 747                      self.callbackcancel();
 748                  }
 749              }, this);
 750  
 751              // When we are at the file limit, only allow 'overwrite', not rename.
 752              if (this.currentfilecount == this.options.maxfiles) {
 753                  node.one('.fp-dlg-butrename').setStyle('display', 'none');
 754                  if (multiplefiles) {
 755                      node.one('.fp-dlg-butrenameall').setStyle('display', 'none');
 756                  }
 757              }
 758  
 759              // If there are more files still to go, offer the 'overwrite/rename all' options.
 760              if (multiplefiles) {
 761                  // Overwrite all original files.
 762                  node.one('.fp-dlg-butoverwriteall').on('click', function(e) {
 763                      e.preventDefault();
 764                      process_dlg.hide();
 765                      this.overwriteall = true;
 766                      self.add_to_upload_queue(file, file.name, true);
 767                      self.process_renames();
 768                  }, this);
 769  
 770                  // Rename all new files.
 771                  node.one('.fp-dlg-butrenameall').on('click', function(e) {
 772                      e.preventDefault();
 773                      process_dlg.hide();
 774                      this.renameall = true;
 775                      self.add_to_upload_queue(file, newname, false);
 776                      self.process_renames();
 777                  }, this);
 778              }
 779              node.one('.fp-dlg-text').setContent(M.util.get_string('fileexists', 'moodle', file.name));
 780              process_dlg_node.one('.fp-dlg-butrename').setContent(M.util.get_string('renameto', 'repository', newname));
 781  
 782              // Destroy the dialog once it has been hidden.
 783              process_dlg.after('visibleChange', function(e) {
 784                  if (!process_dlg.get('visible')) {
 785                      process_dlg.destroy(true);
 786                  }
 787              });
 788  
 789              process_dlg.show();
 790          },
 791  
 792          /**
 793           * Checks if there is already a file with the given name in the current folder
 794           * or in the list of already uploading files
 795           * @param string filename - the name to test
 796           * @return bool true if the name already exists
 797           */
 798          has_name_clash: function(filename) {
 799              // Check against the already uploaded files
 800              var i;
 801              for (i=0; i<this.currentfiles.length; i++) {
 802                  if (filename == this.currentfiles[i].filename) {
 803                      return true;
 804                  }
 805              }
 806              // Check against the uploading files that have already been processed
 807              for (i=0; i<this.uploadqueue.length; i++) {
 808                  if (filename == this.uploadqueue[i].filename) {
 809                      return true;
 810                  }
 811              }
 812              return false;
 813          },
 814  
 815          /**
 816           * Gets a unique file name
 817           *
 818           * @param string filename
 819           * @return string the unique filename generated
 820           */
 821          generate_unique_name: function(filename) {
 822              // Loop through increating numbers until a unique name is found.
 823              while (this.has_name_clash(filename)) {
 824                  filename = increment_filename(filename);
 825              }
 826              return filename;
 827          },
 828  
 829          /**
 830           * Upload the next file from the uploadqueue - called recursively after each
 831           * upload is complete, then handles the callback to the filemanager/filepicker
 832           * @param lastresult - the last result from the server
 833           */
 834          do_upload: function(lastresult) {
 835              if (this.uploadqueue.length > 0) {
 836                  var filedetails = this.uploadqueue.shift();
 837                  this.upload_file(filedetails.file, filedetails.filename, filedetails.overwrite);
 838              } else {
 839                  this.uploadfinished(lastresult);
 840              }
 841          },
 842  
 843          /**
 844           * Run the callback to the filemanager/filepicker
 845           */
 846          uploadfinished: function(lastresult) {
 847              this.callback(lastresult);
 848          },
 849  
 850          /**
 851           * Upload a single file via an AJAX call to the 'upload' repository. Automatically
 852           * calls do_upload as each upload completes.
 853           * @param File file - the file to upload
 854           * @param string filename - the name to give the file
 855           * @param bool overwrite - true if the existing file should be overwritten
 856           */
 857          upload_file: function(file, filename, overwrite) {
 858  
 859              // This would be an ideal place to use the Y.io function
 860              // however, this does not support data encoded using the
 861              // FormData object, which is needed to transfer data from
 862              // the DataTransfer object into an XMLHTTPRequest
 863              // This can be converted when the YUI issue has been integrated:
 864              // http://yuilibrary.com/projects/yui3/ticket/2531274
 865              var xhr = new XMLHttpRequest();
 866              var self = this;
 867  
 868              // Update the progress bar
 869              xhr.upload.addEventListener('progress', function(e) {
 870                  if (e.lengthComputable && self.callbackprogress) {
 871                      var percentage = Math.round((e.loaded * 100) / e.total);
 872                      self.callbackprogress(filename, percentage);
 873                  }
 874              }, false);
 875  
 876              xhr.onreadystatechange = function() { // Process the server response
 877                  if (xhr.readyState == 4) {
 878                      if (xhr.status == 200) {
 879                          var result = JSON.parse(xhr.responseText);
 880                          if (result) {
 881                              if (result.error) {
 882                                  self.print_msg(result.error, 'error'); // TODO add filename?
 883                                  self.uploadfinished();
 884                              } else {
 885                                  // Only update the filepicker if there were no errors
 886                                  if (result.event == 'fileexists') {
 887                                      // Do not worry about this, as we only care about the last
 888                                      // file uploaded, with the filepicker
 889                                      result.file = result.newfile.filename;
 890                                      result.url = result.newfile.url;
 891                                  }
 892                                  result.client_id = self.options.clientid;
 893                                  if (self.callbackprogress) {
 894                                      self.callbackprogress(filename, 100);
 895                                  }
 896                              }
 897                          }
 898                          self.do_upload(result); // continue uploading
 899                      } else {
 900                          self.print_msg(M.util.get_string('serverconnection', 'error'), 'error');
 901                          self.uploadfinished();
 902                      }
 903                  }
 904              };
 905  
 906              // Prepare the data to send
 907              var formdata = new FormData();
 908              formdata.append('repo_upload_file', file); // The FormData class allows us to attach a file
 909              formdata.append('sesskey', M.cfg.sesskey);
 910              formdata.append('repo_id', this.repositoryid);
 911              formdata.append('itemid', this.options.itemid);
 912              if (this.options.author) {
 913                  formdata.append('author', this.options.author);
 914              }
 915              if (this.options.filemanager) { // Filepickers do not have folders
 916                  formdata.append('savepath', this.options.filemanager.currentpath);
 917              }
 918              formdata.append('title', filename);
 919              if (overwrite) {
 920                  formdata.append('overwrite', 1);
 921              }
 922              if (this.options.contextid) {
 923                  formdata.append('ctx_id', this.options.contextid);
 924              }
 925  
 926              // Accepted types can be either a string or an array, but an array is
 927              // expected in the processing script, so make sure we are sending an array
 928              if (this.options.acceptedtypes.constructor == Array) {
 929                  for (var i=0; i<this.options.acceptedtypes.length; i++) {
 930                      formdata.append('accepted_types[]', this.options.acceptedtypes[i]);
 931                  }
 932              } else {
 933                  formdata.append('accepted_types[]', this.options.acceptedtypes);
 934              }
 935  
 936              // Send the file & required details.
 937              var uploadUrl = this.api;
 938              if (uploadUrl.indexOf('?') !== -1) {
 939                  uploadUrl += '&action=upload';
 940              } else {
 941                  uploadUrl += '?action=upload';
 942              }
 943              xhr.open("POST", uploadUrl, true);
 944              xhr.send(formdata);
 945              return true;
 946          }
 947      });
 948  
 949      dnduploadhelper.init(Y, options);
 950  };


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1