{"version":3,"file":"s3multipartupload.min.js","sources":["../src/s3multipartupload.js"],"sourcesContent":["/* jshint ignore:start */\ndefine(['jquery', 'core/log'], function ($, log) {\n\n    \"use strict\"; // jshint ;_;\n\n    log.debug('Filter PoodLL: s3multipartupload initialising');\n\n    return {\n\n        PART_SIZE: 10 * 1024 * 1024, // Minimum part size defined by aws s3 is 5 MB(5 * 1024 * 1024), maximum 5 GB (5 * 1024 * 1024 * 1024)\n        SERVER_LOC: '?', // Location of the server\n        completed: false,\n        file: null,\n        fileInfo: null,\n        sendBackData: null,\n        uploadXHR: [],\n        partURLs: [],\n        // Progress monitoring\n        byterate: [],\n        lastUploadedSize: [],\n        lastUploadedTime: [],\n        loaded: [],\n        total: [],\n\n\n        init: function (file) {\n            this.completed = false;\n            this.file = file;\n            this.fileInfo = {\n                name: this.file.name,\n                type: this.file.type,\n                size: this.file.size,\n                lastModifiedDate: this.file.lastModifiedDate\n            };\n            this.sendBackData = null;\n            this.uploadXHR = [];\n            // Progress monitoring\n            this.byterate = [];\n            this.lastUploadedSize = [];\n            this.lastUploadedTime = [];\n            this.loaded = [];\n            this.total = [];\n\n        },\n\n        /**\n         * Creates the multipart upload\n         */\n        createMultipartUpload: function () {\n\n            //The REST API we are calling\n            var functionname = 'local_cpapi_fetch_multipartupload_details';\n\n            //fetch the Posturl. We need this.\n            //set up our ajax request\n            var xhr = new XMLHttpRequest();\n            var that = this;\n\n            //set up our handler for the response\n            xhr.onreadystatechange = function (e) {\n                if (this.readyState === 4) {\n                    if (xhr.status == 200) {\n\n                        //get a yes or forgetit or tryagain\n                        var payload = xhr.responseText;\n                        var payloadobject = JSON.parse(payload);\n                        if (payloadobject) {\n\n                            //returnCode > 0  indicates an error\n                            if (payloadobject.returnCode > 0) {\n                                //We alert the iframe host that something did not go right\n                                var messageObject = {};\n                                messageObject.id = that.config.id;\n                                messageObject.type = \"error\";\n                                messageObject.code = payloadobject.returnCode;\n                                messageObject.message = payloadobject.returnMessage;\n                                that.config.hermes.postMessage(messageObject);\n                                return;\n                                //if all good, then lets do the upload\n                            } else {\n                                that.partURLs = payloadobject.partURLs;\n                                that.config.allowedURL = payloadobject.allowedURL;\n                                that.config.posturl = payloadobject.postURL;\n                                that.config.filename = payloadobject.filename;\n                                that.config.s3filename = payloadobject.s3filename;\n                                that.config.s3root = payloadobject.s3root;\n                                that.config.cloudfilename = payloadobject.shortfilename;\n                                that.config.cloudroot = payloadobject.shortroot;\n\n                                //do the upload\n                                that.sendAll();\n                            }\n                        } else {\n                            log.debug('error:' + payloadobject.message);\n                        }\n                    } else {\n                        log.debug('Not 200 response:' + xhr.status);\n                    }\n                }\n            };\n\n            //prepare our upload parts\n            var parts = this.prepareParts();\n\n\n            //log.debug(params);\n            var xhrparams = \"wstoken=\" + this.config.wstoken\n                + \"&wsfunction=\" + functionname\n                + \"&moodlewsrestformat=\" + this.config.moodlewsrestformat\n                + \"&mediatype=\" + this.config.mediatype\n                + '&parts=' + parts;\n\n            var serverurl = M.cfg.wwwroot + \"/webservice/rest/server.php\";\n            xhr.open(\"POST\", serverurl, true);\n            xhr.setRequestHeader(\"Cache-Control\", \"no-cache\");\n            xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\n            xhr.send(xhrparams);\n        },\n\n        /**\n         * Creates the multipart upload\n         */\n        doMultipartUpload: function () {\n\n            //The REST API we are calling\n            var functionname = 'local_cpapi_fetch_multipartupload_urls';\n\n            //fetch the Posturl. We need this.\n            //set up our ajax request\n            var xhr = new XMLHttpRequest();\n            var that = this;\n\n            //set up our handler for the response\n            xhr.onreadystatechange = function (e) {\n                if (this.readyState === 4) {\n                    if (xhr.status == 200) {\n\n                        //get a yes or forgetit or tryagain\n                        var payload = xhr.responseText;\n                        var payloadobject = JSON.parse(payload);\n                        if (payloadobject) {\n\n                            //returnCode > 0  indicates an error\n                            if (payloadobject.returnCode > 0) {\n                                //We alert the iframe host that something did not go right\n                                var messageObject = {};\n                                messageObject.id = that.config.id;\n                                messageObject.type = \"error\";\n                                messageObject.code = payloadobject.returnCode;\n                                messageObject.message = payloadobject.returnMessage;\n                                that.config.hermes.postMessage(messageObject);\n                                return;\n                                //if all good, then lets do the upload\n                            } else {\n                                that.partURLs = payloadobject.partURLs;\n                                that.config.allowedURL = payloadobject.allowedURL;\n                                that.config.posturl = payloadobject.postURL;\n                                that.config.filename = payloadobject.filename;\n                                that.config.s3filename = payloadobject.s3filename;\n                                that.config.s3root = payloadobject.s3root;\n                                that.config.cloudfilename = payloadobject.shortfilename;\n                                that.config.cloudroot = payloadobject.shortroot;\n\n                                //do the upload\n                                that.sendAll();\n                            }\n                        } else {\n                            log.debug('error:' + payloadobject.message);\n                        }\n                    } else {\n                        log.debug('Not 200 response:' + xhr.status);\n                    }\n                }\n            };\n\n            //prepare our upload parts\n            var parts = this.prepareParts();\n\n\n            //log.debug(params);\n            var xhrparams = \"wstoken=\" + this.config.wstoken\n                + \"&wsfunction=\" + functionname\n                + \"&moodlewsrestformat=\" + this.config.moodlewsrestformat\n                + \"&mediatype=\" + this.config.mediatype\n                + '&parts=' + parts;\n\n            var serverurl = M.cfg.wwwroot + \"/webservice/rest/server.php\";\n            xhr.open(\"POST\", serverurl, true);\n            xhr.setRequestHeader(\"Cache-Control\", \"no-cache\");\n            xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\n            xhr.send(xhrparams);\n        },\n\n\n/**\n * Call this function to start uploading to server\n */\nstart: function() {\n    this.createMultipartUpload();\n},\n\n/** private */\nprepareParts: function() {\n    var blobs = this.blobs = [];\n    this.partURLs = [];\n    var start = 0;\n    var parts =[];\n    var end, blob;\n    var partNum = 0;\n\n    //create blobs and determine each blob file size\n    while(start < this.file.size) {\n        end = Math.min(start + this.PART_SIZE, this.file.size);\n        filePart = this.file.slice(start, end);\n        // this is to prevent push blob with 0Kb\n        if (filePart.size > 0)\n            blobs.push(filePart);\n        start = this.PART_SIZE * ++partNum;\n    }\n    //return blob details so we can use them when uploading\n    for (var i = 0; i < blobs.length; i++) {\n        blob = blobs[i];\n        parts[i]={};\n        parts[i].partNumber= i+1;\n        parts[i].contentLength = blob.size;\n    }\n    return parts;\n},\n\n\n /*\nuploadParts: function() {\n    var blobs = this.blobs = [], promises = [];\n    var start = 0;\n    var parts =0;\n    var end, blob;\n    var partNum = 0;\n\n    while(start < this.file.size) {\n        end = Math.min(start + this.PART_SIZE, this.file.size);\n        filePart = this.file.slice(start, end);\n        // this is to prevent push blob with 0Kb\n        if (filePart.size > 0)\n            blobs.push(filePart);\n        start = this.PART_SIZE * ++partNum;\n    }\n    //fetch and store the presigned  upload URLs\n    for (var i = 0; i < blobs.length; i++) {\n        blob = blobs[i];\n        promises.push(this.uploadXHR[i]=$.post(this.SERVER_LOC, {\n            command: 'part',\n            sendBackData: this.sendBackData,\n            partNumber: i+1,\n            contentLength: blob.size\n        }));\n    }\n    $.when.apply(null, promises)\n        .then(this.sendAll.bind(this), this.onServerError)\n        .done(this.onPrepareCompleted);\n},\n*/\n\n/**\n * Sends all the created upload parts in a loop\n */\nsendAll: function() {\n    var blobs = this.blobs;\n    var length = blobs.length;\n    for (var i = 0; i < length; i++) {\n        this.sendToS3(partURLs[i], blobs[i], i);\n    }\n},\n\n/**\n * Used to send each uploadPart\n * @param  theurl the upload url\n * @param  blob blob  data bytes\n * @param  int index part index (base zero)\n */\nsendToS3: function(theurl, blob, index) {\n    var self = this;\n    var size = blob.size;\n    var request = self.uploadXHR[index] = new XMLHttpRequest();\n\n    //handle a result from request object\n    request.onreadystatechange = function() {\n        if (request.readyState === 4) { // 4 is DONE\n            // self.uploadXHR[index] = null;\n            if (request.status !== 200) {\n                self.updateProgress();\n                self.onS3UploadError(request);\n                return;\n            }\n            self.updateProgress();\n        }\n    };\n\n    //handle an  on progress event from the upload of the request\n    request.upload.onprogress = function(e) {\n        if (e.lengthComputable) {\n            self.total[index] = size;\n            self.loaded[index] = e.loaded;\n            if (self.lastUploadedTime[index])\n            {\n                var time_diff=(new Date().getTime() - self.lastUploadedTime[index])/1000;\n                if (time_diff > 0.005) // 5 miliseconds has passed\n                {\n                    var byterate=(self.loaded[index] - self.lastUploadedSize[index])/time_diff;\n                    self.byterate[index] = byterate;\n                    self.lastUploadedTime[index]=new Date().getTime();\n                    self.lastUploadedSize[index]=self.loaded[index];\n                }\n            }\n            else\n            {\n                self.byterate[index] = 0;\n                self.lastUploadedTime[index]=new Date().getTime();\n                self.lastUploadedSize[index]=self.loaded[index];\n            }\n            // Only send update to user once, regardless of how many\n            // parallel XHRs we have (unless the first one is over).\n            if (index==0 || self.total[0]==self.loaded[0])\n                self.updateProgress();\n        }\n    };\n    //send the request !!\n    request.open('PUT', theurl, true);\n    request.send(blob);\n\n}, //end of sendToS3\n\n/**\n * Abort multipart upload\n */\ncancel: function() {\n    var self = this;\n    for (var i=0; i<this.uploadXHR.length; ++i) {\n        this.uploadXHR[i].abort();\n    }\n    $.post(self.SERVER_LOC, {\n        command: 'abort',\n        sendBackData: self.sendBackData\n    }).done(function(data) {\n\n    });\n},\n\n/**\n * Complete multipart upload\n */\ncompleteMultipartUpload: function() {\n    var self = this;\n    if (this.completed) return;\n    this.completed=true;\n    $.post(self.SERVER_LOC, {\n        command: 'complete',\n        sendBackData: self.sendBackData\n    }).done(function(data) {\n        self.onUploadCompleted(data);\n    }).fail(function(jqXHR, textStatus, errorThrown) {\n        self.onServerError('complete', jqXHR, textStatus, errorThrown);\n    });\n},\n\n/**\n * Track progress, propagate event, and check for completion\n */\nupdateProgress: function() {\n    var total=0;\n    var loaded=0;\n    var byterate=0.0;\n    var complete=1;\n    for (var i=0; i<this.total.length; ++i) {\n        loaded += +this.loaded[i] || 0;\n        total += this.total[i];\n        if (this.loaded[i]!=this.total[i])\n        {\n            // Only count byterate for active transfers\n            byterate += +this.byterate[i] || 0;\n            complete=0;\n        }\n    }\n    if (complete)\n        this.completeMultipartUpload();\n    total=this.fileInfo.size;\n    this.onProgressChanged(loaded, total, byterate);\n},\n\n// Overridable events:\n\n/**\n * Overrride this function to catch errors occured when communicating to your server\n *\n * @param {type} command Name of the command which failed,one of 'CreateMultipartUpload', 'SignUploadPart','CompleteMultipartUpload'\n * @param {type} jqXHR jQuery XHR\n * @param {type} textStatus resonse text status\n * @param {type} errorThrown the error thrown by the server\n */\nonServerError: function(command, jqXHR, textStatus, errorThrown) {},\n\n/**\n * Overrride this function to catch errors occured when uploading to S3\n *\n * @param XMLHttpRequest xhr the XMLHttpRequest object\n */\nonS3UploadError: function(xhr) {},\n\n/**\n * Override this function to show user update progress\n *\n * @param {type} uploadedSize is the total uploaded bytes\n * @param {type} totalSize the total size of the uploading file\n * @param {type} speed bytes per second\n */\nonProgressChanged: function(uploadedSize, totalSize, bitrate) {},\n\n/**\n * Override this method to execute something when upload finishes\n *\n */\nonUploadCompleted: function(serverData) {},\n/**\n * Override this method to execute something when part preparation is completed\n *\n */\nonPrepareCompleted: function() {}\n\n};//end of return object\n});//utterly the end"],"names":["define","$","log","debug","PART_SIZE","SERVER_LOC","completed","file","fileInfo","sendBackData","uploadXHR","partURLs","byterate","lastUploadedSize","lastUploadedTime","loaded","total","init","name","this","type","size","lastModifiedDate","createMultipartUpload","xhr","XMLHttpRequest","that","onreadystatechange","e","readyState","status","payload","responseText","payloadobject","JSON","parse","returnCode","messageObject","id","config","code","message","returnMessage","hermes","postMessage","allowedURL","posturl","postURL","filename","s3filename","s3root","cloudfilename","shortfilename","cloudroot","shortroot","sendAll","parts","prepareParts","xhrparams","wstoken","moodlewsrestformat","mediatype","serverurl","M","cfg","wwwroot","open","setRequestHeader","send","doMultipartUpload","start","blobs","end","blob","partNum","Math","min","filePart","slice","push","i","length","partNumber","contentLength","sendToS3","theurl","index","self","request","updateProgress","onS3UploadError","upload","onprogress","lengthComputable","time_diff","Date","getTime","cancel","abort","post","command","done","data","completeMultipartUpload","onUploadCompleted","fail","jqXHR","textStatus","errorThrown","onServerError","complete","onProgressChanged","uploadedSize","totalSize","bitrate","serverData","onPrepareCompleted"],"mappings":"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"}