From fe4586d26badaa52f3cde61002b281b3e8da1788 Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 23 Jul 2014 13:57:22 -0400 Subject: [PATCH 001/344] Updated s3's util.js to allow unprefixed headers Add support for S3 uploading with valid unprefixed headers like Cache-Control or Content-Disposition. Removes requirement for monkey-patching qq.s3.util internals. --- client/js/s3/util.js | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/client/js/s3/util.js b/client/js/s3/util.js index 6a9bee468..051511c2a 100644 --- a/client/js/s3/util.js +++ b/client/js/s3/util.js @@ -52,6 +52,35 @@ qq.s3.util = qq.s3.util || (function() { return bucket; }, + + /** Create Prefixed request headers which are appropriate for S3. + * + * If the request header is appropriate for S3 (e.g. Cache-Control) then pass + * it along without a metadata prefix. For all other request header parameter names, + * apply qq.s3.util.AWS_PARAM_PREFIX before the name. + * + * @param name Name of the Request Header parameter to construct a (possibly) prefixed name. + * @returns {String} A valid Request Header parameter name. + */ + _getPrefixedParamName: function (name) { + switch (name) + { + // + // Valid request headers (not sent by fine-uploader) which should be returned as-is (case-sensitive) + // see: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html + // + case "Cache-Control": + case "Content-Disposition": + case "Content-Encoding": + case "Content-MD5": // CAW: Content-MD5 might not be appropriate from user-land + case "x-amz-server-side-encryption-customer-algorithm": + case "x-amz-server-side-encryption-customer-key": + case "x-amz-server-side-encryption-customer-key-MD5": + return name; + default: + return qq.s3.util.AWS_PARAM_PREFIX + name; + } + }, /** * Create a policy document to be signed and sent along with the S3 upload request. @@ -112,7 +141,7 @@ qq.s3.util = qq.s3.util || (function() { // user metadata qq.each(params, function(name, val) { - var awsParamName = qq.s3.util.AWS_PARAM_PREFIX + name, + var awsParamName = qq.s3.util._getPrefixedParamName(name), param = {}; param[awsParamName] = encodeURIComponent(val); @@ -155,7 +184,7 @@ qq.s3.util = qq.s3.util || (function() { * Generates all parameters to be passed along with the S3 upload request. This includes invoking a callback * that is expected to asynchronously retrieve a signature for the policy document. Note that the server * signing the request should reject a "tainted" policy document that includes unexpected values, since it is - * still possible for a malicious user to tamper with these values during policy document generation, b + * still possible for a malicious user to tamper with these values during policy document generation, * before it is sent to the server for signing. * * @param spec Object with properties: `params`, `type`, `key`, `accessKey`, `acl`, `expectedStatus`, `successRedirectUrl`, @@ -208,9 +237,10 @@ qq.s3.util = qq.s3.util || (function() { awsParams.acl = acl; // Custom (user-supplied) params must be prefixed with the value of `qq.s3.util.AWS_PARAM_PREFIX`. - // Custom param values will be URI encoded as well. + // Params such as Cache-Control or Content-Disposition will not be prefixed. + // All param values will be URI encoded as well. qq.each(customParams, function(name, val) { - var awsParamName = qq.s3.util.AWS_PARAM_PREFIX + name; + var awsParamName = qq.s3.util._getPrefixedParamName(name); awsParams[awsParamName] = encodeURIComponent(val); }); From 75b201f1f40439b0c19605b5d4a2bd2c81521a15 Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 23 Jul 2014 15:44:52 -0400 Subject: [PATCH 002/344] Fixed jslint note about trailing whitespace --- client/js/s3/util.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/s3/util.js b/client/js/s3/util.js index 051511c2a..b0e162c81 100644 --- a/client/js/s3/util.js +++ b/client/js/s3/util.js @@ -70,8 +70,8 @@ qq.s3.util = qq.s3.util || (function() { // see: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html // case "Cache-Control": - case "Content-Disposition": - case "Content-Encoding": + case "Content-Disposition": + case "Content-Encoding": case "Content-MD5": // CAW: Content-MD5 might not be appropriate from user-land case "x-amz-server-side-encryption-customer-algorithm": case "x-amz-server-side-encryption-customer-key": From 1efe8ca30ef277e0917814840208abce58fdd1e2 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 22 Apr 2015 14:27:25 -0500 Subject: [PATCH 003/344] chore(build): start of 5.3.0 development --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e5df0b884..eb7e8ad5a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.2.1 +Version: 5.3.0-1 [![Build Status](https://travis-ci.org/FineUploader/fine-uploader.png?branch=master)](https://travis-ci.org/FineUploader/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index e55997529..7d7bc3282 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.2.1", + "version": "5.3.0-1", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index 69708963a..f3a9cdf97 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.2.1"; +qq.version = "5.3.0-1"; diff --git a/package.json b/package.json index 7377b091d..4689aceb8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fine-uploader", "title": "Fine Uploader", - "version": "5.2.1", + "version": "5.3.0-1", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ From a7ee94fc2fd1801ce2553b0158df972d2898ebfc Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 22 Apr 2015 14:30:18 -0500 Subject: [PATCH 004/344] feat(s3): start of S3 v4 signature support Add version config option. #1336 --- client/js/s3/uploader.basic.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/js/s3/uploader.basic.js b/client/js/s3/uploader.basic.js index 26797225a..ae77663a1 100644 --- a/client/js/s3/uploader.basic.js +++ b/client/js/s3/uploader.basic.js @@ -42,10 +42,11 @@ sessionToken: null }, - // optional/ignored if `credentials` is provided + // All but `version` are ignored if `credentials` is provided. signature: { + customHeaders: {}, endpoint: null, - customHeaders: {} + version: 2 }, uploadSuccess: { From 960e6e360b6febcb164ab7622a3f7957dd6cb133 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 28 Apr 2015 15:49:21 -0500 Subject: [PATCH 005/344] feat(v4-signatures): V4 signature support for POST uploads Includes changes to development S3 PHP signature server. These changes will need to be rolled into the examples in the server-examples repo as well before release. In addition to writing a v4 test, I also cleaned up some tests in the simple-uploads spec by removing the flaky assertion-counting logic. Doc updates to S3 options are included. #1336 --- client/js/s3/s3.form.upload.handler.js | 3 + client/js/s3/s3.xhr.upload.handler.js | 3 + client/js/s3/uploader.basic.js | 3 + client/js/s3/util.js | 122 ++++++++++++++++++---- docs/api/options-s3.jmd | 2 + test/dev/handlers/s3/s3.php | 55 +++++++--- test/unit/s3/simple-file-uploads.js | 135 ++++++++++++++++++------- 7 files changed, 251 insertions(+), 72 deletions(-) diff --git a/client/js/s3/s3.form.upload.handler.js b/client/js/s3/s3.form.upload.handler.js index 0be97c334..d385fb852 100644 --- a/client/js/s3/s3.form.upload.handler.js +++ b/client/js/s3/s3.form.upload.handler.js @@ -22,6 +22,7 @@ qq.s3.FormUploadHandler = function(options, proxy) { endpointStore = options.endpointStore, aclStore = options.aclStore, reducedRedundancy = options.objectProperties.reducedRedundancy, + region = options.objectProperties.region, serverSideEncryption = options.objectProperties.serverSideEncryption, validation = options.validation, signature = options.signature, @@ -95,7 +96,9 @@ qq.s3.FormUploadHandler = function(options, proxy) { maxFileSize: validation.maxSizeLimit, successRedirectUrl: successRedirectUrl, reducedRedundancy: reducedRedundancy, + region: region, serverSideEncryption: serverSideEncryption, + signatureVersion: signature.version, log: log }, qq.bind(getSignatureAjaxRequester.getSignature, this, id)); diff --git a/client/js/s3/s3.xhr.upload.handler.js b/client/js/s3/s3.xhr.upload.handler.js index e49cba512..1754d33ed 100644 --- a/client/js/s3/s3.xhr.upload.handler.js +++ b/client/js/s3/s3.xhr.upload.handler.js @@ -21,6 +21,7 @@ qq.s3.XhrUploadHandler = function(spec, proxy) { endpointStore = spec.endpointStore, aclStore = spec.aclStore, reducedRedundancy = spec.objectProperties.reducedRedundancy, + region = spec.objectProperties.region, serverSideEncryption = spec.objectProperties.serverSideEncryption, validation = spec.validation, signature = spec.signature, @@ -273,7 +274,9 @@ qq.s3.XhrUploadHandler = function(spec, proxy) { minFileSize: validation.minSizeLimit, maxFileSize: validation.maxSizeLimit, reducedRedundancy: reducedRedundancy, + region: region, serverSideEncryption: serverSideEncryption, + signatureVersion: signature.version, log: log }, qq.bind(requesters.policySignature.getSignature, this, id)); diff --git a/client/js/s3/uploader.basic.js b/client/js/s3/uploader.basic.js index ae77663a1..1ca3294ea 100644 --- a/client/js/s3/uploader.basic.js +++ b/client/js/s3/uploader.basic.js @@ -27,6 +27,9 @@ reducedRedundancy: false, + // Defined at http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region + region: "us-east-1", + serverSideEncryption: false }, diff --git a/client/js/s3/util.js b/client/js/s3/util.js index 62fbefb84..89bffc9c9 100644 --- a/client/js/s3/util.js +++ b/client/js/s3/util.js @@ -5,9 +5,13 @@ qq.s3.util = qq.s3.util || (function() { "use strict"; return { + ALGORITHM_PARAM_NAME: "x-amz-algorithm", + AWS_PARAM_PREFIX: "x-amz-meta-", - SESSION_TOKEN_PARAM_NAME: "x-amz-security-token", + CREDENTIAL_PARAM_NAME: "x-amz-credential", + + DATE_PARAM_NAME: "x-amz-date", REDUCED_REDUNDANCY_PARAM_NAME: "x-amz-storage-class", REDUCED_REDUNDANCY_PARAM_VALUE: "REDUCED_REDUNDANCY", @@ -15,6 +19,12 @@ qq.s3.util = qq.s3.util || (function() { SERVER_SIDE_ENCRYPTION_PARAM_NAME: "x-amz-server-side-encryption", SERVER_SIDE_ENCRYPTION_PARAM_VALUE: "AES256", + SESSION_TOKEN_PARAM_NAME: "x-amz-security-token", + + V4_ALGORITHM_PARAM_VALUE: "AWS4-HMAC-SHA256", + + V4_SIGNATURE_PARAM_NAME: "x-amz-signature", + /** * This allows for the region to be specified in the bucket's endpoint URL, or not. * @@ -63,10 +73,11 @@ qq.s3.util = qq.s3.util || (function() { var policy = {}, conditions = [], bucket = spec.bucket, + date = spec.date, key = spec.key, + accessKey = spec.accessKey, acl = spec.acl, type = spec.type, - expirationDate = new Date(), expectedStatus = spec.expectedStatus, sessionToken = spec.sessionToken, params = spec.params, @@ -74,9 +85,11 @@ qq.s3.util = qq.s3.util || (function() { minFileSize = spec.minFileSize, maxFileSize = spec.maxFileSize, reducedRedundancy = spec.reducedRedundancy, - serverSideEncryption = spec.serverSideEncryption; + region = spec.region, + serverSideEncryption = spec.serverSideEncryption, + signatureVersion = spec.signatureVersion; - policy.expiration = qq.s3.util.getPolicyExpirationDate(expirationDate); + policy.expiration = qq.s3.util.getPolicyExpirationDate(date); conditions.push({acl: acl}); conditions.push({bucket: bucket}); @@ -109,7 +122,24 @@ qq.s3.util = qq.s3.util || (function() { conditions[conditions.length - 1][qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE; } - conditions.push({key: key}); + if (signatureVersion === 2) { + conditions.push({key: key}); + } + else if (signatureVersion === 4) { + conditions.push({}); + conditions[conditions.length - 1][qq.s3.util.ALGORITHM_PARAM_NAME] = qq.s3.util.V4_ALGORITHM_PARAM_VALUE; + + conditions.push({}); + conditions[conditions.length - 1].key = key; + + conditions.push({}); + conditions[conditions.length - 1][qq.s3.util.CREDENTIAL_PARAM_NAME] = + qq.s3.util.getV4CredentialsString({date: date, key: accessKey, region: region}); + + conditions.push({}); + conditions[conditions.length - 1][qq.s3.util.DATE_PARAM_NAME] = + qq.s3.util.getV4PolicyDate(date); + } // user metadata qq.each(params, function(name, val) { @@ -160,14 +190,13 @@ qq.s3.util = qq.s3.util || (function() { * before it is sent to the server for signing. * * @param spec Object with properties: `params`, `type`, `key`, `accessKey`, `acl`, `expectedStatus`, `successRedirectUrl`, - * `reducedRedundancy`, serverSideEncryption, and `log()`, along with any options associated with `qq.s3.util.getPolicy()`. + * `reducedRedundancy`, `region`, `serverSideEncryption`, `version`, and `log()`, along with any options associated with `qq.s3.util.getPolicy()`. * @returns {qq.Promise} Promise that will be fulfilled once all parameters have been determined. */ generateAwsParams: function(spec, signPolicyCallback) { var awsParams = {}, customParams = spec.params, promise = new qq.Promise(), - policyJson = qq.s3.util.getPolicy(spec), sessionToken = spec.sessionToken, type = spec.type, key = spec.key, @@ -176,11 +205,17 @@ qq.s3.util = qq.s3.util || (function() { expectedStatus = spec.expectedStatus, successRedirectUrl = qq.s3.util.getSuccessRedirectAbsoluteUrl(spec.successRedirectUrl), reducedRedundancy = spec.reducedRedundancy, + region = spec.region, serverSideEncryption = spec.serverSideEncryption, - log = spec.log; + signatureVersion = spec.signatureVersion, + now = new Date(), + log = spec.log, + policyJson; + + spec.date = now; + policyJson = qq.s3.util.getPolicy(spec); awsParams.key = key; - awsParams.AWSAccessKeyId = accessKey; if (type) { awsParams["Content-Type"] = type; @@ -215,16 +250,32 @@ qq.s3.util = qq.s3.util || (function() { awsParams[awsParamName] = encodeURIComponent(val); }); + if (signatureVersion === 2) { + awsParams.AWSAccessKeyId = accessKey; + } + else if (signatureVersion === 4) { + awsParams[qq.s3.util.ALGORITHM_PARAM_NAME] = qq.s3.util.V4_ALGORITHM_PARAM_VALUE; + awsParams[qq.s3.util.CREDENTIAL_PARAM_NAME] = qq.s3.util.getV4CredentialsString({date: now, key: accessKey, region: region}); + awsParams[qq.s3.util.DATE_PARAM_NAME] = qq.s3.util.getV4PolicyDate(now); + } + // Invoke a promissory callback that should provide us with a base64-encoded policy doc and an // HMAC signature for the policy doc. signPolicyCallback(policyJson).then( function(policyAndSignature, updatedAccessKey, updatedSessionToken) { awsParams.policy = policyAndSignature.policy; - awsParams.signature = policyAndSignature.signature; - if (updatedAccessKey) { - awsParams.AWSAccessKeyId = updatedAccessKey; + if (spec.signatureVersion === 2) { + awsParams.signature = policyAndSignature.signature; + + if (updatedAccessKey) { + awsParams.AWSAccessKeyId = updatedAccessKey; + } + } + else if (spec.signatureVersion === 4) { + awsParams[qq.s3.util.V4_SIGNATURE_PARAM_NAME] = policyAndSignature.signature; } + if (updatedSessionToken) { awsParams[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = updatedSessionToken; } @@ -263,16 +314,29 @@ qq.s3.util = qq.s3.util || (function() { }, getPolicyExpirationDate: function(date) { + return qq.s3.util.getPolicyDate(date, 5); + }, + + getCredentialsDate: function(date) { + return date.getFullYear() + "" + + (date.getMonth() + 1) + "" + + date.getDate(); + }, + + getPolicyDate: function(date, _minutesToAdd_) { + var minutesToAdd = _minutesToAdd_ || 0, + pad, r; + /*jshint -W014 */ // Is this going to be a problem if we encounter this moments before 2 AM just before daylight savings time ends? - date.setMinutes(date.getMinutes() + 5); + date.setMinutes(date.getMinutes() + (minutesToAdd || 0)); if (Date.prototype.toISOString) { return date.toISOString(); } else { - var pad = function(number) { - var r = String(number); + pad = function(number) { + r = String(number); if (r.length === 1) { r = "0" + r; @@ -282,13 +346,13 @@ qq.s3.util = qq.s3.util || (function() { }; return date.getUTCFullYear() - + "-" + pad(date.getUTCMonth() + 1) - + "-" + pad(date.getUTCDate()) - + "T" + pad(date.getUTCHours()) - + ":" + pad(date.getUTCMinutes()) - + ":" + pad(date.getUTCSeconds()) - + "." + String((date.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5) - + "Z"; + + "-" + pad(date.getUTCMonth() + 1) + + "-" + pad(date.getUTCDate()) + + "T" + pad(date.getUTCHours()) + + ":" + pad(date.getUTCMinutes()) + + ":" + pad(date.getUTCSeconds()) + + "." + String((date.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5) + + "Z"; } }, @@ -343,6 +407,20 @@ qq.s3.util = qq.s3.util || (function() { } }, + getV4CredentialsString: function(spec) { + return spec.key + "/" + + qq.s3.util.getCredentialsDate(spec.date) + "/" + + spec.region + "/s3/aws4_request"; + }, + + getV4PolicyDate: function(date) { + return qq.s3.util.getCredentialsDate(date) + "T" + + date.getHours() + "" + + date.getMinutes() + "" + + date.getSeconds() + + "Z"; + }, + // AWS employs a strict interpretation of [RFC 3986](http://tools.ietf.org/html/rfc3986#page-12). // So, we must ensure all reserved characters listed in the spec are percent-encoded, // and spaces are replaced with "+". diff --git a/docs/api/options-s3.jmd b/docs/api/options-s3.jmd index 9e5cc1465..45c56ca9b 100644 --- a/docs/api/options-s3.jmd +++ b/docs/api/options-s3.jmd @@ -71,6 +71,7 @@ alert("The `chunking.paramNames` option does **not** apply to S3.") ("objectProperties.bucket", "bucket", "Describes the name of the bucket used to house the file in S3. This is required if the bucket cannot be determined by examining the endpoint (such as if you are using a CDN as an endpoint). Possible values are a string representing the bucket name, or a function. If the value is a function, Fine Uploader S3 will pass the associated file ID as a parameter when invoking your function. If the value is a function it may return one of a promise or a `String`.", "String or Function", "(assumes the bucket can be determined by parsing the endpoint string)",), ("objectProperties.key", "key", "Describes the object key used to identify the file in your S3 bucket. Possible values are 'uuid', 'filename' or a function. If the value is a function, Fine Uploader S3 will pass the associated file ID as a parameter when invoking your function. If the value is a function it may return one of a promise or a `String`.", "String or Function", "uuid",), ("objectProperties.reducedRedundancy", "reducedRedundancy", "Set this to true if you would like to use the reduced redundancy storage class for all objects uploaded to S3.", "Boolean", "false",), + ("objectProperties.region", "region", "Version 4 signatures only: The region identifier for the target bucket. These are defined at http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region.", "String", "us-east-1",), ("objectProperties.serverSideEncryption", "serverSideEncryption", "Set this to true if you would like all uploaded files to be encrypted by AWS.", "Boolean", "false",), ) )}} @@ -95,6 +96,7 @@ alert("`request.customHeaders` does **not** apply to S3.") ( ("signature.customHeaders", "customHeaders", "Additional headers sent along with each signature request. If you declare a function as the value, the associated file's ID will be passed to your function when it is invoked.", "Object, Function", "{}",), ("signature.endpoint", "endpoint", "The endpoint that Fine Uploader can use to send policy documents (HTML form uploads) or other strings to sign (REST requests) before sending requests off to S3.", "String", "null",), + ("signature.version", "version", "The AWS/S3 signature version to use. Currently supported values are `2` and `4`.", "Integer", "2",), ) )}} diff --git a/test/dev/handlers/s3/s3.php b/test/dev/handlers/s3/s3.php index 86e5eaf8d..5969be23a 100644 --- a/test/dev/handlers/s3/s3.php +++ b/test/dev/handlers/s3/s3.php @@ -69,8 +69,8 @@ // DELETE, in a "_method" parameter. function getRequestMethod() { - if ($_POST['_method'] != null) { - return $_POST['_method']; + if ($_REQUEST['_method'] != null) { + return $_REQUEST['_method']; } return $_SERVER['REQUEST_METHOD']; @@ -88,8 +88,8 @@ function getS3Client() { // Only needed if the delete file feature is enabled function deleteObject() { getS3Client()->deleteObject(array( - 'Bucket' => $_POST['bucket'], - 'Key' => $_POST['key'] + 'Bucket' => $_REQUEST['bucket'], + 'Key' => $_REQUEST['key'] )); } @@ -133,8 +133,15 @@ function signPolicy($policyStr) { if (isPolicyValid($policyObj)) { $encodedPolicy = base64_encode($policyStr); - $response = array('policy' => $encodedPolicy, 'signature' => sign($encodedPolicy)); + + if (isset($_REQUEST["v4"])) { + $response = array('policy' => $encodedPolicy, 'signature' => signV4Policy($encodedPolicy, $policyObj)); + } + else { + $response = array('policy' => $encodedPolicy, 'signature' => sign($encodedPolicy)); + } echo json_encode($response); + } else { echo json_encode(array("invalid" => true)); @@ -166,19 +173,39 @@ function sign($stringToSign) { global $clientPrivateKey; return base64_encode(hash_hmac( - 'sha1', - $stringToSign, - $clientPrivateKey, - true - )); + 'sha1', + $stringToSign, + $clientPrivateKey, + true + )); +} + +function signV4Policy($stringToSign, $policyObj) { + global $clientPrivateKey; + + foreach ($policyObj["conditions"] as $condition) { + if (isset($condition["x-amz-credential"])) { + $credentialCondition = $condition["x-amz-credential"]; + } + } + + $pattern = "/.+\/(.+)\\/(.+)\/s3\/aws4_request/"; + preg_match($pattern, $credentialCondition, $matches); + + $dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true); + $dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true); + $dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true); + $signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true); + + return hash_hmac('sha256', $stringToSign, $signingKey); } // This is not needed if you don't require a callback on upload success. function verifyFileInS3($includeThumbnail) { global $expectedMaxSize; - $bucket = $_POST["bucket"]; - $key = $_POST["key"]; + $bucket = $_REQUEST["bucket"]; + $key = $_REQUEST["key"]; // If utilizing CORS, we return a 200 response with the error message in the body // to ensure Fine Uploader can parse the error message in IE9 and IE8, @@ -235,8 +262,8 @@ function isFileViewableImage($filename) { // (which is our goal here - keep it simple) we only include a link to // a viewable image and only if the browser is not capable of generating a client-side preview. function shouldIncludeThumbnail() { - $filename = $_POST["name"]; - $isPreviewCapable = $_POST["isBrowserPreviewCapable"] == "true"; + $filename = $_REQUEST["name"]; + $isPreviewCapable = $_REQUEST["isBrowserPreviewCapable"] == "true"; $isFileViewableImage = isFileViewableImage($filename); return !$isPreviewCapable && $isFileViewableImage; diff --git a/test/unit/s3/simple-file-uploads.js b/test/unit/s3/simple-file-uploads.js index 1991b13a8..a5e56b945 100644 --- a/test/unit/s3/simple-file-uploads.js +++ b/test/unit/s3/simple-file-uploads.js @@ -12,9 +12,13 @@ if (qqtest.canDownloadFileAsBlob) { accessKey: testAccessKey, endpoint: testS3Endpoint }, - typicalSignatureOption = { + v2SignatureOption = { endpoint: testSignatureEndoint }, + v4SignatureOption = { + endpoint: testSignatureEndoint, + version: 4 + }, startTypicalTest = function(uploader, callback) { qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function (blob) { var signatureRequest, uploadRequest, policyDoc, @@ -40,12 +44,68 @@ if (qqtest.canDownloadFileAsBlob) { }); }; - it("test most basic upload w/ signature request", function(done) { - assert.expect(24, done); + describe("v4 signatures", function() { + it("test most basic upload w/ signature request", function(done) { + var uploader = new qq.s3.FineUploaderBasic({ + request: typicalRequestOption, + signature: v4SignatureOption + } + ); + + startTypicalTest(uploader, function(signatureRequest, policyDoc, uploadRequest, conditions) { + var uploadRequestParams, + now = new Date(), + policyDate; + + assert.equal(signatureRequest.method, "POST"); + assert.equal(signatureRequest.url, testSignatureEndoint); + assert.equal(signatureRequest.requestHeaders["Content-Type"].indexOf("application/json;"), 0); + + assert.ok(new Date(policyDoc.expiration).getTime() > Date.now()); + assert.equal(policyDoc.conditions.length, 9); + + assert.equal(conditions.acl, "private"); + assert.equal(conditions.bucket, testBucketName); + assert.equal(conditions["Content-Type"], "image/jpeg"); + assert.equal(conditions.success_action_status, 200); + assert.equal(conditions["x-amz-algorithm"], "AWS4-HMAC-SHA256"); + assert.equal(conditions.key, uploader.getKey(0)); + assert.equal(conditions.key, uploader.getUuid(0) + ".jpg"); + assert.equal(conditions["x-amz-credential"], testAccessKey + "/" + now.getFullYear() + (now.getMonth() + 1) + now.getDate() + "/us-east-1/s3/aws4_request"); + policyDate = conditions["x-amz-date"]; + assert.ok(policyDate); + assert.equal(conditions["x-amz-meta-qqfilename"], "test.jpg"); + + signatureRequest.respond(200, null, JSON.stringify({policy: "thepolicy", signature: "thesignature"})); + uploadRequestParams = uploadRequest.requestBody.fields; + + assert.equal(uploadRequest.url, testS3Endpoint); + assert.equal(uploadRequest.method, "POST"); + + assert.equal(uploadRequestParams.key, uploader.getUuid(0) + ".jpg"); + assert.equal(uploadRequestParams["Content-Type"], "image/jpeg"); + assert.equal(uploadRequestParams.success_action_status, 200); + assert.equal(uploadRequestParams.acl, "private"); + assert.equal(uploadRequestParams["x-amz-meta-qqfilename"], "test.jpg"); + assert.equal(uploadRequestParams["x-amz-algorithm"], "AWS4-HMAC-SHA256"); + assert.equal(uploadRequestParams["x-amz-credential"], testAccessKey + "/" + now.getFullYear() + (now.getMonth() + 1) + now.getDate() + "/us-east-1/s3/aws4_request"); + assert.equal(uploadRequestParams["x-amz-date"], policyDate); + + assert.ok(uploadRequestParams.file); + + assert.equal(uploadRequestParams["x-amz-signature"], "thesignature"); + assert.equal(uploadRequestParams.policy, "thepolicy"); + + done(); + }); + }); + }); + + it("test most basic upload w/ signature request", function(done) { var uploader = new qq.s3.FineUploaderBasic({ request: typicalRequestOption, - signature: typicalSignatureOption + signature: v2SignatureOption } ); @@ -84,15 +144,15 @@ if (qqtest.canDownloadFileAsBlob) { assert.equal(uploadRequestParams.signature, "thesignature"); assert.equal(uploadRequestParams.policy, "thepolicy"); + + done(); }); }); it("converts all parameters (metadata) to lower case before sending them to S3", function(done) { - assert.expect(5, done); - var uploader = new qq.s3.FineUploaderBasic({ request: typicalRequestOption, - signature: typicalSignatureOption + signature: v2SignatureOption } ); @@ -114,15 +174,15 @@ if (qqtest.canDownloadFileAsBlob) { assert.equal(uploadRequestParams["x-amz-meta-mixedcase"], "value"); assert.equal(uploadRequestParams["x-amz-meta-mixedcasefunc"], "value2"); + + done(); }); }); it("respects the objectProperties.key option w/ a value of 'filename'", function(done) { - assert.expect(5, done); - var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { key: "filename" } @@ -140,16 +200,16 @@ if (qqtest.canDownloadFileAsBlob) { uploadRequestParams = uploadRequest.requestBody.fields; assert.equal(uploadRequestParams["x-amz-meta-qqfilename"], "test.jpg"); + + done(); }); }); it("respects the objectProperties.key option w/ a custom key generation function", function(done) { - assert.expect(5, done); - var customKeyPrefix = "testcustomkey_", uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { key: function(id) { return customKeyPrefix + this.getName(id); @@ -168,6 +228,8 @@ if (qqtest.canDownloadFileAsBlob) { uploadRequestParams = uploadRequest.requestBody.fields; assert.equal(uploadRequestParams["x-amz-meta-qqfilename"], "test.jpg"); + + done(); }); }); @@ -177,7 +239,7 @@ if (qqtest.canDownloadFileAsBlob) { function runTest(keyFunc, done) { var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { key: keyFunc } @@ -222,7 +284,7 @@ if (qqtest.canDownloadFileAsBlob) { function runTest(keyFunc, done) { var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { key: keyFunc } @@ -259,7 +321,7 @@ if (qqtest.canDownloadFileAsBlob) { function runTest(keyFunc, done) { var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { key: keyFunc }, @@ -298,11 +360,9 @@ if (qqtest.canDownloadFileAsBlob) { }); it("respects the objectProperties.acl option w/ a custom value set via option", function(done) { - assert.expect(3, done); - var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { acl: "public-read" } @@ -317,15 +377,15 @@ if (qqtest.canDownloadFileAsBlob) { uploadRequestParams = uploadRequest.requestBody.fields; assert.equal(uploadRequestParams.acl, "public-read"); + + done(); }); }); it("respects the objectProperties.acl option w/ a custom value set via API", function(done) { - assert.expect(3, done); - var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { acl: "public-read" } @@ -342,15 +402,15 @@ if (qqtest.canDownloadFileAsBlob) { uploadRequestParams = uploadRequest.requestBody.fields; assert.equal(uploadRequestParams.acl, "test-acl"); + + done(); }); }); it("respects the objectProperties.reducedRedundancy option w/ a value of true", function(done) { - assert.expect(3, done); - var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { reducedRedundancy: true } @@ -365,15 +425,15 @@ if (qqtest.canDownloadFileAsBlob) { uploadRequestParams = uploadRequest.requestBody.fields; assert.equal(uploadRequestParams[qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME], qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE); + + done(); }); }); it("respects the objectProperties.serverSideEncryption option w/ a value of true", function(done) { - assert.expect(3, done); - var uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, objectProperties: { serverSideEncryption: true } @@ -388,14 +448,14 @@ if (qqtest.canDownloadFileAsBlob) { uploadRequestParams = uploadRequest.requestBody.fields; assert.equal(uploadRequestParams[qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME], qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE); + + done(); }); }); it("respects custom headers to be sent with signature request", function(done) { - assert.expect(2, done); - var customHeader = {"test-header-name": "test-header-value"}, - customSignatureOptions = qq.extend({}, typicalSignatureOption), + customSignatureOptions = qq.extend({}, v2SignatureOption), uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, signature: qq.extend(customSignatureOptions, {customHeaders: customHeader}) @@ -404,6 +464,8 @@ if (qqtest.canDownloadFileAsBlob) { startTypicalTest(uploader, function(signatureRequest, policyDoc, uploadRequest, conditions) { assert.equal(signatureRequest.requestHeaders["test-header-name"], customHeader["test-header-name"]); + + done(); }); }); @@ -413,7 +475,7 @@ if (qqtest.canDownloadFileAsBlob) { uploadSuccessHeaders = {"test-header-name": "test-header-value"}, uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, uploadSuccess: { endpoint: "foo/bar", params: uploadSuccessParams, @@ -454,12 +516,10 @@ if (qqtest.canDownloadFileAsBlob) { }); it("Declares an upload as a failure if uploadSuccess response indicates a problem with the file. Also tests uploadSuccessRequest endpoint option.", function(done) { - assert.expect(3, done); - var uploadSuccessUrl = "/upload/success", uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, uploadSuccess: { endpoint: uploadSuccessUrl } @@ -476,6 +536,8 @@ if (qqtest.canDownloadFileAsBlob) { assert.equal(uploadSuccessRequest.url, uploadSuccessUrl); uploadSuccessRequest.respond(200, null, JSON.stringify({success: false})); assert.equal(uploader.getUploads()[0].status, qq.status.UPLOAD_FAILED); + + done(); }); }); @@ -483,7 +545,7 @@ if (qqtest.canDownloadFileAsBlob) { var uploadSuccessUrl = "/upload/success", uploader = new qq.s3.FineUploaderBasic({ request:typicalRequestOption, - signature: typicalSignatureOption, + signature: v2SignatureOption, uploadSuccess: { endpoint: uploadSuccessUrl, method: "PUT" @@ -501,6 +563,7 @@ if (qqtest.canDownloadFileAsBlob) { assert.equal(uploadSuccessRequest.method, "PUT"); uploadSuccessRequest.respond(200, null, null); assert.equal(uploader.getUploads()[0].status, qq.status.UPLOAD_SUCCESSFUL); + done(); }); }); From d4491684040ed7d46d7c36aad284d3a01fbc6ea9 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 28 Apr 2015 15:56:54 -0500 Subject: [PATCH 006/344] docs(v4-signatures): Signature version/region option text cleanup #1336 [skip ci] --- docs/api/options-s3.jmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/options-s3.jmd b/docs/api/options-s3.jmd index 45c56ca9b..b08e05c83 100644 --- a/docs/api/options-s3.jmd +++ b/docs/api/options-s3.jmd @@ -71,7 +71,7 @@ alert("The `chunking.paramNames` option does **not** apply to S3.") ("objectProperties.bucket", "bucket", "Describes the name of the bucket used to house the file in S3. This is required if the bucket cannot be determined by examining the endpoint (such as if you are using a CDN as an endpoint). Possible values are a string representing the bucket name, or a function. If the value is a function, Fine Uploader S3 will pass the associated file ID as a parameter when invoking your function. If the value is a function it may return one of a promise or a `String`.", "String or Function", "(assumes the bucket can be determined by parsing the endpoint string)",), ("objectProperties.key", "key", "Describes the object key used to identify the file in your S3 bucket. Possible values are 'uuid', 'filename' or a function. If the value is a function, Fine Uploader S3 will pass the associated file ID as a parameter when invoking your function. If the value is a function it may return one of a promise or a `String`.", "String or Function", "uuid",), ("objectProperties.reducedRedundancy", "reducedRedundancy", "Set this to true if you would like to use the reduced redundancy storage class for all objects uploaded to S3.", "Boolean", "false",), - ("objectProperties.region", "region", "Version 4 signatures only: The region identifier for the target bucket. These are defined at http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region.", "String", "us-east-1",), + ("objectProperties.region", "region", "Version 4 signatures only: The [S3 region identifier](http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region) for the target bucket.", "String", "us-east-1",), ("objectProperties.serverSideEncryption", "serverSideEncryption", "Set this to true if you would like all uploaded files to be encrypted by AWS.", "Boolean", "false",), ) )}} @@ -96,7 +96,7 @@ alert("`request.customHeaders` does **not** apply to S3.") ( ("signature.customHeaders", "customHeaders", "Additional headers sent along with each signature request. If you declare a function as the value, the associated file's ID will be passed to your function when it is invoked.", "Object, Function", "{}",), ("signature.endpoint", "endpoint", "The endpoint that Fine Uploader can use to send policy documents (HTML form uploads) or other strings to sign (REST requests) before sending requests off to S3.", "String", "null",), - ("signature.version", "version", "The AWS/S3 signature version to use. Currently supported values are `2` and `4`.", "Integer", "2",), + ("signature.version", "version", "The AWS/S3 signature version to use. Currently supported values are `2` and `4`. Directly related to [`objectProperties.region`](#objectProperties.region).", "Integer", "2",), ) )}} From 0e1b03d6536c9e5310b0517b4bc0250b4770324f Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 4 Jun 2015 13:14:46 -0500 Subject: [PATCH 007/344] chore(build): start of 5.2.2 hotfix work --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e5df0b884..a60de56cf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.2.1 +Version: 5.2.2 [![Build Status](https://travis-ci.org/FineUploader/fine-uploader.png?branch=master)](https://travis-ci.org/FineUploader/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index e55997529..01f9dfde9 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.2.1", + "version": "5.2.2", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index 69708963a..93df55ad4 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.2.1"; +qq.version = "5.2.2"; diff --git a/package.json b/package.json index 7377b091d..09a9169c9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fine-uploader", "title": "Fine Uploader", - "version": "5.2.1", + "version": "5.2.2", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ From d5a40e7c6963efc8f4d5701682dee5ac980567b3 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 4 Jun 2015 13:15:38 -0500 Subject: [PATCH 008/344] fix(templating): cancelled files abort queued preview gen process fixes #1416 --- client/js/templating.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/js/templating.js b/client/js/templating.js index f3d18ff3e..65f472013 100644 --- a/client/js/templating.js +++ b/client/js/templating.js @@ -512,6 +512,10 @@ qq.Templating = function(spec) { }); } } + // File element in template may have been removed, so move on to next item in queue + else { + generateNextQueuedPreview(); + } } else if (thumbnail) { displayWaitingImg(thumbnail); From 0ce5f290b30e7e0a59da17166ceda9426b43c304 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 4 Jun 2015 14:35:55 -0500 Subject: [PATCH 009/344] fix(templates): trash button submits form The default type of any - - + + + @@ -48,15 +48,15 @@
- +
- - + +
@@ -64,8 +64,8 @@
- - + +
diff --git a/client/html/templates/gallery.html b/client/html/templates/gallery.html index 0591dc31e..be5a04eb4 100644 --- a/client/html/templates/gallery.html +++ b/client/html/templates/gallery.html @@ -38,8 +38,8 @@
- - + @@ -51,13 +51,13 @@ - - - @@ -67,15 +67,15 @@
- +
- - + +
@@ -83,8 +83,8 @@
- - + +
diff --git a/client/html/templates/simple-thumbnails.html b/client/html/templates/simple-thumbnails.html index cc12cdcef..d7d0086fb 100644 --- a/client/html/templates/simple-thumbnails.html +++ b/client/html/templates/simple-thumbnails.html @@ -40,9 +40,9 @@ - - - + + + @@ -50,15 +50,15 @@
- +
- - + +
@@ -66,8 +66,8 @@
- - + +
diff --git a/docs/features/forms.jmd b/docs/features/forms.jmd index ce16da8af..ac98206b5 100644 --- a/docs/features/forms.jmd +++ b/docs/features/forms.jmd @@ -109,9 +109,9 @@ file that integrates Fine Uploader into an existing form: - - - + + + @@ -119,15 +119,15 @@ file that integrates Fine Uploader into an existing form:
- +
- - + +
@@ -135,8 +135,8 @@ file that integrates Fine Uploader into an existing form:
- - + +
diff --git a/docs/features/pause.jmd b/docs/features/pause.jmd index c54408322..3cc933319 100644 --- a/docs/features/pause.jmd +++ b/docs/features/pause.jmd @@ -26,8 +26,8 @@ If you want "pause" and/or "continue" buttons to appear next to your files at th add the following to your template: ```html - - + + ``` These elements must be added under the qq-upload-list-selector container in your template in order for Fine Uploader UI to find them. diff --git a/docs/features/styling.jmd b/docs/features/styling.jmd index dc0f2ad5a..ab967980e 100644 --- a/docs/features/styling.jmd +++ b/docs/features/styling.jmd @@ -161,7 +161,7 @@ To do this, the file list portion of the template can be modified to include thi
  • ... - +
``` diff --git a/docs/quickstart/02-setting_options-azure.jmd b/docs/quickstart/02-setting_options-azure.jmd index f37d3ce3c..37a297d05 100644 --- a/docs/quickstart/02-setting_options-azure.jmd +++ b/docs/quickstart/02-setting_options-azure.jmd @@ -145,9 +145,9 @@ Now our Fine Uploader page should look something like this: - - - + + + @@ -155,15 +155,15 @@ Now our Fine Uploader page should look something like this:
- +
- - + +
@@ -171,8 +171,8 @@ Now our Fine Uploader page should look something like this:
- - + +
diff --git a/docs/quickstart/02-setting_options-s3.jmd b/docs/quickstart/02-setting_options-s3.jmd index b1e57615f..2ba56a897 100644 --- a/docs/quickstart/02-setting_options-s3.jmd +++ b/docs/quickstart/02-setting_options-s3.jmd @@ -142,9 +142,9 @@ Now our Fine Uploader page should look something like this: - - - + + + @@ -152,15 +152,15 @@ Now our Fine Uploader page should look something like this:
- +
- - + +
@@ -168,8 +168,8 @@ Now our Fine Uploader page should look something like this:
- - + +
diff --git a/docs/quickstart/02-setting_options.jmd b/docs/quickstart/02-setting_options.jmd index 537ae7af8..de1dad4f7 100644 --- a/docs/quickstart/02-setting_options.jmd +++ b/docs/quickstart/02-setting_options.jmd @@ -139,9 +139,9 @@ Now our Fine Uploader page should look something like this: - - - + + + @@ -149,15 +149,15 @@ Now our Fine Uploader page should look something like this:
- +
- - + +
@@ -165,8 +165,8 @@ Now our Fine Uploader page should look something like this:
- - + +
From 48fcf95be83d39f89d53391113ee58af45e5b245 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 4 Jun 2015 14:39:50 -0500 Subject: [PATCH 010/344] chore(dev): set button types for dev templates too #1417 [skip ci] --- test/dev/index.html | 54 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/test/dev/index.html b/test/dev/index.html index 3b8859587..0388ab3e5 100644 --- a/test/dev/index.html +++ b/test/dev/index.html @@ -27,8 +27,8 @@
- - + @@ -40,13 +40,13 @@ - - - @@ -56,15 +56,15 @@
- +
- - + +
@@ -72,8 +72,8 @@
- - + +
@@ -107,9 +107,9 @@ - - - + + + @@ -117,15 +117,15 @@ - + - - + + @@ -133,8 +133,8 @@ - - + + @@ -166,9 +166,9 @@ - - - + + + @@ -176,15 +176,15 @@ - + - - + + @@ -192,8 +192,8 @@ - - + + @@ -220,7 +220,7 @@

Fine Uploader Development

Manually Trigger Uploads

    - +
    From d51992a650179cd74d90370f85fb4804cb4fa52d Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Fri, 5 Jun 2015 09:37:43 -0500 Subject: [PATCH 011/344] chore(dev): prevent php warnings from messing w/ the response [skip ci] --- test/dev/handlers/s3/s3.php | 1 + 1 file changed, 1 insertion(+) diff --git a/test/dev/handlers/s3/s3.php b/test/dev/handlers/s3/s3.php index 86e5eaf8d..e2668ef26 100644 --- a/test/dev/handlers/s3/s3.php +++ b/test/dev/handlers/s3/s3.php @@ -1,4 +1,5 @@ Date: Fri, 5 Jun 2015 09:38:26 -0500 Subject: [PATCH 012/344] style(gallery): trash icon is sometimes pushed to the next line fixes #1418 --- client/fineuploader-gallery.css | 1 + 1 file changed, 1 insertion(+) diff --git a/client/fineuploader-gallery.css b/client/fineuploader-gallery.css index 55948403b..bdcca3b4a 100644 --- a/client/fineuploader-gallery.css +++ b/client/fineuploader-gallery.css @@ -310,6 +310,7 @@ } .qq-gallery .qq-upload-size { + float: left; font-size: 11px; color: #929292; margin-bottom: 3px; From 5a6781a38c103da14c145c590999bd352007184f Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Fri, 5 Jun 2015 10:44:06 -0500 Subject: [PATCH 013/344] fix(thumbnails): deleting file w/ entry in preview queue halts previews for new files fixes #1419 --- client/js/uploader.api.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/js/uploader.api.js b/client/js/uploader.api.js index 17bcf9a82..70dd2931a 100644 --- a/client/js/uploader.api.js +++ b/client/js/uploader.api.js @@ -684,7 +684,14 @@ _maybeUpdateThumbnail: function(fileId) { var thumbnailUrl = this._thumbnailUrls[fileId]; - this._templating.updateThumbnail(fileId, thumbnailUrl); + if (thumbnailUrl || + this._options.thumbnails.placeholders.waitUntilResponse || + !qq.supportedFeatures.imagePreviews) { + + // This will replace the "waiting" placeholder with a "preview not available" placeholder + // if called with a null thumbnailUrl. + this._templating.updateThumbnail(fileId, thumbnailUrl); + } }, _addCannedFile: function(sessionData) { From ae5b176f3c3043fb9b73c804cf0202c7c08eba96 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Fri, 5 Jun 2015 10:56:57 -0500 Subject: [PATCH 014/344] fix(thumbnails): don't try to update thumb for a deleted file fixes #1419 --- client/js/uploader.api.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/js/uploader.api.js b/client/js/uploader.api.js index 70dd2931a..6b0bc6094 100644 --- a/client/js/uploader.api.js +++ b/client/js/uploader.api.js @@ -682,11 +682,13 @@ }, _maybeUpdateThumbnail: function(fileId) { - var thumbnailUrl = this._thumbnailUrls[fileId]; + var thumbnailUrl = this._thumbnailUrls[fileId], + fileStatus = this.getUploads({id: fileId}).status; - if (thumbnailUrl || + if (fileStatus !== qq.status.DELETED && + (thumbnailUrl || this._options.thumbnails.placeholders.waitUntilResponse || - !qq.supportedFeatures.imagePreviews) { + !qq.supportedFeatures.imagePreviews)) { // This will replace the "waiting" placeholder with a "preview not available" placeholder // if called with a null thumbnailUrl. From 8aa7bfe11478824e061727ea95f08862d7d9a6dc Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 9 Jun 2015 10:46:07 -0500 Subject: [PATCH 015/344] chore(merge): merge 5.2.2 hotfix in, inc build num #1419 #1418 #1417 #1416 --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a60de56cf..eb7e8ad5a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.2.2 +Version: 5.3.0-1 [![Build Status](https://travis-ci.org/FineUploader/fine-uploader.png?branch=master)](https://travis-ci.org/FineUploader/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index 01f9dfde9..7d7bc3282 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.2.2", + "version": "5.3.0-1", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index 93df55ad4..f3a9cdf97 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.2.2"; +qq.version = "5.3.0-1"; diff --git a/package.json b/package.json index 09a9169c9..4689aceb8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fine-uploader", "title": "Fine Uploader", - "version": "5.2.2", + "version": "5.3.0-1", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ From c24a9caf5b58d22610c4fe91a534095b1479474f Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 10 Jun 2015 15:29:43 -0500 Subject: [PATCH 016/344] docs(getting-started): fix formatting issue with sub-titles --- docs/quickstart/02-setting_options-azure.jmd | 2 +- docs/quickstart/02-setting_options-s3.jmd | 2 +- docs/quickstart/03-setting_up_server-azure.jmd | 2 +- docs/quickstart/03-setting_up_server-s3.jmd | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/quickstart/02-setting_options-azure.jmd b/docs/quickstart/02-setting_options-azure.jmd index 37a297d05..6d90ed2d5 100644 --- a/docs/quickstart/02-setting_options-azure.jmd +++ b/docs/quickstart/02-setting_options-azure.jmd @@ -2,7 +2,7 @@ {% set page_title = "Setting Options Azure" %} {% block content %} {% markdown %} -# Setting Fine Uploader Options Azure Blob Storage{: .page-header } +# Setting Fine Uploader Options Azure Blob Storage {: .page-header } At this point in the tutorial you should have an very simple HTML page with a Fine Uploader Azure instance. This section will get you started with setting some of diff --git a/docs/quickstart/02-setting_options-s3.jmd b/docs/quickstart/02-setting_options-s3.jmd index 2ba56a897..07d060aeb 100644 --- a/docs/quickstart/02-setting_options-s3.jmd +++ b/docs/quickstart/02-setting_options-s3.jmd @@ -2,7 +2,7 @@ {% set page_title = "Setting Options S3" %} {% block content %} {% markdown %} -# Setting Fine Uploader Options Amazon S3{: .page-header } +# Setting Fine Uploader Options Amazon S3 {: .page-header } At this point in the tutorial you should have an very simple HTML page with a Fine Uploader instance. This section will get you started with setting some of diff --git a/docs/quickstart/03-setting_up_server-azure.jmd b/docs/quickstart/03-setting_up_server-azure.jmd index 073af204d..e18c548f1 100644 --- a/docs/quickstart/03-setting_up_server-azure.jmd +++ b/docs/quickstart/03-setting_up_server-azure.jmd @@ -3,7 +3,7 @@ {% block content %} {% markdown %} -# Server Set-Up Azure Blob Storage{: .page-header} +# Server Set-Up Azure Blob Storage {: .page-header} We have provided a [sample C# endpoint handler for Fine Uploader Azure][csharp]. If you would like to develop your own endpoint handler, or modify the example, you should read about [handling diff --git a/docs/quickstart/03-setting_up_server-s3.jmd b/docs/quickstart/03-setting_up_server-s3.jmd index ca7a02bed..435efaf76 100644 --- a/docs/quickstart/03-setting_up_server-s3.jmd +++ b/docs/quickstart/03-setting_up_server-s3.jmd @@ -3,7 +3,7 @@ {% block content %} {% markdown %} -# Server Set-Up Amazon S3{: .page-header} +# Server Set-Up Amazon S3 {: .page-header} For this tutorial we are going to use [Node.js](http://nodejs.org/) to develop a simple server which will accept uploads from our Fine Uploader instance. If From fdce4c82bc5c29cacb9ff14a553e845672382b87 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 10 Jun 2015 15:29:43 -0500 Subject: [PATCH 017/344] docs(getting-started): fix formatting issue with sub-titles --- docs/quickstart/02-setting_options-azure.jmd | 2 +- docs/quickstart/02-setting_options-s3.jmd | 2 +- docs/quickstart/03-setting_up_server-azure.jmd | 2 +- docs/quickstart/03-setting_up_server-s3.jmd | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/quickstart/02-setting_options-azure.jmd b/docs/quickstart/02-setting_options-azure.jmd index 37a297d05..6d90ed2d5 100644 --- a/docs/quickstart/02-setting_options-azure.jmd +++ b/docs/quickstart/02-setting_options-azure.jmd @@ -2,7 +2,7 @@ {% set page_title = "Setting Options Azure" %} {% block content %} {% markdown %} -# Setting Fine Uploader Options Azure Blob Storage{: .page-header } +# Setting Fine Uploader Options Azure Blob Storage {: .page-header } At this point in the tutorial you should have an very simple HTML page with a Fine Uploader Azure instance. This section will get you started with setting some of diff --git a/docs/quickstart/02-setting_options-s3.jmd b/docs/quickstart/02-setting_options-s3.jmd index 2ba56a897..07d060aeb 100644 --- a/docs/quickstart/02-setting_options-s3.jmd +++ b/docs/quickstart/02-setting_options-s3.jmd @@ -2,7 +2,7 @@ {% set page_title = "Setting Options S3" %} {% block content %} {% markdown %} -# Setting Fine Uploader Options Amazon S3{: .page-header } +# Setting Fine Uploader Options Amazon S3 {: .page-header } At this point in the tutorial you should have an very simple HTML page with a Fine Uploader instance. This section will get you started with setting some of diff --git a/docs/quickstart/03-setting_up_server-azure.jmd b/docs/quickstart/03-setting_up_server-azure.jmd index 073af204d..e18c548f1 100644 --- a/docs/quickstart/03-setting_up_server-azure.jmd +++ b/docs/quickstart/03-setting_up_server-azure.jmd @@ -3,7 +3,7 @@ {% block content %} {% markdown %} -# Server Set-Up Azure Blob Storage{: .page-header} +# Server Set-Up Azure Blob Storage {: .page-header} We have provided a [sample C# endpoint handler for Fine Uploader Azure][csharp]. If you would like to develop your own endpoint handler, or modify the example, you should read about [handling diff --git a/docs/quickstart/03-setting_up_server-s3.jmd b/docs/quickstart/03-setting_up_server-s3.jmd index ca7a02bed..435efaf76 100644 --- a/docs/quickstart/03-setting_up_server-s3.jmd +++ b/docs/quickstart/03-setting_up_server-s3.jmd @@ -3,7 +3,7 @@ {% block content %} {% markdown %} -# Server Set-Up Amazon S3{: .page-header} +# Server Set-Up Amazon S3 {: .page-header} For this tutorial we are going to use [Node.js](http://nodejs.org/) to develop a simple server which will accept uploads from our Fine Uploader instance. If From 100a7da1677b17934afae78310272e4fbf24a15a Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 9 Jun 2015 15:47:16 -0500 Subject: [PATCH 018/344] docs(*): cleanup [skip ci] --- docs/_static/css/main.css | 6 + docs/_static/js/sidebar.js | 12 +- docs/_templates/layout.html | 2 +- docs/_templates/navbar.html | 2 +- docs/api/events-s3.jmd | 42 ++++--- docs/api/events.jmd | 48 ++++---- docs/api/methods-azure.jmd | 2 +- docs/api/methods-s3.jmd | 2 +- docs/api/methods.jmd | 20 ++-- docs/api/options-azure.jmd | 8 +- docs/api/options-s3.jmd | 15 +-- docs/api/options-ui.jmd | 22 ++-- docs/api/options.jmd | 22 ++-- docs/api/qq.jmd | 62 +++++----- docs/browser-support.jmd | 53 ++++----- docs/contributing.jmd | 13 ++- docs/endpoint_handlers/amazon-s3.jmd | 70 +++++------ docs/endpoint_handlers/traditional.jmd | 121 ++++++++++---------- docs/faq.jmd | 26 ++--- docs/features/CORS.jmd | 73 ++++++++++-- docs/features/async-tasks-and-promises.jmd | 27 +++-- docs/features/azure.jmd | 26 +++-- docs/features/cancellable-uploads.jmd | 13 +-- docs/features/chunking.jmd | 42 ++++--- docs/features/concurrent-chunking.jmd | 6 +- docs/features/delete.jmd | 68 ++++++++++- docs/features/drag-and-drop.jmd | 8 +- docs/features/extra-buttons.jmd | 29 ++--- docs/features/filename-edit.jmd | 64 +++++++++-- docs/features/handling-errors.jmd | 25 ++-- docs/features/no-server-uploads.jmd | 5 +- docs/features/paste-to-upload.jmd | 35 ++++-- docs/features/resume.jmd | 82 +++++++++++-- docs/features/session.jmd | 24 +++- docs/features/upload-from-mobile-camera.jmd | 68 ++++++++++- docs/index.jmd | 41 ++++--- docs/integrating/jquery.jmd | 95 ++++++++------- docs/modes/core.jmd | 26 ++--- docs/modes/ui.jmd | 24 ++-- 39 files changed, 843 insertions(+), 486 deletions(-) diff --git a/docs/_static/css/main.css b/docs/_static/css/main.css index a14622012..44dcbbcf6 100755 --- a/docs/_static/css/main.css +++ b/docs/_static/css/main.css @@ -235,3 +235,9 @@ h4 a, a:hover, a:focus, :visited { .method-params > hr { margin: 2px; } + +.version-number { + padding: 1px; + margin-right: 5px; + font-size: 18px; +} diff --git a/docs/_static/js/sidebar.js b/docs/_static/js/sidebar.js index acf87b74d..5b2750cbd 100755 --- a/docs/_static/js/sidebar.js +++ b/docs/_static/js/sidebar.js @@ -18,18 +18,18 @@ var renderSidebarNav = function(type, headers) { "
    -

    {{ PKG['version'] }}

    +

    {{ PKG['version'] }}

    {% block pre_content %}{% endblock %} diff --git a/docs/_templates/navbar.html b/docs/_templates/navbar.html index e00db09a3..46954a529 100644 --- a/docs/_templates/navbar.html +++ b/docs/_templates/navbar.html @@ -36,7 +36,7 @@
  • Pause In-Progress Uploads
  • Progress Bars
  • Previews & Thumbnails
  • -
  • Request
  • +
  • Request Parameters
  • Resuming
  • Retrying
  • S3 Uploads
  • diff --git a/docs/api/events-s3.jmd b/docs/api/events-s3.jmd index ae5d71e1c..dd39ed1a6 100644 --- a/docs/api/events-s3.jmd +++ b/docs/api/events-s3.jmd @@ -2,45 +2,53 @@ {% set page_title = "S3 Methods" %} {% block sidebar %} - + +{% endblock %} + +{% block js_head %} + +{% endblock %} + +{% block js_footer %} + {% endblock %} {% block content %} {% markdown %} -# Events S3{: .page-header } +# Events S3 {: .page-header } The S3 uploader provides many of the same events as the traditional -uploader, but there are some additional events exclusive to S3 endpoints. +uploader, but there are some additional events _exclusive_ to S3 endpoints. If you plan to use the S3 uploader, then use the following table as your event -reference. All methods outlined in the core/traditional documentation are also available -in Fine Uploader S3, unless otherwise noted here. +reference. **All methods outlined in the core/traditional documentation are also available +in Fine Uploader S3, unless otherwise noted here.** ### Syntax The following syntax is the correct way to define event handlers: -``` +```javascript callbacks: { onDelete: function(id) { - + //... }, onDeleteComplete: function(id, xhr, isError) { - //... + //... } } ``` -#### credentialsExpired +{% endmarkdown %} +
    -Called before a request is sent to S3 if the temporary credentials have expired. You must return -a promise. If your attempt to refresh the temporary credentials is successful, you must fulfill -the promise via the `success` method, passing the new `credentials` object. Otherwise, call `failure` -with a descriptive reason. +{{ api_event("credentialsExpired", "onCredentialsExpired", +"Called before a request is sent to S3 if the temporary credentials have expired. You must return a promise. If your attempt to refresh the temporary credentials is successful, you must fulfill the promise via the `success` method, passing the new `credentials` object. Otherwise, call `failure` with a descriptive reason.", [])}} ----- +
    -{% endmarkdown %} {% endblock %} diff --git a/docs/api/events.jmd b/docs/api/events.jmd index bfe26f702..a3526425e 100644 --- a/docs/api/events.jmd +++ b/docs/api/events.jmd @@ -37,10 +37,10 @@ The following syntax is the correct way to define event handlers: ``` callbacks: { onDelete: function(id) { - // ... + // ... }, onDeleteComplete: function(id, xhr, isError) { - //... + //... } } ``` @@ -48,7 +48,7 @@ callbacks: { {% endmarkdown %}
    -{{ api_event("autoRetry", "autoRetry", "Called before each automatic retry attempt for a failed item.", +{{ api_event("autoRetry", "onAutoRetry", "Called before each automatic retry attempt for a failed item.", [ { "name": "id", @@ -68,7 +68,7 @@ callbacks: { ]) }} -{{ api_event("cancel", "cancel", +{{ api_event("cancel", "onCancel", """Called when the item has been canceled. Return `false` to prevent the upload from being canceled. Also can return a promise if non-blocking work is required here. Calling `failure()` on the promise is equivalent to @@ -91,7 +91,7 @@ the upload may actually complete until the promise has actually be fullfilled. ]) }} -{{ api_event("complete", "complete", +{{ api_event("complete", "onComplete", """Called when the item has finished uploading. The `responseJSON` will contain the raw response from the server including the @@ -120,7 +120,7 @@ The `responseJSON` will contain the raw response from the server including the ]) }} -{{ api_event("allComplete", "allComplete", +{{ api_event("allComplete", "onAllComplete", "Called when all submitted items have reached a point of termination. A file has reached a point of termination if it has been cancelled, rejected, or uploaded (failed or successful). For example, if a file in the group is paused, and all other files in the group have uploaded successfully the allComplete event will not be invoked for the group @@ -141,7 +141,7 @@ called if all files in the group have been cancelled or rejected (i.e. if none o ]) }} -{{ api_event("delete", "delete", "Called just before a delete request is sent for the associated item.", +{{ api_event("delete", "onDelete", "Called just before a delete request is sent for the associated item.", [ { "name": "id", @@ -151,7 +151,7 @@ called if all files in the group have been cancelled or rejected (i.e. if none o ]) }} -{{ api_event("deleteComplete", "deleteComplete", "Called just after receiving a response from the server for a delete file request.", +{{ api_event("deleteComplete", "onDeleteComplete", "Called just after receiving a response from the server for a delete file request.", [ { "name": "id", @@ -171,7 +171,7 @@ called if all files in the group have been cancelled or rejected (i.e. if none o ]) }} -{{ api_event("error", "error", "Called whenever an exceptional condition occurs (see [Handling Errors](../features/handling-errors.html))", +{{ api_event("error", "onError", "Called whenever an exceptional condition occurs (see [Handling Errors](../features/handling-errors.html))", [ { "name": "id", @@ -196,7 +196,7 @@ called if all files in the group have been cancelled or rejected (i.e. if none o ]) }} -{{ api_event("manualRetry", "manualRetry", +{{ api_event("manualRetry", "onManualRetry", """Called before each manual retry attempt. Return `false` to prevent this an all future retry attempts on the associated item.""", @@ -214,7 +214,7 @@ Return `false` to prevent this an all future retry attempts on the associated it ]) }} -{{ api_event("pasteReceived", "pasteReceived", +{{ api_event("pasteReceived", "onPasteReceived", """Called when a pasted image has been received (before upload). The pasted image is represented as a `Blob`. Also can return a promise if non-blocking work is required here. If using a Promise the value of the success parameter must be the name to associate with the pasted image. If the associated attempt is marked a failure then you should include a string explaining the reason in your failure callback for the Promise. @@ -232,7 +232,7 @@ The `promptForName` option, if `true`, will effectively wipe away any custom imp ]) }} -{{ api_event("progress", "progress", +{{ api_event("progress", "onProgress", """ Called during the upload, as it progresses, but only for the AJAX uploader. For chunked uploads, this will be called for each chunk. @@ -263,7 +263,7 @@ Useful for implementing a progress bar. ]) }} -{{ api_event("resume", "resume", +{{ api_event("resume", "onResume", """ Called just before an upload is resumed. See the [`uploadChunk`](#uploadChunk) event for more info on the `chunkData` object. """, @@ -286,7 +286,7 @@ Useful for implementing a progress bar. ]) }} -{{ api_event("sessionRequestComplete", "sessionRequestComplete", +{{ api_event("sessionRequestComplete", "onSessionRequestComplete", """ Invoked when a session request has completed. The `response` will be either an `Array` containing the response data or `null` if the response did not contain valid JSON. The `success` parameter will be `false` if ANY of the file items represented in the response could not be parsed (due to bad syntax, missing name/UUID property, etc). @@ -312,7 +312,7 @@ See the [Initial File List feature page](../features/session.html) for more deta ]) }} -{{ api_event("statusChange", "statusChange", +{{ api_event("statusChange", "onStatusChange", """ Invoked whenever the status changes for any item submitted by the uploader. The status values correspond to those found in the `qq.status` object. For reference: @@ -350,7 +350,7 @@ The status values correspond to those found in the `qq.status` object. For refer ]) }} -{{ api_event("submit", "submit", +{{ api_event("submit", "onSubmit", """ Called when the item has been selected and is a candidate for uploading. This does not mean the item is going to be uploaded. Return `false` to prevent @@ -372,7 +372,7 @@ If a promise is returned, a call to failure is the same as returning `false`. ]) }} -{{ api_event("submitDelete", "submitDelete", +{{ api_event("submitDelete", "onSubmitDelete", """ Called before an item has been marked for deletion has been submitted to the uploader. @@ -390,7 +390,7 @@ returned, a call to failure is the same as returning `false`. ]) }} -{{ api_event("submitted", "submitted", +{{ api_event("submitted", "onSubmitted", """Called when the item has been successfully submitted to the uploader. The file will upload immediately if there is a) at least one free connection @@ -417,7 +417,7 @@ been added to the DOM immediately before this callback is invoked. ]) }} -{{ api_event("totalProgress", "totalProgress", +{{ api_event("totalProgress", "onTotalProgress", """ Called during a batch of uploads, as they progress, but only for the AJAX uploader. This represents the total progress of all files in the batch. @@ -437,7 +437,7 @@ Useful for implementing an aggregate progress bar. ]) }} -{{ api_event("upload", "upload", "Called just before an item begins uploading to the server.", +{{ api_event("upload", "onUpload", "Called just before an item begins uploading to the server.", [ { "name": "id", @@ -452,7 +452,7 @@ Useful for implementing an aggregate progress bar. ]) }} -{{ api_event("uploadChunk", "uploadChunk", +{{ api_event("uploadChunk", "onUploadChunk", """ Called just before a chunk request is sent. The `chunkData` object, which is passed into the 'uploadChunk' event handler, has 4 properties: @@ -482,7 +482,7 @@ The `chunkData` object, which is passed into the 'uploadChunk' event handler, ha ]) }} -{{ api_event("uploadChunkSuccess", "uploadChunkSuccess", +{{ api_event("uploadChunkSuccess", "onUploadChunkSuccess", """ This is similar to the [`complete` event](#complete), except it is invoked after each chunk has been **successfully** uploaded. @@ -512,7 +512,7 @@ See the [`uploadChunk` event](#uploadChunk) for more information on the `chunkDa ]) }} -{{ api_event("validate", "validate", +{{ api_event("validate", "onValidate", """ Called once for each selected, dropped, or `addFiles` submitted file. This callback is always invoked before the default Fine Uploader validators @@ -546,7 +546,7 @@ The `blobData` object has two properties: `name` and `size`. The `size` property ]) }} -{{ api_event("validateBatch", "validateBatch", +{{ api_event("validateBatch", "onValidateBatch", """ This callback is always invoked before the default Fine Uploader validators execute. diff --git a/docs/api/methods-azure.jmd b/docs/api/methods-azure.jmd index f08888ef3..459053fa5 100644 --- a/docs/api/methods-azure.jmd +++ b/docs/api/methods-azure.jmd @@ -21,7 +21,7 @@ $(document).ready(function() { {% block content %} {% markdown %} -# Methods Azure{: .page-header } +# Methods Azure {: .page-header } {% endmarkdown %}
    diff --git a/docs/api/methods-s3.jmd b/docs/api/methods-s3.jmd index 997e3c423..a3fca6f05 100644 --- a/docs/api/methods-s3.jmd +++ b/docs/api/methods-s3.jmd @@ -21,7 +21,7 @@ $(document).ready(function() { {% block content %} {% markdown %} -# Methods S3{: .page-header } +# Methods S3 {: .page-header } {% endmarkdown %}
    diff --git a/docs/api/methods.jmd b/docs/api/methods.jmd index 622d8b004..9f36e2464 100644 --- a/docs/api/methods.jmd +++ b/docs/api/methods.jmd @@ -78,7 +78,7 @@ A `CanvasWrapper` object: {{ api_method("cancelAll", "cancelAll ()", "Cancels all queued or currently uploading items.") }} -{{ api_method("clearStoredFiles", "clearStoredFiles ()", "Clears the internal list of stored items. Only applies when `autoUpload` is `false`") }} +{{ api_method("clearStoredFiles", "clearStoredFiles ()", "Clears the internal list of stored items. Only applies when [`autoUpload`](options.html#autoUpload) is `false`") }} {{ api_method("continueUpload", "continueUpload (id)", "Attempts to continue a paused upload.", [ @@ -176,7 +176,7 @@ A `CanvasWrapper` object: } ]) }} -{{ api_method("getInProgress", "getInProgress ()", "Returns the number of items that are either currently uploading or waiting for an available connection (`qq.status.QUEUED`). If called inside of a `cancel` event handler, then this method will return a value that includes the upload associated with the `cancel` event handler. This is because the upload will not be canceled until the event handler returns.", none, [ +{{ api_method("getInProgress", "getInProgress ()", "Returns the number of items that are either currently uploading or waiting for an available connection (`qq.status.QUEUED`). If called inside of [a `cancel` event](events.html#cancel) handler, then this method will return a value that includes the upload associated with the `cancel` event handler. This is because the upload will not be canceled until the event handler returns.", none, [ { "type": "Integer", "description": "The number of items that are currently uploading or queued." @@ -224,7 +224,7 @@ A `CanvasWrapper` object: ]) }} -{{ api_method("getRemainingAllowedItems", "getRemainingAllowedItems ()", "Returns the numer of remaining allowed items that may be submitted for upload based on `validation.itemLimit`.", null, +{{ api_method("getRemainingAllowedItems", "getRemainingAllowedItems ()", "Returns the numer of remaining allowed items that may be submitted for upload based on [`validation.itemLimit`](options.html#validation.itemLimit).", null, [ { "type": "Integer", @@ -375,7 +375,7 @@ A `CanvasWrapper` object: } ], null) }} -{{ api_method("setEndpoint", "setEndpoint (path[, identifier])", "Modify the location where upload requests should be directed. Pass in a file id or `HTMLElement` to change the endpoint for that specific item.", +{{ api_method("setEndpoint", "setEndpoint (path[, identifier])", "Modify the location where upload requests should be directed. Pass in a file id to change the endpoint for that specific item.", [ { "name": "path", @@ -385,7 +385,7 @@ A `CanvasWrapper` object: { "name": "identifier", "type": "Integer or HTMLElement", - "description": "Either an integer corresponding to a file, or an `HTMLElement` corresponding to an upload button." + "description": "An integer corresponding to a file." } ], null) }} @@ -403,21 +403,21 @@ A `CanvasWrapper` object: } ], null) }} -{{ api_method("setDeleteFileEndpoint", "setDeleteFileEndpoint (path[, identifier])", "Modify the location where upload requests should be directed. Pass in a file id or `HTMLElement` to change the endpoint for that specific item.", +{{ api_method("setDeleteFileEndpoint", "setDeleteFileEndpoint (path[, identifier])", "Modify the location where [delete requests](../features/delete.html) should be directed. Pass in a file id to change the endpoint for that specific item.", [ { "name": "path", "type": "String", - "description": "A valid URI where deleterequests will be sent." + "description": "A valid URI where delete requests will be sent." }, { "name": "identifier", "type": "Integer or HTMLElement", - "description": "Either an integer corresponding to a file, or an `HTMLElement` corresponding to an upload button." + "description": "An integer corresponding to a file." } ], null) }} -{{ api_method("setDeleteFileParams", "setDeleteFileParams (params[, id])", "Set the parameters for a delete request. Pass in a file id to make the parameters specific to that file.", +{{ api_method("setDeleteFileParams", "setDeleteFileParams (params[, id])", "Set the parameters for [a delete request](../features/delete.html). Pass in a file id to make the parameters specific to that file.", [ { "name": "params", @@ -432,7 +432,7 @@ A `CanvasWrapper` object: ], null) }} -{{ api_method("setItemLimit", "setItemLimit (newItemLimit)", "Change the `validation.itemLimit` option set during construction/initialization.", +{{ api_method("setItemLimit", "setItemLimit (newItemLimit)", "Change [the `validation.itemLimit` option](options.html#validation.itemLimit) set during construction/initialization.", [ { "name": "newItemLimit", diff --git a/docs/api/options-azure.jmd b/docs/api/options-azure.jmd index 4f396c0ae..8783118b4 100644 --- a/docs/api/options-azure.jmd +++ b/docs/api/options-azure.jmd @@ -36,7 +36,7 @@ behavior for the Traditional or Azure uploader. {% endmarkdown %} {{ -alert("The `chunking.paramNames` option does **not** apply to Fine Uploader Azure.") +alert("The [`chunking.paramNames` option](options.html#chunking) does **not** apply to Azure.") }} {{ api_parent_option("chunking", "chunking", "", ( @@ -47,7 +47,7 @@ alert("The `chunking.paramNames` option does **not** apply to Fine Uploader Azur {{ api_parent_option("cors", "cors", "", ( - ("cors.allowXdr", "allowXdr", "Enables or disables cross-domain ajax calls (if `expected` property is true) in IE9 and older.", "Boolean", "true",), + ("cors.allowXdr", "allowXdr", "Enables or disables cross-domain ajax calls (if [`expected` property](options.html#core.expected) is true) in IE9 and older.", "Boolean", "true",), ) )}} @@ -58,11 +58,11 @@ alert("The `chunking.paramNames` option does **not** apply to Fine Uploader Azur )}} {{ -alert("The `resume.paramNames` option does **not** apply to Fine Uploader Azure.") +alert("The [`resume.paramNames` option](options.html#resume) does **not** apply to Azure.") }} {{ -alert("`request.customHeaders` does **not** apply to Fine Uploader Azure.") +alert("The [`request.customHeaders` option](options.html#request.customHeaders) does **not** apply to Azure.") }} {{ api_parent_option("request", "request", "", diff --git a/docs/api/options-s3.jmd b/docs/api/options-s3.jmd index 9e5cc1465..693962738 100644 --- a/docs/api/options-s3.jmd +++ b/docs/api/options-s3.jmd @@ -29,7 +29,7 @@ If you are using the S3 uploader, all of Fine Uploader's options are still present, and most of them still function the same way. However, some of the options are slightly different. This page will list any new or different options for the S3 uploader. Any option not listed here can be assumed to have the same -behavior for the Traditional or S3 uploader. +behavior as the traditional endpoint uploader. ## Core @@ -45,7 +45,7 @@ behavior for the Traditional or S3 uploader. )}} {{ -alert("The `chunking.paramNames` option does **not** apply to S3.") +alert("The [`chunking.paramNames` option](options.html#chunking) does **not** apply to S3.") }} {{ api_parent_option("chunking", "chunking", "", ( @@ -55,7 +55,7 @@ alert("The `chunking.paramNames` option does **not** apply to S3.") {{ api_parent_option("cors", "cors", "", ( - ("cors.allowXdr", "allowXdr", "Enables or disables cross-domain ajax calls (if `expected` property is true) in IE9 and older.", "Boolean", "true",), + ("cors.allowXdr", "allowXdr", "Enables or disables cross-domain ajax calls (if [the `expected` property](options.html#cors.expected) is true) in IE9 and older.", "Boolean", "true",), ) )}} @@ -76,16 +76,17 @@ alert("The `chunking.paramNames` option does **not** apply to S3.") )}} {{ -alert("The `resume.paramNames` option does **not** apply to S3.") +alert("The [`resume.paramNames` option](options.html#resume) does **not** apply to S3.") }} {{ -alert("`request.customHeaders` does **not** apply to S3.") +alert("The [`request.customHeaders` option](options.html#request.customHeaders) does **not** apply to S3.") }} + {{ api_parent_option("request", "request", "", ( - ("request.accessKey", "accessKey", "Your AWS public key. **NOT YOUR SECRET KEY**. Ignored if `credentials` have been set.", "String", "null",), - ("request.endpoint", "endpoint", "URL for your S3 bucket or the URL of a CDN that forwards the request to S3. All valid bucket URLs documented by Amazon are supported, including custom domains. SSL is also supported. If you use a CDN address, be sure to specify the bucket via the `objectProperties.bucket` option.", "String", "null",), + ("request.accessKey", "accessKey", "Your AWS public key. **NOT YOUR SECRET KEY**. Ignored if [`credentials`](#credentials) have been set.", "String", "null",), + ("request.endpoint", "endpoint", "URL for your S3 bucket or the URL of a CDN that forwards the request to S3. All valid bucket URLs documented by Amazon are supported, including custom domains. SSL is also supported. If you use a CDN address, be sure to specify the bucket via [the `objectProperties.bucket` option](#objectProperties.bucket).", "String", "null",), ("request.filenameParam", "filenameParam", "Part of the parameter name that contains the name of the associated file which may differ from the key name. Prefixed with 'x-amz-meta-' by Fine Uploader.", "String", "qqfilename",), ("request.params", "params", "Parameters passed along with each upload request.", "Object", "{}",), ) diff --git a/docs/api/options-ui.jmd b/docs/api/options-ui.jmd index e522807ea..408490340 100644 --- a/docs/api/options-ui.jmd +++ b/docs/api/options-ui.jmd @@ -26,8 +26,8 @@ $(document).ready(function() { ## UI -Fine Uploader UI mode has a few different options as well as some options -pertaining specifically to the UI which Core mode does not have. +Fine Uploader [UI mode](../modes/ui.html) has a few different options as well as some options +pertaining specifically to the UI which [core mode](../modes/core.html) does not have. Any options that exist in Core mode also exist in UI mode, and, in most cases, can be overridden. @@ -50,9 +50,7 @@ can be overridden. {{ api_option("template", "template", "This points to the container element that contains the template to use for one or more Fine Uploader UI instances. You can either specify a string, which is the element ID (the ID of the container element on the page) or an `Element` that points to the container element.", "String or Element", "qq-template") }} {{ api_parent_option("deleteFile", "deleteFile", -""" `deleteFile` is also in the Core mode options. This section defines UI specific -options for `deleteFile`. -""", +"This section defines UI specific options for [the core `deleteFile` option](options.html#deleteFile).", ( ("deleteFile.confirmMessage", "confirmMessage", "The message displayed in the confirm delete dialog.", "String", "Are you sure you want to delete {filename}?",), ("deleteFile.deletingFailedText", "deletingFailedText", "The status message to appear next to a file that has failed to delete.", "String", "Delete failed",), @@ -85,7 +83,7 @@ options for `deleteFile`. {{ api_parent_option("messages", "messages", "", ( - ("messages.tooManyFilesError", "tooManyFilesError", "Text sent to `showMessage` in the 'error' event handler when `multiple` is `false` and more than one file is dropped at once.", "String", "You may only drop one file.",), + ("messages.tooManyFilesError", "tooManyFilesError", "Text sent to [`showMessage`](#showMessage) when `multiple` is `false` and more than one file is dropped at once.", "String", "You may only drop one file.",), ("messages.unsupportedBrowser", "unsupportedBrowser", "Text displayed to users who have ancient browsers.", "String", "Unrecoverable error - the browser does not permit uploading of any kind.",), ) )}} @@ -94,7 +92,7 @@ options for `deleteFile`. """`messages` is also in the Core mode options. This section defines UI specific options for `messages`""", "info", "Note:") }} -{{ api_parent_option("retry", "retry", "", +{{ api_parent_option("retry", "retry", "This section defines UI specific options for [the core `retry` option](options.html#retry)", ( ("retry.autoRetryNote", "autoRetryNote", "The text of the note that will optionally appear next to the item during automatic retry attempts. Ignored if `showAutoRetryNote` is false.", "String", "Retrying {retryNum}/{maxAuto} ...",), ("retry.showButton", "showButton", "Enable or disable the showing of a button/link next to the failed item after all retry attempts have been exhausted. Clicking the button/link will force the uploader to make another attempt.", "Boolean", "false",), @@ -102,10 +100,6 @@ options for `messages`""", "info", "Note:") }} ) )}} -{{ alert( -"""`retry` is also in the Core mode options. This section defines UI specific -options for `retry`""", "info", "Note:") }} - {{ api_parent_option("thumbnails", "thumbnails", "", ( ("thumbnails.maxCount", "maxCount", "Maximum number of previews to render per Fine Uploader instance. A call to the reset method resets this value as well.", "Integer", "0",), @@ -124,12 +118,12 @@ options for `retry`""", "info", "Note:") }} {{ api_parent_option("paste", "paste", "", ( - ("paste.namePromptMessage", "namePromptMessage", "Text that will appear in the `showPrompt` dialog.", "String", "Please name this image",), - ("paste.promptForName", "promptForName", "Enable or disable the usage of `showPrompt` by Fine Uploader to prompt the user for a filename for a pasted file.", "Boolean", "false",), + ("paste.namePromptMessage", "namePromptMessage", "Text that will appear in [the `showPrompt` dialog](#showPrompt).", "String", "Please name this image",), + ("paste.promptForName", "promptForName", "Enable or disable the usage of [`showPrompt`](#showPrompt) by Fine Uploader to prompt the user for a filename for a pasted file.", "Boolean", "false",), ) )}} -{{ api_parent_option("scaling", "scaling", "See the [Upload Scaled Images feature page](../feature/scaling.html) for more details.", +{{ api_parent_option("scaling", "scaling", "See the [Upload Scaled Images feature page](../features/scaling.html) for more details.", ( ("scaling.failureText", "failureText", "Text that will appear next to a scaled image that could not be generated. This is in addition to the behavior associated with this property provided by Fine Uploader Core.", "String", "Failed to scale",), ("scaling.hideScaled", "hideScaled", "Set this to true if you do not want any scaled images to be displayed in the file list.", "Boolean", "false",), diff --git a/docs/api/options.jmd b/docs/api/options.jmd index e4b0ac6e7..0468c746f 100644 --- a/docs/api/options.jmd +++ b/docs/api/options.jmd @@ -28,20 +28,20 @@ $(document).ready(function() { Fine Uploader has a **plethora** of options. Many of these options change meaning depending on what sort of uploader you are using. Pay close attention to the -mode you are in and keep that in mind when determining which options do what. +mode you are in and keep that in mind when determining the meaning of a particular option. ## Core {% endmarkdown %} -{{ api_option("autoUpload", "autoUpload", "Set to `false` if you want to be able to upload queued items later by calling the `uploadStoredFiles()` method.", "Boolean", "true") }} +{{ api_option("autoUpload", "autoUpload", "Set to `false` if you want to be able to upload queued items later by calling the [`uploadStoredFiles()` method](methods.html#uploadStoredFiles).", "Boolean", "true") }} -{{ api_option("button", "button", "Specify an element to use as the 'select files' button. Cannot be a `
    + diff --git a/client/html/templates/gallery.html b/client/html/templates/gallery.html index be5a04eb4..3c7d69eb5 100644 --- a/client/html/templates/gallery.html +++ b/client/html/templates/gallery.html @@ -1,101 +1,82 @@ - - - - - - - - - - Fine Uploader Gallery UI - - - - - + + + +
    +
    + +
    +
    + +
    +
    + + +
    +
    + +
    + +
    + + +
    +
    +
    + diff --git a/client/html/templates/simple-thumbnails.html b/client/html/templates/simple-thumbnails.html index d7d0086fb..668c0c029 100644 --- a/client/html/templates/simple-thumbnails.html +++ b/client/html/templates/simple-thumbnails.html @@ -1,84 +1,64 @@ - - - - - - - - - - Fine Uploader default UI with thumbnails - - - - - +
    + +
    +
    +
    Upload a file
    +
    + + Processing dropped files... + + +
      +
    • +
      +
      +
      + + + + + + + + + + +
    • +
    + +
    +
    + +
    +
    + +
    +
    + + +
    +
    + +
    + +
    + + +
    +
    +
    + diff --git a/client/js/version.js b/client/js/version.js index 331cbd1aa..0b26ca579 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.11.5"; +qq.version = "5.11.6"; diff --git a/docs/quickstart/01-getting-started.jmd b/docs/quickstart/01-getting-started.jmd index 48edae184..4ad6215d1 100644 --- a/docs/quickstart/01-getting-started.jmd +++ b/docs/quickstart/01-getting-started.jmd @@ -4,120 +4,814 @@ {% markdown %} # Getting Started with Fine Uploader {: .page-header } -This tutorial will serve as a step-by-step walkthrough for getting starting with -Fine Uploader. It is geared towards those who have minimal experience with -Javascript, server-side concepts, or the web. +Fine Uploader is a pure-JavaScript browser-based file upload library with a long list of features that is unmatched by any other library. The power of Fine Uploader comes from its comprehensive set of options, API methods, and callbacks/events. The menu at the top of this page will give you access to more details regarding these three critical pieces of Fine Uploader. You can also read more about many of the features in the top navigation menu as well. -The tutorial is not comprehensive or meant to cover all aspects of Fine Uploader. -For more information, make sure to read through the list of features as well -as the API documentation. +This tutorial will serve as a step-by-step guide that you can follow to get Fine Uploader up and running in your project. The tutorial is not comprehensive or meant to cover all aspects of Fine Uploader. For more information, make sure to read through the list of features as well +as the API documentation. Both of these items (and more) are available in the top nav menu anywhere on this site. Please also see the live demos at [http://fineuploader.com/demos](http://fineuploader.com/demos) and detailed write-ups and integration ideas at [https://blog.fineuploader.com](https://blog.fineuploader.com/). -### 1. Download +### 1. Download (or build) -You can get Fine Uploader in the [downloads](http://fineuploader.com/downloads.html) -section. The download comes bundled with all required scripts and modules. +First, you will need to download or build Fine Uploader's JS, CSS, image, and template files. You have exactly three different supported options to achieve this. **Choose _one_ of the following options:** -### 2. Decide which Fine Uploader to use +#### Download [Fine Uploader from npm](https://www.npmjs.com/package/fine-uploader) -#### Modes -Fine Uploader comes with two different modes pertaining to the user interface. +The simplest option is to run `npm install fine-uploader` in your project directory. But this may not be appropriate for all situations. -##### [Core Mode](../modes/core.html) -The most basic Fine Uploader mode. Core mode assumes that the developer will code their own UI -but still make use of Fine Uploader's API. Core mode is suggested for advanced developers -who wish to heavily customize the uploader's user interface. To use Core Mode: +If your project has a package.json file, you should do this instead: -```javascript -var uploader = new qq.FineUploaderBasic({/* options go here .... */}); -``` +1. Add a "fine-uploader" entry to the "dependencies" section of your project's package.json file. The safest option is to explicitly specify the version number you need. For example, as of 16 August 2016, your entry would be `"fine-uploader": "5.11.5"`. For more information about package.json files, read [the official npm documentation on the subject](https://docs.npmjs.com/files/package.json). +2. Run `npm install` in the same directory as your project's package.json file. -**Each endpoint handler has a "core" build artifact associated with it. For example, if -you only need core functionality for Fine Uploader S3, you should import "s3.fine-uploader.core.js".** +In either case, fine-uploader will be installed in a directory of the same name under the node_modules directory. -##### [UI Mode](../modes/ui.html) -Inherits everything from Core mode. UI mode also comes with a fully functional -user interface which includes, but is not limited to: a default upload button, -progress bars, retry/cancel/delete buttons, proper display of error messages, -and more. UI mode is recommended for beginning developers or for those who -are pleased with the default Fine Uploader interface or can customize the default Fine Uploader -UI via CSS. To use UI Mode: +#### Download Fine Uploader from FineUploader.com -```javascript -var uploader = new qq.FineUploader({/* options go here ... */}); -``` -**Each endpoint handler has a "ui" build artifact associated with it. For example, if -you need core _and_ UI functionality for Fine Uploader S3, you should import "s3.fine-uploader.js".** +Navigate to [http://fineuploader.com/customize](http://fineuploader.com/customize) and select the desired build type. See "step 2" below for a detailed explanation of the different Fine Uploader build types. For any version since (and including) 5.11.0, you can download the individual builds at [https://github.com/FineUploader/fine-uploader/releases](https://github.com/FineUploader/fine-uploader/releases). + + +#### Build Fine Uploader yourself from the GitHub repo + +1. `git clone https://github.com/FineUploader/fine-uploader.git` +2. `cd fine-uploader` +3. `npm install` +4. `make build` + +This will result in a "_build" directory with CSS, JS, image, and template files for all possible builds of Fine Uploader. For information on how to further process these build files, if needed, read the ["Generating build artifacts" section the project's README.md file](https://github.com/FineUploader/fine-uploader#generating-build-artifacts). + +### 2. Decide which build of Fine Uploader to use + +Fine Uploader builds are categorized by "endpoint type" _and_ by feature set. Each of the four endpoint type builds are available with each of the two supported feature sets. **Choose one of the following endpoint types _and_ one of the "feature sets" before moving on to step 3**. + +#### Endpoint types + +A Fine Uploader build that is tied to a specific "endpoint type" is designed to send requests to match a specific type of server. There are three endpoint types supported: + +##### Traditional endpoints + +Traditional endpoints reside on a server or servers that you control. Fine Uploader "Traditional" will send all HTTP requests (such as "upload" and "delete file", to name a couple) to _your_ server. It is expected that you maintain and control the server that handles these requests. The [traditional server endpoints documentation page](../endpoint_handlers/traditional.html) describes how Fine Uploader "Traditional" will send requests to your server, and how your server must respond to these requests. + +##### S3 endpoints + +Fine Uploader S3 sends all requests (with the exception of the delete file request) directly to one or more S3 buckets. You may [utilize a server you control to sign these requests](../endpoint_handlers/amazon-s3.html) (the safest option), or you may elect to [omit a server entirely and sign the requests client side](../features/no-server-uploads.html) using Amazon's Web Identity Federation. + +##### Microsoft Azure endpoints + +Fine Uploader Azure sends _all_ requests directly to one or more Azure Blob Storage containers. You must utilize [a server you control to sign each request](../endpoint_handlers/azure.html). + +{{ alert("Fine Uploader Azure does not support IE9 and older. This is due to the fact that Azure's API does not allow files to be uploaded via multipart encoded POST requests, which is critical for IE9 and older support. If you need to support IE9 and older, you will need to load/use Fine Uploader with its traditional endpoint handler if the value of `qq.supportedFeatures.ajaxUploading` is `false`.") }} + +##### "All" endpoints + +If you would like to be able to send files to various endpoint types from a single running instance of Fine Uploader, a build of the library exists that includes support for traditional endpoint, S3 endpoints, _and_ Azure endpoints. + +#### Feature sets + +In addition to endpoint types Fine Uploader builds are classified according to what we call "feature sets". The two feature sets with associated builds are "UI" and "core". + +##### Core feature set + +The most basic Fine Uploader mode. [The core feature set](../modes/core.html) assumes that you will design and code your own UI but still make use of Fine Uploader's API. This feature set is suggested for advanced developers who wish to heavily customize the uploader's user interface. + + +##### UI feature set + +Inherits everything from the core feature set. [The UI feature set](../modes/ui.html) also comes with a fully functional user interface which includes, but is not limited to: a default upload button, progress bars, retry/cancel/delete buttons, proper display of error messages, and more. This feature set is recommended for those who are pleased with the default Fine Uploader interface or can sufficiently customize the default Fine Uploader UI using CSS overrides. **In most cases, the UI feature set will be the most appropriate choice.** + +### 3. Gather the appropriate Fine Uploader files + +After picking and endpoint type and a set of features from step 2, you can now gather the Fine Uploader files you will need to bring advanced file uploading capabilities to your project. + +#### Traditional endpoint, core feature set + +You will need the following JavaScript files: + +- fine-uploader/fine-uploader.core.js +- fine-uploader/fine-uploader.core.map.js (optional, but useful for live-debugging) + +#### Traditional endpoint, UI feature set + +You will need the following JavaScript files: + +- fine-uploader/fine-uploader.js +- fine-uploader/fine-uploader.map.js (optional, but useful for live-debugging) + +Next, decide if you'd like to adopt the "gallery layout" or the "row-based layout" for submitted files. See the [demos page](http://fineuploader.com/demos.html) for examples of both. The "Gallery View for Images" demo shows the gallery layout, and the "Manually Trigger Uploads & Edit File Names" demo illustrates a row-based layout. + +If you choose the gallery layout, you'll need this css file: + +- fine-uploader/fine-uploader-gallery.css + +...and this template file: + +- fine-uploader/templates/gallery.html + +If you've elected for a row-based UI layout, you'll need _these_ CSS files instead of the gallery files listed previously: + +- fine-uploader/fine-uploader-new.css + + +...and this template file: + +- fine-uploader/templates/simple-thumbnails.html + +You'll also need these image files, which should be located in the same directory as the Fine Uploader CSS file: + +- fine-uploader/loading.gif +- fine-uploader/processing.gif +- fine-uploader/continue.gif (optional - only if using the pause upload feature) +- fine-uploader/edit.gif (optional - only if using the edit filename feature) +- fine-uploader/retry.gif (optional - only if using the retry failed upload feature) +- fine-uploader/trash.gif (optional - only if using the delete file feature) + +The path to these placeholder images must be specified in the set of options you pass Fine Uploader. These are only needed if you make use of the [thumbnail preview feature](../features/thumbnails.html): + +- fine-uploader/placeholders/not_available-generic.png +- fine-uploader/placeholders/waiting-generic.png + +#### S3 endpoint, core feature set + +You will need the following JavaScript files: + +- s3.fine-uploader/s3.fine-uploader.core.js +- s3.fine-uploader/s3.fine-uploader.core.map.js (optional, but useful for live-debugging) + +#### S3 endpoint, UI feature set + +You will need the following JavaScript files: + +- s3.fine-uploader/s3.fine-uploader.js +- s3.fine-uploader/s3.fine-uploader.map.js (optional, but useful for live-debugging) + +Next, decide if you'd like to adopt the "gallery layout" or the "row-based layout" for submitted files. See the [demos page](http://fineuploader.com/demos.html) for examples of both. The "Gallery View for Images" demo shows the gallery layout, and the "Manually Trigger Uploads & Edit File Names" demo illustrates a row-based layout. + +If you choose the gallery layout, you'll need this css file: + +- s3.fine-uploader/fine-uploader-gallery.css + + +...and this template file: -{{ alert( -"""If you are using Fine Uploader UI, you MUST include a template in your document/markup. You can -use the `default.html` file in the `templates` directory bundled with the library and customize it as desired. See the -[styling documentation page](../features/styling.html) for more details.""") }} +- s3.fine-uploader/templates/gallery.html +If you've elected for a row-based UI layout, you'll need _these_ CSS files instead of the gallery files listed previously: -#### Endpoint Handlers +- s3.fine-uploader/fine-uploader-new.css -##### [Traditional](../endpoint_handlers/traditional.html) -To use the traditional endpoint handler, simply declare Fine Uploader as you -normally would. -##### [Amazon S3](../endpoint_handlers/amazon-s3.html) -Amazon's S3 is currently the most popular cloud service. -Fine Uploader comes with built in support that allows all files to be uploaded directly to S3 via the browser. -To make use of Fine Uploader S3: +...and this template file: -```javascript -var uploader = new qq.s3.FineUploader({/* options go here ... */}); +- s3.fine-uploader/templates/simple-thumbnails.html + +You'll also need these image files, which should be located in the same directory as the Fine Uploader CSS file: + +- s3.fine-uploader/loading.gif +- s3.fine-uploader/processing.gif +- s3.fine-uploader/continue.gif (optional - only if using the pause upload feature) +- s3.fine-uploader/edit.gif (optional - only if using the edit filename feature) +- s3.fine-uploader/retry.gif (optional - only if using the retry failed upload feature) +- s3.fine-uploader/trash.gif (optional - only if using the delete file feature) + +The path to these placeholder images must be specified in the set of options you pass Fine Uploader. These are only needed if you make use of the [thumbnail preview feature](../features/thumbnails.html): + +- s3.fine-uploader/placeholders/not_available-generic.png +- s3.fine-uploader/placeholders/waiting-generic.png + +#### Azure endpoint, core feature set + +You will need the following JavaScript files: + +- azure.fine-uploader/azure.fine-uploader.core.js +- azure.fine-uploader/azure.fine-uploader.core.map.js (optional, but useful for live-debugging) + +#### Azure endpoint, UI feature set + +You will need the following JavaScript files: + +- azure.fine-uploader/azure.fine-uploader.js +- azure.fine-uploader/azure.fine-uploader.map.js (optional, but useful for live-debugging) + +Next, decide if you'd like to adopt the "gallery layout" or the "row-based layout" for submitted files. See the [demos page](http://fineuploader.com/demos.html) for examples of both. The "Gallery View for Images" demo shows the gallery layout, and the "Manually Trigger Uploads & Edit File Names" demo illustrates a row-based layout. + +If you choose the gallery layout, you'll need this css file: + +- azure.fine-uploader/fine-uploader-gallery.css + +...and this template file: + +- azure.fine-uploader/templates/gallery.html + +If you've elected for a row-based UI layout, you'll need _these_ CSS files instead of the gallery files listed previously: + +- azure.fine-uploader/fine-uploader-new.css + +...and this template file: + +- azure.fine-uploader/templates/simple-thumbnails.html + +You'll also need these image files, which should be located in the same directory as the Fine Uploader CSS file: + +- azure.fine-uploader/loading.gif +- azure.fine-uploader/processing.gif +- azure.fine-uploader/continue.gif (optional - only if using the pause upload feature) +- azure.fine-uploader/edit.gif (optional - only if using the edit filename feature) +- azure.fine-uploader/retry.gif (optional - only if using the retry failed upload feature) +- azure.fine-uploader/trash.gif (optional - only if using the delete file feature) + +The path to these placeholder images must be specified in the set of options you pass Fine Uploader. These are only needed if you make use of the [thumbnail preview feature](../features/thumbnails.html): + +- azure.fine-uploader/placeholders/not_available-generic.png +- azure.fine-uploader/placeholders/waiting-generic.png + +#### All endpoint types, core feature set + +You will need the following JavaScript files: + +- all.fine-uploader/all.fine-uploader.core.js +- all.fine-uploader/all.fine-uploader.core.map.js (optional, but useful for live-debugging) + +#### All endpoint types, UI feature set + +You will need the following JavaScript files: + +- all.fine-uploader/all.fine-uploader.js +- all.fine-uploader/all.fine-uploader.map.js (optional, but useful for live-debugging) + +Next, decide if you'd like to adopt the "gallery layout" or the "row-based layout" for submitted files. See the [demos page](http://fineuploader.com/demos.html) for examples of both. The "Gallery View for Images" demo shows the gallery layout, and the "Manually Trigger Uploads & Edit File Names" demo illustrates a row-based layout. + +If you choose the gallery layout, you'll need this css file: + +- all.fine-uploader/fine-uploader-gallery.css + +...and this template file: + +- all.fine-uploader/templates/gallery.html + +If you've elected for a row-based UI layout, you'll need _these_ CSS files instead of the gallery files listed previously: + +- all.fine-uploader/fine-uploader-new.css + +...and this template file: + +- all.fine-uploader/templates/simple-thumbnails.html + +You'll also need these image files, which should be located in the same directory as the Fine Uploader CSS file: + +- all.fine-uploader/loading.gif +- all.fine-uploader/processing.gif +- all.fine-uploader/continue.gif (optional - only if using the pause upload feature) +- all.fine-uploader/edit.gif (optional - only if using the edit filename feature) +- all.fine-uploader/retry.gif (optional - only if using the retry failed upload feature) +- all.fine-uploader/trash.gif (optional - only if using the delete file feature) + +The path to these placeholder images must be specified in the set of options you pass Fine Uploader. These are only needed if you make use of the [thumbnail preview feature](../features/thumbnails.html): + +- all.fine-uploader/placeholders/not_available-generic.png +- all.fine-uploader/placeholders/waiting-generic.png + +### 4. Integrating Fine Uploader into your project + +The three most common environments for web projects are: + +1. Globally scoped (` + Upload your files + + + + + +``` + +Remember, when using the "core" build, you will need to build your own UI and update it as needed by observing Fine Uploader callbacks/events. See the API menu at the top of this page for more information on events. + +##### Traditional endpoint, UI feature set + +```html + + + + + + + + + + Fine Uploader Gallery UI + + +
    + + + ``` -##### [Azure Blob Storage](../endpoint_handlers/azure.html) -Fine Uploader comes with built in support that allows all files to be uploaded directly to Azure via the browser. -To make use of Fine Uploader Azure: +The above examples assumes you are using the gallery layout. Simply replace the above ` + Upload your files + + + + + +``` -```javascript -var uploader = new qq.azure.FineUploader({/* options go here ... */}); +Remember, when using the "core" build, you will need to build your own UI and update it as needed by observing Fine Uploader callbacks/events. See the API menu at the top of this page for more information on events. + +##### S3 endpoint, UI feature set + +```html + + + + + + + + + + Fine Uploader Gallery UI + + +
    + + + ``` -{{ alert("Fine Uploader Azure does not support IE9 and older. This is due to the fact that Azure's API does -not allow files to be uploaded via multipart encoded POST requests, which is critical for IE9 and older support. -If you need to support IE9 and older, you will need to load/use Fine Uploader with its traditional endpoint -handler if the value of `qq.supportedFeatures.ajaxUploading` is `false`.") }} - - -#### Callbacks -Like many other JavaScript libraries, Fine Uploader implements a event system -that uses callbacks to execute user-provided functions at runtime, based on -specific events. These [events are listed here](../api/events.html). - -Callbacks are defined in the `callbacks` option -property. The parameters for the callbacks are the same as listed in the -[documentation](../api/events.html). - -**Example:** - -```javascript -var uploader = new qq.FineUploader({ - // options - callbacks: { - onUpload: function (id, name) { - }, - onSubmitted: function (id, name) { - } - } -}); + +The above examples assumes you are using the gallery layout. Simply replace the above ` + Upload your files + + + + + ``` -### 3. Determine how you would like to import Fine Uploader +Remember, when using the "core" build, you will need to build your own UI and update it as needed by observing Fine Uploader callbacks/events. See the API menu at the top of this page for more information on events. + +##### Azure endpoint, UI feature set + +```html + + + + + + + + + + Fine Uploader Gallery UI + + +
    + + + +``` -You have the following options available to pull Fine Uploader into your project: +The above examples assumes you are using the gallery layout. Simply replace the above ` + Upload your files + + + + + +``` + +Remember, when using the "core" build, you will need to build your own UI and update it as needed by observing Fine Uploader callbacks/events. See the API menu at the top of this page for more information on events. + +##### All endpoint types, UI feature set + +```html + + + + + + + + + + Fine Uploader Gallery UI + + +
    +
    +
    + + + + +``` -1. CommonJS (`require`). -2. ES6 Modules (`import`). -3. Globally scoped (` + From c1ae785d1742fe0e8673fb911508f48294d13c32 Mon Sep 17 00:00:00 2001 From: RayCLin Date: Sat, 22 Oct 2016 11:06:13 +0800 Subject: [PATCH 233/344] docs(getting started): page 1 code example typos (#1677) closes #1676 --- docs/quickstart/01-getting-started.jmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart/01-getting-started.jmd b/docs/quickstart/01-getting-started.jmd index 4ad6215d1..250ec48cf 100644 --- a/docs/quickstart/01-getting-started.jmd +++ b/docs/quickstart/01-getting-started.jmd @@ -313,7 +313,7 @@ Remember, when using the "core" build, you will need to build your own UI and up - + From 75c75857c8966ea08008d507f6fce44ef9079d9d Mon Sep 17 00:00:00 2001 From: Arve Skogvold Date: Thu, 27 Oct 2016 14:35:51 +0200 Subject: [PATCH 234/344] docs(forms.jmd): Minor edit to fix invalid example code (#1679) [skip ci] --- docs/features/forms.jmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/forms.jmd b/docs/features/forms.jmd index 19e7bff16..2ed72103e 100644 --- a/docs/features/forms.jmd +++ b/docs/features/forms.jmd @@ -87,7 +87,7 @@ other, better options. Please see [modules feature page](modules.html) for more ```html - - +
    @@ -166,7 +166,7 @@ other, better options. Please see [modules feature page](modules.html) for more }); - + ``` {% endmarkdown %} From f3dedc389191b62d23bd72fdf74bbf4eed79cee5 Mon Sep 17 00:00:00 2001 From: Vlad Holubiev Date: Thu, 27 Oct 2016 22:16:35 +0300 Subject: [PATCH 235/344] docs(options-s3.jmd): Remove console.log on S3 Options page (#1681) [skip ci] --- docs/api/options-s3.jmd | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/api/options-s3.jmd b/docs/api/options-s3.jmd index a796bb3c4..cef6e8a35 100644 --- a/docs/api/options-s3.jmd +++ b/docs/api/options-s3.jmd @@ -13,7 +13,6 @@ {% block js_footer %} From 5fb466bf370dec25b39fd30a1024772e5912bd1b Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sun, 6 Nov 2016 13:24:59 -0600 Subject: [PATCH 236/344] fix(upload.handler.controller): deleted file doesn't fail fast enough Now, if a zero-sized chunk is detected (which happens if the file is deleted or no longer available during the upload), the upload will be marked as failing. fixes #1669 --- .../upload.handler.controller.js | 168 ++++++++++-------- 1 file changed, 90 insertions(+), 78 deletions(-) diff --git a/client/js/upload-handler/upload.handler.controller.js b/client/js/upload-handler/upload.handler.controller.js index affa91184..90857fd5b 100644 --- a/client/js/upload-handler/upload.handler.controller.js +++ b/client/js/upload-handler/upload.handler.controller.js @@ -86,6 +86,61 @@ qq.UploadHandlerController = function(o, namespace) { ); }, + handleFailure: function(chunkIdx, id, response, xhr) { + var name = options.getName(id); + + log("Chunked upload request failed for " + id + ", chunk " + chunkIdx); + + handler.clearCachedChunk(id, chunkIdx); + + var responseToReport = upload.normalizeResponse(response, false), + inProgressIdx; + + if (responseToReport.reset) { + chunked.reset(id); + } + else { + inProgressIdx = qq.indexOf(handler._getFileState(id).chunking.inProgress, chunkIdx); + if (inProgressIdx >= 0) { + handler._getFileState(id).chunking.inProgress.splice(inProgressIdx, 1); + handler._getFileState(id).chunking.remaining.unshift(chunkIdx); + } + } + + // We may have aborted all other in-progress chunks for this file due to a failure. + // If so, ignore the failures associated with those aborts. + if (!handler._getFileState(id).temp.ignoreFailure) { + // If this chunk has failed, we want to ignore all other failures of currently in-progress + // chunks since they will be explicitly aborted + if (concurrentChunkingPossible) { + handler._getFileState(id).temp.ignoreFailure = true; + + log(qq.format("Going to attempt to abort these chunks: {}. These are currently in-progress: {}.", JSON.stringify(Object.keys(handler._getXhrs(id))), JSON.stringify(handler._getFileState(id).chunking.inProgress))); + qq.each(handler._getXhrs(id), function(ckid, ckXhr) { + log(qq.format("Attempting to abort file {}.{}. XHR readyState {}. ", id, ckid, ckXhr.readyState)); + ckXhr.abort(); + // Flag the transport, in case we are waiting for some other async operation + // to complete before attempting to upload the chunk + ckXhr._cancelled = true; + }); + + // We must indicate that all aborted chunks are no longer in progress + handler.moveInProgressToRemaining(id); + + // Free up any connections used by these chunks, but don't allow any + // other files to take up the connections (until we have exhausted all auto-retries) + connectionManager.free(id, true); + } + + if (!options.onAutoRetry(id, name, responseToReport, xhr)) { + // If one chunk fails, abort all of the others to avoid odd race conditions that occur + // if a chunk succeeds immediately after one fails before we have determined if the upload + // is a failure or not. + upload.cleanup(id, responseToReport, xhr); + } + } + }, + hasMoreParts: function(id) { return !!handler._getFileState(id).chunking.remaining.length; }, @@ -148,95 +203,52 @@ qq.UploadHandlerController = function(o, namespace) { chunked.sendNext(id); } - handler.uploadChunk(id, chunkIdx, resuming).then( - // upload chunk success - function success(response, xhr) { - log("Chunked upload request succeeded for " + id + ", chunk " + chunkIdx); - - handler.clearCachedChunk(id, chunkIdx); - - var inProgressChunks = handler._getFileState(id).chunking.inProgress || [], - responseToReport = upload.normalizeResponse(response, true), - inProgressChunkIdx = qq.indexOf(inProgressChunks, chunkIdx); - - log(qq.format("Chunk {} for file {} uploaded successfully.", chunkIdx, id)); - - chunked.done(id, chunkIdx, responseToReport, xhr); + if (chunkData.blob.size === 0) { + log(qq.format("Chunk {} for file {} will not be uploaded, zero sized chunk.", chunkIdx, id), "error"); + chunked.handleFailure(chunkIdx, id, "File is no longer available", null); + } + else { + handler.uploadChunk(id, chunkIdx, resuming).then( + // upload chunk success + function success(response, xhr) { + log("Chunked upload request succeeded for " + id + ", chunk " + chunkIdx); - if (inProgressChunkIdx >= 0) { - inProgressChunks.splice(inProgressChunkIdx, 1); - } + handler.clearCachedChunk(id, chunkIdx); - handler._maybePersistChunkedState(id); + var inProgressChunks = handler._getFileState(id).chunking.inProgress || [], + responseToReport = upload.normalizeResponse(response, true), + inProgressChunkIdx = qq.indexOf(inProgressChunks, chunkIdx); - if (!chunked.hasMoreParts(id) && inProgressChunks.length === 0) { - chunked.finalize(id); - } - else if (chunked.hasMoreParts(id)) { - chunked.sendNext(id); - } - else { - log(qq.format("File ID {} has no more chunks to send and these chunk indexes are still marked as in-progress: {}", id, JSON.stringify(inProgressChunks))); - } - }, + log(qq.format("Chunk {} for file {} uploaded successfully.", chunkIdx, id)); - // upload chunk failure - function failure(response, xhr) { - log("Chunked upload request failed for " + id + ", chunk " + chunkIdx); + chunked.done(id, chunkIdx, responseToReport, xhr); - handler.clearCachedChunk(id, chunkIdx); + if (inProgressChunkIdx >= 0) { + inProgressChunks.splice(inProgressChunkIdx, 1); + } - var responseToReport = upload.normalizeResponse(response, false), - inProgressIdx; + handler._maybePersistChunkedState(id); - if (responseToReport.reset) { - chunked.reset(id); - } - else { - inProgressIdx = qq.indexOf(handler._getFileState(id).chunking.inProgress, chunkIdx); - if (inProgressIdx >= 0) { - handler._getFileState(id).chunking.inProgress.splice(inProgressIdx, 1); - handler._getFileState(id).chunking.remaining.unshift(chunkIdx); + if (!chunked.hasMoreParts(id) && inProgressChunks.length === 0) { + chunked.finalize(id); } - } - - // We may have aborted all other in-progress chunks for this file due to a failure. - // If so, ignore the failures associated with those aborts. - if (!handler._getFileState(id).temp.ignoreFailure) { - // If this chunk has failed, we want to ignore all other failures of currently in-progress - // chunks since they will be explicitly aborted - if (concurrentChunkingPossible) { - handler._getFileState(id).temp.ignoreFailure = true; - - log(qq.format("Going to attempt to abort these chunks: {}. These are currently in-progress: {}.", JSON.stringify(Object.keys(handler._getXhrs(id))), JSON.stringify(handler._getFileState(id).chunking.inProgress))); - qq.each(handler._getXhrs(id), function(ckid, ckXhr) { - log(qq.format("Attempting to abort file {}.{}. XHR readyState {}. ", id, ckid, ckXhr.readyState)); - ckXhr.abort(); - // Flag the transport, in case we are waiting for some other async operation - // to complete before attempting to upload the chunk - ckXhr._cancelled = true; - }); - - // We must indicate that all aborted chunks are no longer in progress - handler.moveInProgressToRemaining(id); - - // Free up any connections used by these chunks, but don't allow any - // other files to take up the connections (until we have exhausted all auto-retries) - connectionManager.free(id, true); + else if (chunked.hasMoreParts(id)) { + chunked.sendNext(id); } - - if (!options.onAutoRetry(id, name, responseToReport, xhr)) { - // If one chunk fails, abort all of the others to avoid odd race conditions that occur - // if a chunk succeeds immediately after one fails before we have determined if the upload - // is a failure or not. - upload.cleanup(id, responseToReport, xhr); + else { + log(qq.format("File ID {} has no more chunks to send and these chunk indexes are still marked as in-progress: {}", id, JSON.stringify(inProgressChunks))); } + }, + + // upload chunk failure + function failure(response, xhr) { + chunked.handleFailure(chunkIdx, id, response, xhr); } - } - ) - .done(function() { - handler.clearXhr(id, chunkIdx); - }); + ) + .done(function() { + handler.clearXhr(id, chunkIdx); + }); + } } } }, From 9c0a31269a2dd84ba99ecd242ab152e563edc9f0 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sun, 6 Nov 2016 14:08:59 -0600 Subject: [PATCH 237/344] fix(uploader.basic.api.js): file marked as retrying too late Should happen before the wait period, not after. fixes #1670 --- client/js/uploader.api.js | 2 +- client/js/uploader.basic.api.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/client/js/uploader.api.js b/client/js/uploader.api.js index 2603c4354..68bf9acbb 100644 --- a/client/js/uploader.api.js +++ b/client/js/uploader.api.js @@ -255,7 +255,7 @@ } } - if (newStatus === qq.status.UPLOAD_RETRYING) { + if (oldStatus === qq.status.UPLOAD_RETRYING && newStatus === qq.status.UPLOADING) { this._templating.hideRetry(id); this._templating.setStatusText(id); qq(this._templating.getFileContainer(id)).removeClass(this._classes.retrying); diff --git a/client/js/uploader.basic.api.js b/client/js/uploader.basic.api.js index b2b6086b4..c5a1a6eb2 100644 --- a/client/js/uploader.basic.api.js +++ b/client/js/uploader.basic.api.js @@ -1313,13 +1313,15 @@ self._preventRetries[id] = responseJSON[self._options.retry.preventRetryResponseProperty]; if (self._shouldAutoRetry(id, name, responseJSON)) { + var retryWaitPeriod = self._options.retry.autoAttemptDelay * 1000; + self._maybeParseAndSendUploadError.apply(self, arguments); self._options.callbacks.onAutoRetry(id, name, self._autoRetries[id]); self._onBeforeAutoRetry(id, name); + self._uploadData.setStatus(id, qq.status.UPLOAD_RETRYING); self._retryTimeouts[id] = setTimeout(function() { - self.log("Retrying " + name + "..."); - self._uploadData.setStatus(id, qq.status.UPLOAD_RETRYING); + self.log("Starting retry for " + name + "..."); if (callback) { callback(id); @@ -1327,7 +1329,7 @@ else { self._handler.retry(id); } - }, self._options.retry.autoAttemptDelay * 1000); + }, retryWaitPeriod); return true; } From 27b52e891673261aff1ea7cfe4cc16d90c7590ea Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sun, 6 Nov 2016 14:12:18 -0600 Subject: [PATCH 238/344] docs(statistics-and-status-updates.jmd): update retrying status def #1670 [skip ci] --- docs/features/statistics-and-status-updates.jmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/statistics-and-status-updates.jmd b/docs/features/statistics-and-status-updates.jmd index d86bb33eb..ec3ef98e1 100644 --- a/docs/features/statistics-and-status-updates.jmd +++ b/docs/features/statistics-and-status-updates.jmd @@ -23,7 +23,7 @@ For reference, here are valid status values: * `SUBMITTED` - Selected file has been successfully submitted to the uploader. In UI mode, it is also now represented in the DOM. * `QUEUED` - Uploads are in progress, but this one has not yet started due to lack of available connections. It is waiting in line for an available connection before an attempt is made to upload it. * `UPLOADING` - File is currently uploading (in progress). -* `UPLOAD_RETRYING` - The state when an upload retry is about to occur, just before the upload retry it attempted. The file will likely only be in this state for a brief moment. +* `UPLOAD_RETRYING` - The state when an upload retry is about to occur, just before the auto retry waiting period starts. * `UPLOAD_FAILED` - The upload has officially failed to upload, after all auto-retry attempts have been exhausted. * `UPLOAD_SUCCESSFUL` - The upload has officially succeeded. * `CANCELED` - The upload has been canceled. From 62bd9f208c01ad6a6a92ad9625a1e5396af79b83 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 15 Nov 2016 13:28:14 -0600 Subject: [PATCH 239/344] docs(options-ui.jmd): remove duplicate option #1689 [skip ci] --- docs/api/options-ui.jmd | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/api/options-ui.jmd b/docs/api/options-ui.jmd index 2c5f40676..e82815c64 100644 --- a/docs/api/options-ui.jmd +++ b/docs/api/options-ui.jmd @@ -121,7 +121,6 @@ A `resizeInfo` object, which will be passed to the supplied function, contains t {{ api_parent_option("thumbnails.placeholders", "thumbnails.placeholders", "", ( - ("thumbnails.placeholders.notAvailablePath", "notAvailablePath", "Absolute URL or relative path to the image to display if the preview/thumbnail could not be generated/displayed.", "String", "null",), ("thumbnails.placeholders.notAvailablePath", "notAvailablePath", "Absolute URL or relative path to the image to display if the preview/thumbnail could not be generated/displayed.", "String", "null",), ("thumbnails.placeholders.waitingPath", "waitingPath", "Absolute URL or relative path to the image to display during preview generation (modern browsers) or until the server response has been parsed (older browsers).", "String", "null",), ("thumbnails.placeholders.waitUntilResponse", "waitUntilResponse", "Set this to `true` if you want the 'waiting' placeholder image to remain in place until the server response has been parsed. This is useful if you expect to return thumbnail URLs in your upload responses for files types that cannot be previewed. This option is ignored in older browsers where client-side previews cannot be generated.", "Boolean", "false",), From 62ba817d07be86e04845e4e3c4e8d2d8ec17a215 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 15 Nov 2016 16:08:14 -0600 Subject: [PATCH 240/344] docs(options-azure.jmd): typo #1689 [skip ci] --- docs/api/options-azure.jmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/options-azure.jmd b/docs/api/options-azure.jmd index 8783118b4..f731f1e08 100644 --- a/docs/api/options-azure.jmd +++ b/docs/api/options-azure.jmd @@ -76,7 +76,7 @@ alert("The [`request.customHeaders` option](options.html#request.customHeaders) {{ api_parent_option("signature", "signature", "", ( ("signature.customHeaders", "customHeaders", "Additional headers sent along with each signature request. If you declare a function as the value, the associated file's ID will be passed to your function when it is invoked.", "Object, Function", "{}",), - ("signature.endpoint", "endpoint", "The endpoint that Fine Uploader can use to send GET for a SAS before sending requests off to S3. The blob URL and underlying method type associated with the underlying REST request will be included in the query string.", "String", "null",), + ("signature.endpoint", "endpoint", "The endpoint that Fine Uploader can use to send GET for a SAS before sending requests off to Azure. The blob URL and underlying method type associated with the underlying REST request will be included in the query string.", "String", "null",), ) )}} From a902522dd3109bc3e92e093880c91790434f2d7c Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 16 Nov 2016 15:57:32 -0600 Subject: [PATCH 241/344] docs(qq.jmd): invalid docs for qq.each iterable param #1689 [skip ci] --- docs/api/qq.jmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/qq.jmd b/docs/api/qq.jmd index bb4557a24..b62e23307 100644 --- a/docs/api/qq.jmd +++ b/docs/api/qq.jmd @@ -298,7 +298,7 @@ sure to wrap any elements before calling these methods on them. { "name": "iterable", "type": "Array or Object", - "description": "The context the function will assume." + "description": "The array or object containing items/properties to loop through." }, { "name": "callback", From 2202d3f31cf027189c6de48fff1dee5afbb015f5 Mon Sep 17 00:00:00 2001 From: Gabe Kopley Date: Fri, 25 Nov 2016 18:11:46 -0800 Subject: [PATCH 242/344] docs(02-setting_options-s3.jmd): Add comma to object literal (#1694) (now the snippet is valid JavaScript) --- docs/quickstart/02-setting_options-s3.jmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart/02-setting_options-s3.jmd b/docs/quickstart/02-setting_options-s3.jmd index 400fae04e..caba3e85c 100644 --- a/docs/quickstart/02-setting_options-s3.jmd +++ b/docs/quickstart/02-setting_options-s3.jmd @@ -189,7 +189,7 @@ other, better options. Please see [modules feature page](../features/modules.htm debug: true, element: document.getElementById('fine-uploader'), request: { - endpoint: '{ YOUR_BUCKET_NAME }.s3.amazonaws.com' + endpoint: '{ YOUR_BUCKET_NAME }.s3.amazonaws.com', accessKey: '{ YOUR_ACCESS_KEY }' }, signature: { From 4b6809e4ff3f23929a29c39c08e1b8d927b64b60 Mon Sep 17 00:00:00 2001 From: Arturo Guzman Date: Sat, 10 Dec 2016 22:06:44 -0500 Subject: [PATCH 243/344] fix(Makefile): identify.js included twice (#1691) This does not appear to cause any issues, but it does inflate the size of all built JS files a bit. --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index b6c02c08a..89f5c07c8 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,6 @@ core-files = \ $(js-src-dir)/image-support/image.js \ $(js-src-dir)/image-support/exif.js \ $(js-src-dir)/identify.js \ - $(js-src-dir)/identify.js \ $(js-src-dir)/image-support/validation.image.js \ $(js-src-dir)/session.js \ $(js-src-dir)/session.ajax.requester.js \ From 56a995f5df7ea0746870490b432a21b9f0e08f40 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sat, 10 Dec 2016 21:24:41 -0600 Subject: [PATCH 244/344] fix(Makefile): $.fineUploaderDnd missing from jQuery builds fixes #1700 --- Makefile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 89f5c07c8..eb43c9305 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,8 @@ cryptojs-files = \ $(js-3rdparty-src-dir)/crypto-js/lib-typedarrays.js jquery-files = \ - $(js-src-dir)/jquery-plugin.js + $(js-src-dir)/jquery-plugin.js \ + $(js-src-dir)/jquery-dnd.js dnd-files-only = \ $(js-src-dir)/dnd.js @@ -39,10 +40,6 @@ dnd-files = \ $(js-src-dir)/promise.js \ $(js-src-dir)/dnd.js -dnd-jquery-files = \ - $(jquery-files) \ - $(dnd-files) - core-files = \ $(js-src-dir)/util.js \ $(export-file) \ From 6baaff5adc6e5674f388c6295537b2728dac01b8 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sat, 10 Dec 2016 21:28:52 -0600 Subject: [PATCH 245/344] chore(build): field testing for 5.11.10 before release #1691 #1700 --- client/js/version.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/version.js b/client/js/version.js index 84fb5b4bd..97ff18c53 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.11.9"; +qq.version = "5.11.10-beta+1"; diff --git a/package.json b/package.json index 38cadf9f5..d413ef82a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fine-uploader", "title": "Fine Uploader", "main": "lib/traditional.js", - "version": "5.11.9", + "version": "5.11.10-beta+1", "description": "Multiple file upload plugin with progress-bar, drag-and-drop, direct-to-S3 & Azure uploading, client-side image scaling, preview generation, form support, chunking, auto-resume, and tons of other features.", "keywords": [ "amazon", From bf1a00370378ac5294483fb20637a8fce2773511 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sat, 10 Dec 2016 21:38:50 -0600 Subject: [PATCH 246/344] chore(build): release 5.11.10 #1691 #1700 --- client/js/version.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/version.js b/client/js/version.js index 97ff18c53..6119ce32c 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.11.10-beta+1"; +qq.version = "5.11.10"; diff --git a/package.json b/package.json index d413ef82a..eec8c7e73 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fine-uploader", "title": "Fine Uploader", "main": "lib/traditional.js", - "version": "5.11.10-beta+1", + "version": "5.11.10", "description": "Multiple file upload plugin with progress-bar, drag-and-drop, direct-to-S3 & Azure uploading, client-side image scaling, preview generation, form support, chunking, auto-resume, and tons of other features.", "keywords": [ "amazon", From 5f4724846fc39e1fbfe33872a9b0cc6343ec65d9 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Fri, 23 Dec 2016 21:46:32 -0600 Subject: [PATCH 247/344] docs(README.md): add twitter shield [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6aff08bf2..dfe24a9a1 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![npm](https://img.shields.io/npm/v/fine-uploader.svg)](https://www.npmjs.com/package/fine-uploader) [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) [![Stackoverflow](https://img.shields.io/badge/ask-on%20stack%20overflow-brightgreen.svg)](http://stackoverflow.com/questions/tagged/fine-uploader) +[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/fineuploader.svg?style=social&label=Follow%20%40FineUploader)](https://twitter.com/fineuploader) [**Documentation**](http://docs.fineuploader.com) | [**Examples**](http://fineuploader.com/demos) | From d0f809799560b4e03afefe7ad2bb4ed100c6f5cb Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 30 Dec 2016 15:53:35 -0600 Subject: [PATCH 248/344] =?UTF-8?q?Update=20dependencies=20to=20enable=20G?= =?UTF-8?q?reenkeeper=20=F0=9F=8C=B4=20(#1706)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(README.md): add twitter shield [skip ci] * chore(package): update dependencies https://greenkeeper.io/ * chore(package.json): start of v5.12.0 [skip ci] * chore(version.js): start of v5.12.0 --- README.md | 1 + client/js/version.js | 2 +- package.json | 16 ++++++++-------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6aff08bf2..dfe24a9a1 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![npm](https://img.shields.io/npm/v/fine-uploader.svg)](https://www.npmjs.com/package/fine-uploader) [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) [![Stackoverflow](https://img.shields.io/badge/ask-on%20stack%20overflow-brightgreen.svg)](http://stackoverflow.com/questions/tagged/fine-uploader) +[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/fineuploader.svg?style=social&label=Follow%20%40FineUploader)](https://twitter.com/fineuploader) [**Documentation**](http://docs.fineuploader.com) | [**Examples**](http://fineuploader.com/demos) | diff --git a/client/js/version.js b/client/js/version.js index 6119ce32c..22e985cd5 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.11.10"; +qq.version = "5.12.0"; diff --git a/package.json b/package.json index eec8c7e73..43f459293 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fine-uploader", "title": "Fine Uploader", "main": "lib/traditional.js", - "version": "5.11.10", + "version": "5.12.0", "description": "Multiple file upload plugin with progress-bar, drag-and-drop, direct-to-S3 & Azure uploading, client-side image scaling, preview generation, form support, chunking, auto-resume, and tons of other features.", "keywords": [ "amazon", @@ -46,17 +46,17 @@ "url": "https://github.com/FineUploader/fine-uploader/issues" }, "devDependencies": { - "clean-css": "3.4.19", + "clean-css": "3.4.23", "jscs": "3.0.7", - "jshint": "2.9.2", - "karma": "1.1.2", + "jshint": "2.9.4", + "karma": "1.3.0", "karma-firefox-launcher": "1.0.0", - "karma-mocha": "1.1.1", + "karma-mocha": "1.3.0", "karma-spec-reporter": "0.0.26", - "mocha": "3.0.1", - "node-static": "0.7.8 ", + "mocha": "3.2.0", + "node-static": "0.7.9", "pica": "latest", - "uglify-js": "2.7.0" + "uglify-js": "2.7.5" }, "scripts": { "build": "make clean; make build-all-ui", From a12cd73d40295fe4fc7cc79a1af975198aefdf44 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 6 Jan 2017 17:37:49 -0500 Subject: [PATCH 249/344] feat(validation): Allow upload with empty file (#1710) Don't reject an empty file if `validation.allowEmpty` is set to `true`. closes #903 closes #1673 --- client/js/uploader.api.js | 2 +- client/js/uploader.basic.api.js | 5 ++- client/js/uploader.basic.js | 3 +- docs/api/options.jmd | 3 +- test/static/local/helpme.js | 1 + test/unit/uploader.basic.api.js | 54 +++++++++++++++++++++++++++++++++ 6 files changed, 64 insertions(+), 4 deletions(-) diff --git a/client/js/uploader.api.js b/client/js/uploader.api.js index 68bf9acbb..d1ced64b4 100644 --- a/client/js/uploader.api.js +++ b/client/js/uploader.api.js @@ -326,7 +326,7 @@ this._templating.updateProgress(id, loaded, total); - if (Math.round(loaded / total * 100) === 100) { + if (total === 0 || Math.round(loaded / total * 100) === 100) { this._templating.hideCancel(id); this._templating.hidePause(id); this._templating.hideProgress(id); diff --git a/client/js/uploader.basic.api.js b/client/js/uploader.basic.api.js index c5a1a6eb2..8643edf36 100644 --- a/client/js/uploader.basic.api.js +++ b/client/js/uploader.basic.api.js @@ -795,6 +795,9 @@ }, _formatSize: function(bytes) { + if (bytes === 0) { + return bytes + this._options.text.sizeSymbols[0]; + } var i = -1; do { bytes = bytes / 1000; @@ -1797,7 +1800,7 @@ return validityChecker.failure(); } - if (size === 0) { + if (!this._options.validation.allowEmpty && size === 0) { this._itemError("emptyError", name, file); return validityChecker.failure(); } diff --git a/client/js/uploader.basic.js b/client/js/uploader.basic.js index dfeae24f2..ab7425e2a 100644 --- a/client/js/uploader.basic.js +++ b/client/js/uploader.basic.js @@ -39,7 +39,8 @@ maxWidth: 0, minHeight: 0, minWidth: 0 - } + }, + allowEmpty: false }, callbacks: { diff --git a/docs/api/options.jmd b/docs/api/options.jmd index 3b6148232..96d9ea0ec 100644 --- a/docs/api/options.jmd +++ b/docs/api/options.jmd @@ -233,6 +233,7 @@ alert("The `chunking.success.endpoint` option **only** applies to traditional up ( ("validation.acceptFiles", "acceptFiles", "Used by the file selection dialog. Restrict the valid file types that appear in the selection dialog by listing valid content-type specifiers here. See docs on the [accept attribute of the `` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input)", "Comma-delimited list of valid MIMEtypes", "null",), ("validation.allowedExtensions", "allowedExtensions", "Specify file valid file extensions here to restrict uploads to specific types.", "Array", "[]",), + ("validation.allowEmpty", "allowEmpty", "Allow file size of 0 bytes", "Boolean", "false"), ("validation.itemLimit", "itemLimit", "Maximum number of items that can be potentially uploaded in this session. Will reject all items that are added or retried after this limit is reached.", "Integer", "0",), ("validation.minSizeLimit", "minSizeLimit", "The minimum allowable size, in bytes, for an item.", "Integer", "0",), ("validation.sizeLimit", "sizeLimit", "The maximum allowable size, in bytes, for an item.", "Integer", "0",), @@ -241,7 +242,7 @@ alert("The `chunking.success.endpoint` option **only** applies to traditional up ("validation.image.maxHeight", "image.maxHeight", "Restrict images to a maximum height in pixels (wherever possible).", "Integer", "0",), ("validation.image.maxWidth", "image.maxWidth", "Restrict images to a maximum width in pixels (wherever possible).", "Integer", "0",), ("validation.image.minHeight", "image.minHeight", "Restrict images to a minimum height in pixels (wherever possible).", "Integer", "0",), - ("validation.image.minWidth", "image.minWidth", "Restrict images to a minimum width in pixels (wherever possible).", "Integer", "0",), + ("validation.image.minWidth", "image.minWidth", "Restrict images to a minimum width in pixels (wherever possible).", "Integer", "0",) ) ) }} diff --git a/test/static/local/helpme.js b/test/static/local/helpme.js index 9aa75c610..86c4f07e4 100644 --- a/test/static/local/helpme.js +++ b/test/static/local/helpme.js @@ -64,6 +64,7 @@ var helpme = (function () { }; var default_validation = { allowedExtensions: [], + allowEmpty: false, acceptFiles: null, sizeLimit: 0, minSizeLimit: 0, diff --git a/test/unit/uploader.basic.api.js b/test/unit/uploader.basic.api.js index 89bb719df..056eb691b 100644 --- a/test/unit/uploader.basic.api.js +++ b/test/unit/uploader.basic.api.js @@ -479,4 +479,58 @@ describe("uploader.basic.api.js", function () { uploader._handleNewFile(fileInput, 0, []); }); }); + + describe("_formatSize", function() { + beforeEach(function () { + fineuploader = new qq.FineUploaderBasic(); + }); + + it("formats 0 bytes properly", function() { + var formattedSize = fineuploader._formatSize(0); + assert.equal(formattedSize, "0kB"); + }); + + it("formats kB properly", function() { + var formattedSize = fineuploader._formatSize(789); + assert.equal(formattedSize, "0.8kB"); + }); + + it("formats MB properly", function() { + var formattedSize = fineuploader._formatSize(2123456); + assert.equal(formattedSize, "2.1MB"); + }); + + it("formats GB properly", function() { + var formattedSize = fineuploader._formatSize(9602123456); + assert.equal(formattedSize, "9.6GB"); + }); + }); + + describe("_validateFileOrBlobData", function() { + var originalFileOrInput = qq.isFileOrInput; + beforeEach(function () { + fineuploader = new qq.FineUploaderBasic(); + }); + afterEach(function() { + qq.isFileOrInput = originalFileOrInput; + }); + + it("fails if file is empty and allowEmpty is false", function(done) { + qq.isFileOrInput = function() { return true; }; + fineuploader._fileOrBlobRejected = function() {}; + var validationDescriptor = { size: 0 }; + + fineuploader._validateFileOrBlobData({}, validationDescriptor) + .then(function() { assert.fail(); }, function() { done(); }); + }); + + it("passes if file is empty and allowEmpty is true", function(done) { + fineuploader._options.validation.allowEmpty = true; + qq.isFileOrInput = function() { return true; }; + var validationDescriptor = { size: 0 }; + + fineuploader._validateFileOrBlobData({}, validationDescriptor) + .then(function() { done(); }, function() { assert.fail(); }); + }); + }); }); From 67fd668cee5c78c5b2960e062f6941937ab98678 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Fri, 6 Jan 2017 17:05:19 -0600 Subject: [PATCH 250/344] chore(Makefile): test servers may not start without changes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index eb43c9305..8f396e674 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: clean _build publish +.PHONY: clean _build publish start-test-resources-server test-resources-server.PID start-root-server root-server.PID version=$(shell node -pe "require('./package.json').version") dist-out-dir = _dist From 24ed927e890331b6c4cbe39e926be2ae81ad30e3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sat, 14 Jan 2017 14:09:43 -0600 Subject: [PATCH 251/344] =?UTF-8?q?Update=20karma=20to=20the=20latest=20ve?= =?UTF-8?q?rsion=20=F0=9F=9A=80=20(#1721)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43f459293..27d5557bf 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "clean-css": "3.4.23", "jscs": "3.0.7", "jshint": "2.9.4", - "karma": "1.3.0", + "karma": "1.4.0", "karma-firefox-launcher": "1.0.0", "karma-mocha": "1.3.0", "karma-spec-reporter": "0.0.26", From e8e59cdfe5652f0a51b97fd71fc26a2e7570d6de Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 20 Jan 2017 09:02:31 -0600 Subject: [PATCH 252/344] chore(package): update clean-css to version 3.4.24 (#1723) https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27d5557bf..331be0beb 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "url": "https://github.com/FineUploader/fine-uploader/issues" }, "devDependencies": { - "clean-css": "3.4.23", + "clean-css": "3.4.24", "jscs": "3.0.7", "jshint": "2.9.4", "karma": "1.4.0", From a5137776906693b60b68f026d306ba397c75f8a9 Mon Sep 17 00:00:00 2001 From: Matt Dziuban Date: Sat, 28 Jan 2017 01:02:41 -0500 Subject: [PATCH 253/344] feat(request-signer.js): Allow signature custom error messages (#1724) Update S3 request signer to use `error` property on response if set. Includes docs + tests. --- client/js/s3/request-signer.js | 7 +++- docs/endpoint_handlers/amazon-s3.jmd | 11 +++++++ test/unit/s3/simple-file-uploads.js | 48 ++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/client/js/s3/request-signer.js b/client/js/s3/request-signer.js index 4638e9470..cccc531fd 100644 --- a/client/js/s3/request-signer.js +++ b/client/js/s3/request-signer.js @@ -279,9 +279,14 @@ qq.s3.RequestSigner = function(o) { } } + // If the response is parsable and contains an `error` property, use it as the error message + if (response && response.error) { + isError = true; + errorMessage = response.error; + } // If we have received a parsable response, and it has an `invalid` property, // the policy document or request headers may have been tampered with client-side. - if (response && response.invalid) { + else if (response && response.invalid) { isError = true; errorMessage = "Invalid policy document or request headers!"; } diff --git a/docs/endpoint_handlers/amazon-s3.jmd b/docs/endpoint_handlers/amazon-s3.jmd index d56c03d2b..12ef68310 100644 --- a/docs/endpoint_handlers/amazon-s3.jmd +++ b/docs/endpoint_handlers/amazon-s3.jmd @@ -99,6 +99,17 @@ with a status code of 500, a Content-Type of "application/json" and the followin The above response will let Fine Uploader know that the policy document may have been tampered with client-side, and it will not send the file to S3 until the issue is addressed. +If there is any other error generating the signature, you may return a response with a status code of 500, a +Content-Type of "application/json", and a payload including an error message that will be displayed to the user: + +```javascript +{ + "error": "There was an error generating the AWS signature" +} +``` + +If your server returns an error response with no "error" attribute, a default message will be used. + #### Signing the policy - version 2 signatures If your server determines that the policy document is accurate, it should then base-64 encode the policy document, diff --git a/test/unit/s3/simple-file-uploads.js b/test/unit/s3/simple-file-uploads.js index 1d207b19c..29085323f 100644 --- a/test/unit/s3/simple-file-uploads.js +++ b/test/unit/s3/simple-file-uploads.js @@ -153,6 +153,30 @@ if (qqtest.canDownloadFileAsBlob) { done(); }); }); + + it("uses the error field on the signature request response if provided", function(done) { + assert.expect(2, done); + + var uploader = new qq.s3.FineUploaderBasic({ + request: typicalRequestOption, + signature: v4SignatureOption, + callbacks: { + onError: function(id, name, errorReason) { + assert.equal(errorReason, "error message"); + } + } + } + ); + + startTypicalTest(uploader, function(signatureRequest, policyDoc, uploadRequest, conditions) { + var s3RequestSigner = new qq.s3.RequestSigner({ + expectingPolicy: true, + signatureSpec: v4SignatureOption, + }); + + signatureRequest.respond(500, null, JSON.stringify({error: "error message"})); + }); + }); }); it("test most basic upload w/ signature request", function(done) { @@ -202,6 +226,30 @@ if (qqtest.canDownloadFileAsBlob) { }); }); + it("uses the error field on the signature request response if provided", function(done) { + assert.expect(2, done); + + var uploader = new qq.s3.FineUploaderBasic({ + request: typicalRequestOption, + signature: v2SignatureOption, + callbacks: { + onError: function(id, name, errorReason) { + assert.equal(errorReason, "error message"); + } + } + } + ); + + startTypicalTest(uploader, function(signatureRequest, policyDoc, uploadRequest, conditions) { + var s3RequestSigner = new qq.s3.RequestSigner({ + expectingPolicy: true, + signatureSpec: v2SignatureOption, + }); + + signatureRequest.respond(500, null, JSON.stringify({error: "error message"})); + }); + }); + it("handles slow browser system clock", function(done) { var clockDrift = 1000 * 60 * 60, // slow by 1 hour uploader = new qq.s3.FineUploaderBasic({ From 42b767dd097172ad221f6f07465280cb5a8cb52e Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sat, 28 Jan 2017 00:14:39 -0600 Subject: [PATCH 254/344] chore(package.json): upgrade to clean-css 4.x closes #1732 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 331be0beb..69415b969 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fine-uploader", "title": "Fine Uploader", "main": "lib/traditional.js", - "version": "5.12.0", + "version": "5.13.0", "description": "Multiple file upload plugin with progress-bar, drag-and-drop, direct-to-S3 & Azure uploading, client-side image scaling, preview generation, form support, chunking, auto-resume, and tons of other features.", "keywords": [ "amazon", @@ -46,7 +46,7 @@ "url": "https://github.com/FineUploader/fine-uploader/issues" }, "devDependencies": { - "clean-css": "3.4.24", + "clean-css-cli": "4.0.0", "jscs": "3.0.7", "jshint": "2.9.4", "karma": "1.4.0", From e7930e716210a7ec669240776a757da1c4fc02c2 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sat, 28 Jan 2017 00:21:49 -0600 Subject: [PATCH 255/344] chore(version.js): forgot to update all files w/ new version closes #1732 --- client/js/version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/js/version.js b/client/js/version.js index 22e985cd5..329e6a42f 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.12.0"; +qq.version = "5.13.0"; From 503d7e1e6334f91bfd178dced7df687cf7e559c5 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sun, 29 Jan 2017 19:28:59 -0600 Subject: [PATCH 256/344] chore(package.json): update karma to version 1.4.1 (#1736) https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 69415b969..0ee149fc0 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "clean-css-cli": "4.0.0", "jscs": "3.0.7", "jshint": "2.9.4", - "karma": "1.4.0", + "karma": "1.4.1", "karma-firefox-launcher": "1.0.0", "karma-mocha": "1.3.0", "karma-spec-reporter": "0.0.26", From ad5bffc499fa454a7d31db815be65dada3aa1258 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sun, 29 Jan 2017 23:27:00 -0600 Subject: [PATCH 257/344] feat(uploader.basic.api.js): removeFileRef method (#1737) When called, this deleted the reference to the Blob/File (along with all other file state tracked by the upload handler). closes #1711 * docs(methods.jmd): document removeFileRef method closes #1711 --- client/js/uploader.basic.api.js | 4 ++++ client/js/version.js | 2 +- docs/api/methods.jmd | 14 ++++++++++++++ package.json | 2 +- test/unit/simple-file-uploads.js | 25 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/client/js/uploader.basic.api.js b/client/js/uploader.basic.api.js index 8643edf36..6468d77a4 100644 --- a/client/js/uploader.basic.api.js +++ b/client/js/uploader.basic.api.js @@ -308,6 +308,10 @@ return false; }, + removeFileRef: function(id) { + this._handler.expunge(id); + }, + reset: function() { this.log("Resetting uploader..."); diff --git a/client/js/version.js b/client/js/version.js index 22e985cd5..66f550b03 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.12.0"; +qq.version = "5.14.0-beta1"; diff --git a/docs/api/methods.jmd b/docs/api/methods.jmd index 755cbdd71..cd1d6f822 100644 --- a/docs/api/methods.jmd +++ b/docs/api/methods.jmd @@ -30,6 +30,11 @@ $(document).ready(function() { {% markdown %} ## Core {% endmarkdown %} + +{{ +alert("If you pass a large `Blob` that was created using JavaScript in the browser into `addFiles`, you should consider calling the [`removeFileRef` method](#removeFileRef) after the file has been successfully uploaded to free up any memory consumed by the Blob.") +}} + {{ api_method("addFiles", "addFiles (files[, params[, endpoint]])", "Submit one or more files to the uploader. A `BlobWrapper` object: @@ -352,6 +357,15 @@ A `resizeInfo` object, which will be passed to the supplied function, contains t ]) }} +{{ api_method("removeFileRef", "removeFileRef (id)", "", +[ + { + "name": "id", + "type": "Integer", + "description": "Remove internal reference to the associated Blob/File object. For Blobs that are created via JavaScript in the browser, this will free up all consumed memory." + } +]) }} + {{ api_method("reset", "reset ()", "Reset Fine Uploader", null, null) }} {{ api_method("retry", "retry (id)", "Attempt to upload a specific item again.", diff --git a/package.json b/package.json index 0ee149fc0..1a79a1301 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fine-uploader", "title": "Fine Uploader", "main": "lib/traditional.js", - "version": "5.13.0", + "version": "5.14.0-beta1", "description": "Multiple file upload plugin with progress-bar, drag-and-drop, direct-to-S3 & Azure uploading, client-side image scaling, preview generation, form support, chunking, auto-resume, and tons of other features.", "keywords": [ "amazon", diff --git a/test/unit/simple-file-uploads.js b/test/unit/simple-file-uploads.js index 836a3fc46..6da26f4e3 100644 --- a/test/unit/simple-file-uploads.js +++ b/test/unit/simple-file-uploads.js @@ -478,5 +478,30 @@ if (qqtest.canDownloadFileAsBlob) { uploader.addFiles(canvasWrapper); }); }); + + it("removes reference to a Blob via API", function(done) { + qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { + fileTestHelper.mockXhr(); + + var request, + uploader = new qq.FineUploaderBasic({ + autoUpload: false, + request: { endpoint: testUploadEndpoint }, + callbacks: { + onComplete: function(id) { + assert.ok(uploader.getFile(id)); + uploader.removeFileRef(id); + assert.ok(!uploader.getFile(id)); + done(); + } + } + }); + + uploader.addFiles({name: "test", blob: blob}); + uploader.uploadStoredFiles(); + + fileTestHelper.getRequests()[0].respond(200, null, JSON.stringify({success: true})); + }); + }); }); } From 6df2264bf18919a6b04ed43fc68e5a79dd2ebb9e Mon Sep 17 00:00:00 2001 From: Alexandru Bucur Date: Tue, 31 Jan 2017 19:18:03 +0200 Subject: [PATCH 258/344] feat(uploader.basic.api.js): intial setStatus() API method implementation Initially, only qq.status.DELETED and qq.status.DELETE_FAILED are supported. All other statuses will throw. This can be used to mark a file as deleted, or to indicate that a delete attempt failed if you are using delete file logic outside of Fine Uploader's control. This will update the UI by removing the file if you are using Fine Uploader UI as well. closes #1738 --- .gitignore | 1 + client/js/uploader.basic.api.js | 76 ++++++++++++++++----- docs/api/methods.jmd | 34 +++++++++- test/unit/set-status.js | 113 ++++++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 17 deletions(-) create mode 100644 test/unit/set-status.js diff --git a/.gitignore b/.gitignore index 11982d1ef..a8c2d3a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ bin/ src npm-debug.log +Vagrantfile test/dev/handlers/s3/composer.lock test/dev/handlers/traditional/files diff --git a/client/js/uploader.basic.api.js b/client/js/uploader.basic.api.js index 6468d77a4..cf507059e 100644 --- a/client/js/uploader.basic.api.js +++ b/client/js/uploader.basic.api.js @@ -397,6 +397,35 @@ return this._uploadData.uuidChanged(id, newUuid); }, + /** + * Expose the internal status of a file id to the public api for manual state changes + * @public + * + * @param {Number} id, + * @param {String} newStatus + * + * @todo Implement the remaining methods + */ + setStatus: function(id, newStatus) { + var fileRecord = this.getUploads({id: id}); + if (!fileRecord) { + throw new qq.Error(id + " is not a valid file ID."); + } + + switch (newStatus) { + case qq.status.DELETED: + this._onDeleteComplete(id, null, false); + break; + case qq.status.DELETE_FAILED: + this._onDeleteComplete(id, null, true); + break; + default: + var errorMessage = "Method setStatus called on '" + name + "' not implemented yet for " + newStatus; + this.log(errorMessage); + throw new qq.Error(errorMessage); + } + }, + uploadStoredFiles: function() { if (this._storedIds.length === 0) { this._itemError("noFilesError"); @@ -1067,6 +1096,35 @@ }); }, + _handleDeleteSuccess: function(id) { + if (this.getUploads({id: id}).status !== qq.status.DELETED) { + var name = this.getName(id); + + this._netUploadedOrQueued--; + this._netUploaded--; + this._handler.expunge(id); + this._uploadData.setStatus(id, qq.status.DELETED); + this.log("Delete request for '" + name + "' has succeeded."); + } + }, + + _handleDeleteFailed: function(id, xhrOrXdr) { + var name = this.getName(id); + + this._uploadData.setStatus(id, qq.status.DELETE_FAILED); + this.log("Delete request for '" + name + "' has failed.", "error"); + + // Check first if xhrOrXdr is actually passed or valid + // For error reporting, we only have access to the response status if this is not + // an `XDomainRequest`. + if (!xhrOrXdr || xhrOrXdr.withCredentials === undefined) { + this._options.callbacks.onError(id, name, "Delete request failed", xhrOrXdr); + } + else { + this._options.callbacks.onError(id, name, "Delete request failed with response code " + xhrOrXdr.status, xhrOrXdr); + } + }, + // Creates an extra button element _initExtraButton: function(spec) { var button = this._createUploadButton({ @@ -1420,24 +1478,10 @@ var name = this.getName(id); if (isError) { - this._uploadData.setStatus(id, qq.status.DELETE_FAILED); - this.log("Delete request for '" + name + "' has failed.", "error"); - - // For error reporting, we only have access to the response status if this is not - // an `XDomainRequest`. - if (xhrOrXdr.withCredentials === undefined) { - this._options.callbacks.onError(id, name, "Delete request failed", xhrOrXdr); - } - else { - this._options.callbacks.onError(id, name, "Delete request failed with response code " + xhrOrXdr.status, xhrOrXdr); - } + this._handleDeleteFailed(id, xhrOrXdr); } else { - this._netUploadedOrQueued--; - this._netUploaded--; - this._handler.expunge(id); - this._uploadData.setStatus(id, qq.status.DELETED); - this.log("Delete request for '" + name + "' has succeeded."); + this._handleDeleteSuccess(id); } }, diff --git a/docs/api/methods.jmd b/docs/api/methods.jmd index cd1d6f822..dedd9cdfa 100644 --- a/docs/api/methods.jmd +++ b/docs/api/methods.jmd @@ -25,7 +25,7 @@ $(document).ready(function() { # Methods Traditional {: .page-header } {% endmarkdown %} -
    +
    {% markdown %} ## Core @@ -548,6 +548,38 @@ A `resizeInfo` object, which will be passed to your (optional) `customResizer` f } ], null) }} +{{ api_method("setStatus", "setStatus (id, newStatus])", +""" Modify the status of an file. + +The status values correspond to those found in the `qq.status` object. For reference: + +* `SUBMITTED` +* `QUEUED` +* `UPLOADING` +* `UPLOAD_RETRYING` +* `UPLOAD_FAILED` +* `UPLOAD_SUCCESSFUL` +* `CANCELED` +* `REJECTED` +* `DELETED` +* `DELETING` +* `DELETE_FAILED` +* `PAUSED` + +""", +[ + { + "name": "id", + "type": "Integer", + "description": "The file id." + }, + { + "name": "newStatus", + "type": "String", + "description": "An integer corresponding to a file." + } +], null) }} + {{ api_method("uploadStoredFiles", "uploadStoredFiles ()", "Begin uploading all queued items. Throws a `NoFilesError` of there are no items to upload.", null, null) }}
    diff --git a/test/unit/set-status.js b/test/unit/set-status.js new file mode 100644 index 000000000..ff7fe7fba --- /dev/null +++ b/test/unit/set-status.js @@ -0,0 +1,113 @@ +/* globals describe, beforeEach, qq, qqtest, assert, helpme, it */ + +describe("set-status.js", function() { + "use strict"; + + var testUploadEndpoint = "/test/upload", + fileTestHelper = helpme.setupFileTests(); + + var initialFiles = [{ + name: "left.jpg", + uuid: "e109af57-848b-4c2a-bca8-051374d01db1" + }, { + name: "right.jpg", + uuid: "949d16c3-727a-4c3c-8c0f-23404dcd6f3b" + }]; + + it("testing status change of DELETED with initialFiles", function() { + var uploader = new qq.FineUploaderBasic(); + uploader.addInitialFiles(initialFiles); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[0]; + + uploader.setStatus(file.id, qq.status.DELETED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[0]; + + assert.equal(1, uploader.getNetUploads()); + assert.equal(qq.status.DELETED, file.status); + + // ensure same file can't be "deleted" twice + uploader.setStatus(file.id, qq.status.DELETED); + assert.equal(1, uploader.getNetUploads()); + }); + + it("testing status change of DELETE_FAILED with initialFiles", function() { + var uploader = new qq.FineUploaderBasic(); + uploader.addInitialFiles(initialFiles); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[1]; + + uploader.setStatus(file.id, qq.status.DELETE_FAILED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[1]; + + assert.equal(2, uploader.getNetUploads()); + assert.equal(qq.status.DELETE_FAILED, file.status); + }); + + it("testing status change of DELETED with mock uploader", function(done) { + var uploader = new qq.FineUploaderBasic({ + autoUpload: true, + request: { + endpoint: testUploadEndpoint + } + }); + + qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { + fileTestHelper.mockXhr(); + + uploader.addFiles({name: "test", blob: blob}); + uploader.uploadStoredFiles(); + fileTestHelper.getRequests()[0].respond(201, null, JSON.stringify({success: true})); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[0]; + + uploader.setStatus(file.id, qq.status.DELETED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[0]; + + assert.equal(0, uploader.getNetUploads()); + assert.equal(qq.status.DELETED, file.status); + done(); + }); + + }); + + it("testing status change of DELETED with mock uploader", function(done) { + var uploader = new qq.FineUploaderBasic({ + autoUpload: true, + request: { + endpoint: testUploadEndpoint + } + }); + + qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { + fileTestHelper.mockXhr(); + + uploader.addFiles({name: "test", blob: blob}); + uploader.uploadStoredFiles(); + fileTestHelper.getRequests()[0].respond(201, null, JSON.stringify({success: true})); + + var uploaderFiles = uploader.getUploads(); + var file = uploaderFiles[0]; + + uploader.setStatus(file.id, qq.status.DELETE_FAILED); + + uploaderFiles = uploader.getUploads(); + file = uploaderFiles[0]; + + assert.equal(1, uploader.getNetUploads()); + assert.equal(qq.status.DELETE_FAILED, file.status); + done(); + }); + + }); + +}); From e372f8971eac77ff128285b6103f949fb622203b Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 1 Feb 2017 20:56:45 -0600 Subject: [PATCH 259/344] chore(build): 5.14.0-beta2 #1738 --- client/js/version.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/version.js b/client/js/version.js index 66f550b03..33a71f1e4 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.14.0-beta1"; +qq.version = "5.14.0-beta2"; diff --git a/package.json b/package.json index 1a79a1301..3eb88d70d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fine-uploader", "title": "Fine Uploader", "main": "lib/traditional.js", - "version": "5.14.0-beta1", + "version": "5.14.0-beta2", "description": "Multiple file upload plugin with progress-bar, drag-and-drop, direct-to-S3 & Azure uploading, client-side image scaling, preview generation, form support, chunking, auto-resume, and tons of other features.", "keywords": [ "amazon", From ac51eab497a43b38bc334bcbea51bbd2c69f2bf8 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 1 Feb 2017 21:03:06 -0600 Subject: [PATCH 260/344] docs(methods.jmd): Mention the only statuses that are valid ATM closes #1739 [skip ci] --- docs/api/methods.jmd | 18 ++++-------------- .../features/statistics-and-status-updates.jmd | 2 +- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/docs/api/methods.jmd b/docs/api/methods.jmd index dedd9cdfa..0fd7b789b 100644 --- a/docs/api/methods.jmd +++ b/docs/api/methods.jmd @@ -551,20 +551,10 @@ A `resizeInfo` object, which will be passed to your (optional) `customResizer` f {{ api_method("setStatus", "setStatus (id, newStatus])", """ Modify the status of an file. -The status values correspond to those found in the `qq.status` object. For reference: - -* `SUBMITTED` -* `QUEUED` -* `UPLOADING` -* `UPLOAD_RETRYING` -* `UPLOAD_FAILED` -* `UPLOAD_SUCCESSFUL` -* `CANCELED` -* `REJECTED` -* `DELETED` -* `DELETING` -* `DELETE_FAILED` -* `PAUSED` +The status values correspond to those found in the `qq.status` object. Currently, the following status values may be set via this method: + +* `qq.status.DELETED` +* `qq.status.DELETE_FAILED` """, [ diff --git a/docs/features/statistics-and-status-updates.jmd b/docs/features/statistics-and-status-updates.jmd index ec3ef98e1..6c632d7df 100644 --- a/docs/features/statistics-and-status-updates.jmd +++ b/docs/features/statistics-and-status-updates.jmd @@ -1,7 +1,7 @@ {% extends "_templates/base.html" %} {% set page_title = "Statistics and Status Updates" %} {% block sidebar %} -{{ api_links(methods=['getUploads'], events=['statusChange']) }} +{{ api_links(methods=['getUploads', 'setStatus'], events=['statusChange']) }} {% endblock %} {% block content %} {% markdown %} From 2db4a665adc8a07dd9990deb74dcc2039eab2b1f Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 1 Feb 2017 21:04:16 -0600 Subject: [PATCH 261/344] docs(methods.jmd): invalid character in setStatus signature #1739 [skip ci] --- docs/api/methods.jmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/methods.jmd b/docs/api/methods.jmd index 0fd7b789b..abdd8bd57 100644 --- a/docs/api/methods.jmd +++ b/docs/api/methods.jmd @@ -548,7 +548,7 @@ A `resizeInfo` object, which will be passed to your (optional) `customResizer` f } ], null) }} -{{ api_method("setStatus", "setStatus (id, newStatus])", +{{ api_method("setStatus", "setStatus (id, newStatus)", """ Modify the status of an file. The status values correspond to those found in the `qq.status` object. Currently, the following status values may be set via this method: From dd770a4acccc50b0a5b64afbd9b4dc8af1a5d442 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 7 Feb 2017 09:04:06 -0600 Subject: [PATCH 262/344] chore(package): update clean-css-cli to version 4.0.5 (#1746) Closes #1745 https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3eb88d70d..5bd519efa 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "url": "https://github.com/FineUploader/fine-uploader/issues" }, "devDependencies": { - "clean-css-cli": "4.0.0", + "clean-css-cli": "4.0.5", "jscs": "3.0.7", "jshint": "2.9.4", "karma": "1.4.1", From 51d2b252fcca6799d65b813faa655a497794e46c Mon Sep 17 00:00:00 2001 From: fromkeith Date: Sun, 12 Feb 2017 19:27:37 -0700 Subject: [PATCH 263/344] fix(Makefile): npm path not properly set for cygwin (#1698) Detect npm-path properly in cygwin (fixes windows build). Looks for '_NT' to detect if we are on cygwin or not. --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8f396e674..342f1f1a7 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,14 @@ version=$(shell node -pe "require('./package.json').version") dist-out-dir = _dist pub-dir = $(dist-out-dir)/$(version) -npm-bin = $(shell npm bin) + +# properly get npm-bin in cygwin (Eg. CYGWIN_NT-10.0) +platform = $(shell uname -s) +ifeq ($(findstring _NT,$(platform)),_NT) + npm-bin = $(shell cygpath -u $(shell npm bin)) +else + npm-bin = $(shell npm bin) +endif build-out-dir = _build src-dir = client From a5486c6b779b7135ecda5cc9a7aa03131254c19c Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sun, 12 Feb 2017 20:37:55 -0600 Subject: [PATCH 264/344] chore(package): update clean-css-cli to version 4.0.6 (#1749) https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5bd519efa..0fb774359 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "url": "https://github.com/FineUploader/fine-uploader/issues" }, "devDependencies": { - "clean-css-cli": "4.0.5", + "clean-css-cli": "4.0.6", "jscs": "3.0.7", "jshint": "2.9.4", "karma": "1.4.1", From 1c7bb259c54ff18570c1e1951c76cdc8a578f676 Mon Sep 17 00:00:00 2001 From: Sukhdeep Singh Date: Sun, 12 Feb 2017 21:47:19 -0500 Subject: [PATCH 265/344] feat(fine-uploader.d.ts): initial Typescript definitions (#1719) This includes: * Typescript definition file that covers the entire API. * Updated Makefile to include typescript directory in build output. * typescript/fine-uploader.test.ts. * Various documentation fixes. --- Makefile | 1 + client/typescript/fine-uploader.d.ts | 3380 +++++++++++++++++++++++ client/typescript/fine-uploader.test.ts | 110 + docs/api/methods.jmd | 2 +- package.json | 1 + 5 files changed, 3493 insertions(+), 1 deletion(-) create mode 100644 client/typescript/fine-uploader.d.ts create mode 100644 client/typescript/fine-uploader.test.ts diff --git a/Makefile b/Makefile index 342f1f1a7..17752a38e 100644 --- a/Makefile +++ b/Makefile @@ -364,6 +364,7 @@ setup-dist: mkdir -p $(pub-dir) cp LICENSE README.md package.json $(pub-dir) cp -pR $(src-dir)/commonjs/ $(pub-dir)/lib/ + cp -pR $(src-dir)/typescript $(pub-dir)/ copy-build-to-dist: mkdir -p $(pub-dir)/$(PUB-SUBDIR) diff --git a/client/typescript/fine-uploader.d.ts b/client/typescript/fine-uploader.d.ts new file mode 100644 index 000000000..4ef8d5755 --- /dev/null +++ b/client/typescript/fine-uploader.d.ts @@ -0,0 +1,3380 @@ +// Type definitions for FineUploader 5.x.x +// Project: http://fineuploader.com/ +// Definitions by: Sukhdeep Singh + +/** + * The FineUploader namespace contains all the methods, options, events and types + */ +declare namespace FineUploader { + + /* ========================================================== CORE & UI ===================================================================== */ + /** + * type for `resizeInfo` object + */ + interface ResizeInfo { + /** + * The original `File` or `Blob` object, if available. + */ + blob?: File | Blob; + /** + * Desired height of the image after the resize operation. + */ + height?: number; + /** + * The original HTMLImageElement object, if available. + */ + image?: HTMLImageElement; + /** + * `HTMLCanvasElement` element containing the original image data (not resized). + */ + sourceCanvas?: HTMLCanvasElement; + /** + * `HTMLCanvasElement` element containing the `HTMLCanvasElement` that should contain the resized image. + */ + targetCanvas?: HTMLCanvasElement; + /** + * Desired width of the image after the resize operation. + */ + width?: number; + } + + /** + * Callback type for `customResizer` parameter + */ + interface CustomResizerCallBack { + /** + * Contribute this function to manually resize images using alternate 3rd party libraries + * + * @param ResizeInfo resizeInfo : the ResizeInfo object containing all the resize values/options + * @returns Promise : Once the resize is complete, the function must return a promise + */ + (resizeInfo: ResizeInfo): PromiseOptions; + } + + /** + * A BlobWrapper object type + */ + interface BlobWrapper { + /** + * the bytes of the `Blob` object being uploaded + */ + blob?: Blob; + /** + * the name of the `Blob` + */ + name?: string; + } + + /** + * A CanvasWrapper Object type + */ + interface CanvasWrapper { + /** + * the `` to be converted to a file & then uploaded + */ + canvas?: HTMLCanvasElement; + /** + * the name to assign to the created file + */ + name?: string; + /** + * `1`-`100` value indicating the desired quality of the converted file (only for `image/jpeg`) + */ + quality?: number; + /** + * MIME type of the file to create from this `` + */ + type?: MimeType; + } + + /** + * Resumable file object type + */ + interface ResumableFileObject { + /** + * filename + */ + name?: string; + /** + * the unique id + */ + uuid?: number; + /** + * the index of the part where the resume will start from + */ + partIdx?: number; + } + + /** + * Resumable file object type for S3 + */ + interface S3ResumableFileObject extends ResumableFileObject { + /** + * The associated object's S3 key + */ + key?: string; + } + + /** + * Resumable file object type for Azure + */ + interface AzureResumableFileObject extends ResumableFileObject { + /** + * The associated file's blob name in Azure Blob Storage + */ + key?: string; + } + + /** + * type for getUploads method's filter parameter + */ + interface UploadFilter { + /** + * the id of the file + */ + id?: number | number[]; + /** + * the uuid of the file + */ + uuid?: number | number[]; + /** + * status of the file + */ + status?: string | string[]; + } + + /** + * type for getUploads method's return object + */ + interface FoundUploadItems extends UploadFilter { + /** + * the name of the file + */ + name?: string; + /** + * the size of the file + */ + size?: string; + } + + /** + * ScaleImageOptions + */ + interface ScaleImageOptions { + /** + * required + */ + maxSize: number; + /** + * @default `true` + */ + orient?: boolean; + /** + * defaults to the type of the reference image + */ + type?: string; + /** + * number between `0` and `100` + * + * @default `80` + */ + quality?: number; + /** + * @default `false` + */ + includeExif?: boolean; + /** + * Ignored if the current browser does not support image previews. + * + * If you want to use an alternate library to resize the image, you must contribute a function for this option that returns a `Promise`. + * + * Once the resize is complete, your promise must be fulfilled. + * You may, of course, reject your returned `Promise` is the resize fails in some way. + */ + customResizer?: CustomResizerCallBack; + } + + /** + * formatFileName function type + */ + interface FormatFileNameFuncton { + (fileOrBlobName: string): String | void; + } + + /** + * BlobsOptions + */ + interface BlobsOptions { + /** + * The default name to be used for nameless `Blob`s + * + * @default `Misc data` + */ + defaultName?: string; + } + + /** + * CameraOptions + */ + interface CameraOptions { + /** + * `null` allows camera access on the default button in iOS. + * + * Otherwise provide an extra button container element to target + * + * @default `null` + */ + button?: HTMLElement; + /** + * Enable or disable camera access on iOS (iPod, iPhone, and iPad) devices. + * + * ###Note: + * Enabling this will disable multiple file selection + * + * @default `false` + */ + ios?: boolean; + } + + /** + * ConcurrentOptions + */ + interface ConcurrentOptions { + /** + * Allow multiple chunks to be uploaded simultaneously per file + * + * @default `false` + */ + enabled?: boolean; + } + + /** + * ChunkingOptions + */ + interface ChunkingOptions { + /** + * concurrent Chunking options + */ + concurrent?: ConcurrentOptions; + /** + * Enable or disable splitting the file separate chunks. Each chunks is sent in a separate requested + * + * @default `false` + */ + enabled?: boolean; + /** + * Ensure every file is uploaded in chunks, even if the file can only be split up into 1 chunk. + * + * Does not apply if chunking is not possible in the current browser + * + * @default `false` + */ + mandatory?: boolean; + /** + * The maximum size of each chunk, in bytes + * + * @default `2000000` + */ + partSize?: number; + /** + * ParamNamesOptions + */ + paramNames?: ParamNamesOptions; + /** + * SuccessOptions + */ + success?: SuccessOptions; + + } + + /** + * ParamNamesOptions + */ + interface ParamNamesOptions { + /** + * Name of the parameter passed with a chunked request that specifies the size in bytes of the associated chunk + * + * @default `'qqchunksize'` + */ + chunkSize?: string; + /** + * Name of the parameter passed with a chunked request that specifies the starting byte of the associated chunk + * + * @default `'qqpartbyteoffset'` + */ + partByteOffset?: string; + /** + * Name of the parameter passed with a chunked request that specifies the index of the associated partition + * + * @default `'qqpartindex'` + */ + partIndex?: string; + /** + * Name of the parameter passed with a chunked request that specifies the total number of chunks associated with the `File` or `Blob` + * + * @default `'qqtotalparts'` + */ + totalParts?: string; + /** + * Sent with the first request of the resume with a value of `true` + * + * @default `'qqresume'` + */ + resuming?: string; + /** + * totalFileSize + * + * @default `'qqtotalfilesize'` + */ + totalFileSize?: string; + } + + /** + * SuccessOptions + */ + interface SuccessOptions { + /** + * Endpoint to send a POST after all chunks have been successfully uploaded for each file. + * + * Required if the `concurrent.enabled` option is set + * + * @default `null` + */ + endpoint?: string; + } + + /** + * CorsOptions + */ + interface CorsOptions { + /** + * Enable or disable cross-origin requests from IE9 and older where XDomainRequest must be used + * + * @default `false` + */ + allowXdr?: boolean; + /** + * Enable or disable cross-domain requests + * + * @default `false` + */ + expected?: boolean; + /** + * Enable or disable sending credentials along with each cross-domain request. Ignored if allowXdr is true and IE9 is being used + * + * @default `false` + */ + sendCredentials?: boolean; + } + + /** + * DeleteFileOptions + */ + interface DeleteFileOptions { + /** + * Any additional headers to attach to all delete file requests + * + * @default `{}` + */ + customHeaders?: any; + /** + * Enable or disable deletion of uploaded files + * + * @default `false` + */ + enabled?: boolean; + /** + * The endpoint to which delete file requests are sent. + * + * @default `'/server/upload'` + */ + endpoint?: string; + /** + * Set the method to use for delete requests. + * + * Accepts `'POST'` or `'DELETE'` + * + * @default `'DELETE'` + */ + method?: string; + /** + * Any additional parameters to attach to delete file requests + * + * @default `{}` + */ + params?: any; + } + + /** + * ExtraButtonsOptions + */ + interface ExtraButtonsOptions { + /** + * The container element for the upload button + * + * @default `undefined` + */ + element: HTMLElement; + /** + * This value will be used when creating the `title` attribute for the underlying ``. + * + * If not provided, the `text.fileInputTitle` option will be used instead + * + * @default `'file input'` + */ + fileInputTitle?: string; + /** + * `true` to allow folders to be selected, `false` to allow files to be selected. + * + * @default `false` + */ + folders?: boolean; + /** + * Specify to override the default `multiple` value + * + * @default `true` + */ + multiple?: boolean; + /** + * Specify to override the default `validation` option specified. + * + * Any `validation` properties not specified will be inherited from the default `validation` option + * + * @default `validation` + */ + validation?: any; + } + + /** + * FormOptions + */ + interface FormOptions { + /** + * This can be the ID of the or a reference to the element + * + * @default `'qq-form'` + */ + element?: string | HTMLElement; + /** + * If Fine Uploader is able to attach to a form, this value takes the place of the base `autoUpload` option + * + * @default `false` + */ + autoUpload?: boolean; + /** + * Set this to `false` if you do not want Fine Uploader to intercept attempts to submit your form. + * + * By default, Fine Uploader will intercept submit attempts and instead upload all submitted files, including data from your form in each upload request + * + * @default `true` + */ + interceptSubmit?: boolean; + } + + /** + * Messages + */ + interface Messages { + /** + * Text passed to the error event handler if a submitted item is zero bits + * + * @default `'{file} is empty, please select files again without it.'` + */ + emptyError?: string; + /** + * Text passed to the error event handler if an image is too tall + * + * @default `'Image is too tall.'` + */ + maxHeightImageError?: string; + /** + * Text passed to the error event handler if an image is too wide + * + * @default `'Image is too wide.'` + */ + maxWidthImageError?: string; + /** + * Text passed to the error event handler if an image is not tall enough + * + * @default `'Image is not tall enough.'` + */ + minHeightImageError?: string; + /** + * Text passed to the error event handler if an image is not wide enough + * + * @default `'Image is not wide enough.'` + */ + minWidthImageError?: string; + /** + * Text passed to the error event handler if the item is too small + * + * @default `'{file} is too small, minimum file size is {minSizeLimit}.'` + */ + minSizeError?: string; + /** + * Text passed to the error event handler if any empty array of items is submitted + * + * @default `'No files to upload.'` + */ + noFilesError?: string; + /** + * Text displayed to the user when they attempt to leave the page while uploads are still in progress + * + * @default `'The files are being uploaded, if you leave now the upload will be canceled.'` + */ + onLeave?: string; + /** + * Text passed to the error event handler if a retry attempt is declared a failed due to a violation of the `validation.itemLimit` rule + * + * @default `'Retry failed - you have reached your file limit.'` + */ + retryFailTooManyItemsError?: string; + /** + * Text passed to the error event handler if a submitted item is too large. + * + * @default `'{file} is too large, maximum file size is {sizeLimit}.'` + */ + sizeError?: string; + /** + * Text passed to the error event handler if a submit is ignored due to a violation of the `validation.itemLimit` rules + * + * @default `'Too many items ({netItems}) would be uploaded. Item limit is {itemLimit}.'` + */ + tooManyItemsError?: string; + /** + * Text passed to the error event handler if an invalid file type is detected + * + * @default `'{file} has an invalid extension. Valid extension(s): {extensions}.'` + */ + typeError?: string; + /** + * Message displayed if the browser is iOS8 Safari and the corresponding workarounds option is not disabled + * + * @default `'Unrecoverable error - this browser does not permit file uploading of any kind due to serious bugs in iOS8 Safari. Please use iOS8 Chrome until Apple fixes these issues.'` + */ + unsupportedBrowserIos8Safari?: string; + } + + /** + * PasteOptions + */ + interface PasteOptions { + /** + * The default name given to pasted images + * + * @default `'pasted_image'` + */ + defaultName?: string; + /** + * Enable this feature by providing any HTMLElement here + * + * @default `null` + */ + targetElement?: HTMLElement; + } + + /** + * ResumeOptions + */ + interface ResumeOptions { + /** + * The number of days before a persistent resume record will expire + * + * @default `7` + */ + recordsExpireIn?: number; + /** + * Enable or disable the ability to resume failed or stopped chunked uploads + * + * @default `false` + */ + enabled?: boolean; + /** + * paramNames.resuming - Sent with the first request of the resume with a value of `true`. + * + * @default `'qqresume'` + */ + paramNames?: ParamNamesOptions; + } + + /** + * RetryOptions + */ + interface RetryOptions { + /** + * The number of seconds to wait between auto retry attempts + * + * @default `5` + */ + autoAttemptDelay?: number; + /** + * Enable or disable retrying uploads that receive any error response + * + * @default `false` + */ + enableAuto?: boolean; + /** + * The maximum number of times to attempt to retry a failed upload + * + * @default `3` + */ + maxAutoAttempts?: number; + /** + * This property will be looked for in the server response and, if found and `true`, will indicate that no more retries should be attempted for this item + * + * @default `'preventRetry'` + */ + preventRetryResponseProperty?: string; + } + + /** + * RequestOptions + */ + interface RequestOptions { + /** + * Additional headers sent along with each upload request + */ + customHeaders?: any; + /** + * The endpoint to send upload requests to + * + * @default `'/server/upload'` + */ + endpoint?: string; + /** + * The name of the parameter passed if the original filename has been edited or a `Blob` is being sent + * + * @default `'qqfilename'` + */ + filenameParam?: string; + /** + * Force all uploads to use multipart encoding + * + * @default `true` + */ + forceMultipart?: boolean; + /** + * The attribute of the input element which will contain the file name. + * + * For non-multipart-encoded upload requests, this will be included as a parameter in the query string of the URI with a value equal to the file name + * + * @default `'qqfile'` + */ + inputName?: string; + /** + * Specify a method to use when sending files to a traditional endpoint. This option is ignored in older browsers (such as IE 9 and older) + * + * @default `'POST'` + */ + method?: string; + /** + * The parameters that shall be sent with each upload request + */ + params?: any; + /** + * Enable or disable sending parameters in the request body. + * + * If `false`, parameters are sent in the URL. + * Otherwise, parameters are sent in the request body + * + * @default `true` + */ + paramsInBody?: boolean; + /** + * The name of the parameter the uniquely identifies each associated item. The value is a Level 4 UUID + * + * @default `'qquuid'` + */ + uuidName?: string; + /** + * The name of the parameter passed that specifies the total file size in bytes + * + * @default `'qqtotalfilesize'` + */ + totalFileSizeName?: string; + } + + /** + * SizeOptions + */ + interface SizeOptions { + /** + * name property will be appended to the file name of the scaled file + */ + name?: string; + /** + * maximum size + */ + maxSize?: number; + /** + * MIME type + */ + type?: string; + } + + /** + * ScalingOptions + */ + interface ScalingOptions { + /** + * Ignored if the current browser does not support image previews. + * + * If you want to use an alternate scaling library, you must contribute a function for this option that returns a Promise. + * Once the resize is complete, your promise must be fulfilled. You may, of course, reject your returned Promise is the resize fails in some way + * + * @default `undefined` + */ + customResizer?: CustomResizerCallBack; + /** + * A value between `1` and `100` that describes the requested quality of scaled images. + * + * Ignored unless the scaled image type target is `image/jpeg` + * + * @default `80` + */ + defaultQuality?: number + /** + * Scaled images will assume this image type if you don't specify a specific type in your size object, or if the type specified in the size object is not valid. + * + * You generally should not use any value other than `image/jpeg` or `image/png` here. + * + * The default value of null will ensure the scaled image type is `PNG`, unless the original file is a `JPEG`, in which case the scaled file will also be a `JPEG`. + * The default is probably the safest option. + * + * @default `null` + */ + defaultType?: string; + /** + * Text sent to your `complete` event handler as an `error` property of the `response` param if a scaled image could not be generated + * + * @default `'failed to scale'` + */ + failureText?: string; + /** + * Ensure the `EXIF` data from the reference image is inserted into the scaled image. Only applicable when both the reference and the target are type `image/jpeg` + * + * @default `false` + */ + includeExif?: boolean; + /** + * Set this to `false` if you do not want scaled images to be re-oriented based on parsed `EXIF` data before they are uploaded + * + * @default `true` + */ + orient?: boolean; + /** + * Set this to `false` if you don't want to original file to be uploaded as well + * + * @default `true` + */ + sendOriginal?: boolean; + /** + * An array containing size objects that describe scaled versions of each submitted image that should be generated and uploaded + * + * @default `[]` + */ + sizes?: SizeOptions; + } + + /** + * SessionOptions + */ + interface SessionOptions { + /** + * Any additional headers you would like included with the `GET` request sent to your server. Ignored in `IE9` and `IE8` if the endpoint is cross-origin + * + * @default `{}` + */ + customHeaders?: any; + /** + * If non-null, Fine Uploader will send a `GET` request on startup to this endpoint, expecting a `JSON` response containing data about the initial file list to populate + * + * @default `null` + */ + endpoint?: string; + /** + * Any parameters you would like passed with the associated `GET` request to your server + * + * @default `{}` + */ + params?: any; + /** + * Set this to `false` if you do not want the file list to be retrieved from the server as part of a reset. + * + * @default `true` + */ + refreshOnReset?: boolean + } + + /** + * TextOptions + */ + interface TextOptions { + /** + * In the event of non-200 response from the server sans the 'error' property, this message will be passed to the 'error' event handler + * + * @default `'Upload failure reason unknown'` + */ + defaultResponseError?: string; + /** + * The value for the `title` attribute attached to the `` maintained by Fine Uploader for each upload button. + * + * This is used as hover text, among other things. + * + * @default `'file input'` + */ + fileInputTitle?: string; + /** + * Symbols used to represent file size, in ascending order + * + * @default `['kB', 'MB', 'GB', 'TB', 'PB', 'EB']` + */ + sizeSymbols?: string[]; + } + + /** + * ImageOptions + */ + interface ImageOptions { + /** + * Restrict images to a maximum height in pixels (wherever possible) + * + * @default `0` + */ + maxHeight?: number; + /** + * Restrict images to a maximum width in pixels (wherever possible) + * + * @default `0` + */ + maxWidth?: number; + /** + * Restrict images to a minimum height in pixels (wherever possible) + * + * @default `0` + */ + minHeight?: number; + /** + * Restrict images to a minimum width in pixels (wherever possible) + * + * @default `0` + */ + minWidth?: number; + } + + /** + * ValidationOptions + */ + interface ValidationOptions { + /** + * Used by the file selection dialog. + * + * Restrict the valid file types that appear in the selection dialog by listing valid content-type specifiers + * + * @default `null` + */ + acceptFiles?: any; + /** + * Specify file valid file extensions here to restrict uploads to specific types + * + * @default `[]` + */ + allowedExtensions?: string[]; + /** + * Maximum number of items that can be potentially uploaded in this session. + * + * Will reject all items that are added or retried after this limit is reached + * + * @default `0` + */ + itemLimit?: number; + /** + * The minimum allowable size, in bytes, for an item + * + * @default `0` + */ + minSizeLimit?: number; + /** + * The maximum allowable size, in bytes, for an item + * + * @default `0` + */ + sizeLimit?: number; + /** + * When `true` the first invalid item will stop processing further files + * + * @default `true` + */ + stopOnFirstInvalidFile?: boolean; + /** + * ImageOptions + */ + image?: ImageOptions; + } + + /** + * WorkArounds options + */ + interface WorkArounds { + /** + * Ensures all `` elements tracked by Fine Uploader do NOT contain a `multiple` attribute to work around an issue present in iOS7 & 8 that otherwise results in 0-sized uploaded videos + * + * @default `true` + */ + iosEmptyVideos?: boolean; + /** + * Ensures all `` elements tracked by Fine Uploader always have a `multiple` attribute present. + * + * This only applies to iOS8 Chrome and iOS8 UIWebView, and is put in place to work around an issue that causes the browser to crash when a file input element does not contain a `multiple` attribute inside of a `UIWebView` container created by an iOS8 app compiled with and iOS7 SDK + * + * @default `false` + */ + ios8BrowserCrash?: boolean; + /** + * Disables Fine Uploader and displays a message to the user in iOS 8.0.0 Safari. + * + * Due to serious bugs in iOS 8.0.0 Safari, uploading is not possible. + * This was apparently fixed in subsequent builds of iOS8, so this workaround only targets 8.0.0 + * + * @default `true` + */ + ios8SafariUploads?: boolean; + } + + interface PromiseOptions { + /** + * Register callbacks from success and failure. + * + * The promise instance that then is called on will pass any values into the provided callbacks. + * If success or failure have already occurred before these callbacks have been registered, then they will be called immediately after this call has been executed. + * Each subsequent call to then registers an additional set of callbacks. + * + * @param Function successCallback : The function to call when the promise is successfully fulfilled + * @param Function failureCallback : The function to call when the promise is unsuccessfully fulfilled + * @return PromiseOptions : An instance of a promise + */ + then(successCallback: Function, failureCallback: Function): PromiseOptions; + + /** + * Register callbacks for success or failure. + * + * Invoked when the promise is fulfilled regardless of the result. + * The promise instance that done is called on will pass any values into the provided callback. + * Each call to done registers an additional set of callbacks + * + * @param Function callback : The function to call when the promise is fulfilled, successful or not. + * @return PromiseOptions : An instance of a promise + */ + done(callback: Function): PromiseOptions; + + /** + * Call this on a promise to indicate success. + * The parameter values will depend on the situation. + * + * @param Object param : The value to pass to the promise's success handler. + * @return PromiseOptions : An instance of a promise + */ + success(param: any): PromiseOptions; + + /** + * Call this on a promise to indicate failure. + * The parameter values will depend on the situation. + * + * @param Object param : The value to pass to the promise's failure handler. + * @return PromiseOptions : An instance of a promise + */ + failure(param: any): PromiseOptions; + } + + + /* ====================================== Core Callback functions ==================================== */ + + /** + * onAutoRetry function type + */ + interface OnAutoRetry { + /** + * @param number id : The current file's id + * @param string name : The current file's name + * @param number attemptNumber : The number of retry attempts for the current file so far + */ + (id: number, name: string, attemptNumber: number): void; + } + + /** + * onCancel function type + */ + interface OnCancel { + /** + * @param number id : The current file's id + * @param string name : The current file's name + */ + (id: number, name: string): boolean | PromiseOptions | void; + } + + /** + * onComplete function type + */ + interface OnComplete { + /** + * @param number id : The current file's id + * @param string name : The current file's name + * @param Object responseJSON : The raw response from the server + * @param XMLHttpRequest xhr : The object used to make the request + */ + (id: number, name: string, responseJSON: any, xhr: XMLHttpRequest): void; + } + + /** + * onAllComplete function type + */ + interface OnAllComplete { + /** + * @param number[] succeeded : IDs of all files in the group that have uploaded successfully (status = `qq.status.UPLOAD_SUCCESSFUL`) + * @param number[] failed : IDs of all files in the group that have failed (status = `qq.status.UPLOAD_FAILED`) + */ + (succeeded: number[], failed: number[]): void; + } + + /** + * onDelete function type + */ + interface OnDelete { + /** + * @param number id : The current file's id + */ + (id: number): void; + } + + /** + * onDeleteComplete function type + */ + interface OnDeleteComplete { + /** + * @param number id : The current file's id + * @param XMLHttpRequest xhr : The object used to make the request + * @param boolean isError : `true` if there has been an error, `false` otherwise + */ + (id: number, xhr: XMLHttpRequest, isError: boolean): void; + } + + /** + * onError function type + */ + interface OnError { + /** + * @param number id : The current file's id + * @param string name : The current file's name + * @param string errorReason : The reason for the current error + * @param XMLHttpRequest xhr : The object used to make the request + */ + (id: number, name: string, errorReason: string, xhr: XMLHttpRequest): void; + } + + /** + * onManualRetry function type + */ + interface OnManualRetry { + /** + * @param number id : The current file's id + * @param string name : The current file's name + */ + (id: number, name: string): boolean | void; + } + + /** + * onPasteReceived function type + */ + interface OnPasteReceived { + /** + * @param Blob blob : An object encapsulating the image pasted from the clipboard + */ + (blob: Blob): PromiseOptions | void; + } + + /** + * onProgress function type + */ + interface OnProgress { + /** + * @param number id : The current file's id + * @param string name : The current file's name + * @param number uploadedBytes : The number of bytes that have been uploaded so far + * @param number totalBytes : The total number of bytes that comprise this file + */ + (id: number, name: string, uploadedBytes: number, totalBytes: number): void; + } + + /** + * onResume function type + */ + interface OnResume { + /** + * @param number id : The current file's id + * @param string name : The current file's name + * @param Object chunkData : The chunk that will be sent next when file upload resumes + */ + (id: number, name: string, chunkData: any): void; + } + + /** + * onSessionRequestComplete function type + */ + interface OnSessionRequestComplete { + /** + * @param any[] response : The raw response data + * @param boolean success : Indicates whether success has been achieved or not + * @param XMLHttpRequest xhrOrXdr : The raw request + */ + (response: any[], success: boolean, xhrOrXdr: XMLHttpRequest): void; + } + + /** + * onStatusChange function type + */ + interface OnStatusChange { + /** + * @param number id : The current file's id + * @param string oldStatus : The previous item status + * @param string newStatus : The new status of the item + */ + (id: number, oldStatus: string, newStatus: string): void; + } + + /** + * onSubmit function type + */ + interface OnSubmit { + /** + * @param number id : The current file's id + * @param string name : The current file's name + */ + (id: number, name: string): boolean | PromiseOptions | void; + } + + /** + * onSubmitDelete function type + */ + interface OnSubmitDelete { + /** + * @param number id : The current file's id + */ + (id: number): PromiseOptions | void; + } + + /** + * onSubmitted function type + */ + interface OnSubmitted { + /** + * @param number id : The current file's id + * @param string name : The current file's name + */ + (id: number, name: string): void; + } + + /** + * onTotalProgress function type + */ + interface OnTotalProgress { + /** + * @param number totalUploadedBytes : The number of bytes that have been uploaded so far in this batch + * @param number totalBytes : The total number of bytes that comprise all files in the batch + */ + (totalUploadedBytes: number, totalBytes: number): void; + } + + /** + * onUpload function type + */ + interface OnUpload { + /** + * @param number id : The current file's id + * @param string name : The current file's name + */ + (id: number, name: string): void; + } + + /** + * properties for chunkData object + */ + interface ChunkData { + /** + * the 0-based index of the associated partition + */ + partIndex: number; + /** + * the byte offset of the current chunk + */ + startByte: number; + /** + * the last byte of the current chunk + */ + endByte: number; + /** + * the total number of partitions associated with the `File` or `Blob` + */ + totalParts: number; + } + + /** + * onUploadChunk function type + */ + interface OnUploadChunk { + /** + * @param number id : The current file's id + * @param string name : The current file's name + * @param ChunkData chunkData : An object encapsulating the current chunk of data about to be uploaded + */ + (id: number, name: string, chunkData: ChunkData): void; + } + + /** + * onUploadChunkSuccess function type + */ + interface OnUploadChunkSuccess { + /** + * @param number id : The current file's id + * @param ChunkData chunkData : An object encapsulating the current chunk of data about to be uploaded + * @param Object responseJSON : The raw response from the server + * @param XMLHttpRequest xhr : The object used to make the request + */ + (id: number, chunkData: ChunkData, responseJSON: any, xhr: XMLHttpRequest): void; + } + + /** + * blobData object + */ + interface BlobDataObject { + /** + * the name of the file + */ + name: string; + /** + * the size of the file + */ + size: number; + } + + /** + * onValidate function type + */ + interface OnValidate { + /** + * @param BlobDataObject data : An object with a name and size property + * @param HTMLElement buttonContainer : The button corresponding to the respective file if the file was submitted to Fine Uploader using a tracked button + */ + (data: BlobDataObject, buttonContainer?: HTMLElement): PromiseOptions | void; + } + + /** + * onValidateBatch function type + */ + interface OnValidateBatch { + /** + * @param BlobDataObject[] fileOrBlobDataArray : An array of Objects with name and size properties + * @param HTMLElement buttonContainer : The button corresponding to the respective file if the file was submitted to Fine Uploader using a tracked button + */ + (fileOrBlobDataArray: BlobDataObject[], buttonContainer: HTMLElement): PromiseOptions | void; + } + + /** + * Core callback functions + */ + interface CoreEvents { + /** + * Called before each automatic retry attempt for a failed item** + */ + onAutoRetry?: OnAutoRetry; + /** + * Called when the item has been canceled. Return `false` to prevent the upload from being canceled. + * + * Also can return a promise if non-blocking work is required here. Calling `failure()` on the promise is equivalent to returning `false`. + * + * If using a Promise, then processing of the cancel request will be deferred until the promise is fullfilled. + * + * Since there is no way to 'pause' the upload in progress while waiting for the promise to be fullfilled the upload may actually complete until the promise has actually be fullfilled + */ + onCancel?: OnCancel; + /** + * Called when the item has finished uploading. + * + * The `responseJSON` will contain the raw response from the server including the 'success' property which indicates whether the upload succeeded. + */ + onComplete?: OnComplete; + /** + * Called when all submitted items have reached a point of termination. + * + * A file has reached a point of termination if it has been cancelled, rejected, or uploaded (failed or successful). + * + * For example, if a file in the group is paused, and all other files in the group have uploaded successfully the allComplete event will not be invoked for the group until that paused file is either continued and completes the uploading process, or canceled. + * + * This event will not be called if all files in the group have been cancelled or rejected (i.e. if none of the files have reached a status of `qq.status.UPLOAD_SUCCESSFUL` or `qq.status.UPLOAD_FAILED`) + */ + onAllComplete?: OnAllComplete; + /** + * Called just before a delete request is sent for the associated item. + * + * ###Note: + * This is not the correct callback to influence the delete request. + * To do that, use the `onSubmitDelete` callback instead + */ + onDelete?: OnDelete; + /** + * Called just after receiving a response from the server for a delete file request** + */ + onDeleteComplete?: OnDeleteComplete; + /** + * Called whenever an exceptional condition occurs** + */ + onError?: OnError; + /** + * Called before each manual retry attempt. + * + * Return `false` to prevent this and all future retry attempts on the associated item + */ + onManualRetry?: OnManualRetry; + /** + * Called when a pasted image has been received (before upload). + * + * The pasted image is represented as a `Blob`. Also can return a `Promise` if non-blocking work is required here. + * + * If using a `Promise` the value of the success parameter must be the name to associate with the pasted image. + * + * If the associated attempt is marked a failure then you should include a string explaining the reason in your failure callback for the `Promise` + * + * ###NOTE: + * The `promptForName` option, if `true`, will effectively wipe away any custom implementation of this callback. + * + * The two are not meant to be used together. This callback is meant to provide an alternative means to provide a name for a pasted image. + * + * If you are using Fine Uploader Core mode then you can display your own prompt for the name by overriding the default implementation of this callback + */ + onPasteReceived?: OnPasteReceived; + /** + * Called during the upload, as it progresses, but only for the AJAX uploader. + * + * For chunked uploads, this will be called for each chunk. + * Useful for implementing a progress bar + */ + onProgress?: OnProgress; + /** + * Called just before an upload is resumed. + * + * See the `uploadChunk` event for more info on the `chunkData` object + */ + onResume?: OnResume; + /** + * Invoked when a session request has completed. + * + * The `response` will be either an `Array` containing the response data or `null` if the response did not contain valid `JSON`. + * + * The `success` parameter will be `false` if ANY of the file items represented in the response could not be parsed (due to bad syntax, missing name/UUID property, etc) + */ + onSessionRequestComplete?: OnSessionRequestComplete; + /** + * Invoked whenever the status changes for any item submitted by the uploader. + * + * The status values correspond to those found in the `qq.status` object. + * + * For reference: + * * `SUBMITTED` + * * `QUEUED` + * * `UPLOADING` + * * `UPLOAD_RETRYING` + * * `UPLOAD_FAILED` + * * `UPLOAD_SUCCESSFUL` + * * `CANCELED` + * * `REJECTED` + * * `DELETED` + * * `DELETING` + * * `DELETE_FAILED` + * * `PAUSED` + */ + onStatusChange?: OnStatusChange; + /** + * Called when the item has been selected and is a candidate for uploading** + * + * This does not mean the item is going to be uploaded. Return `false` to prevent submission to the uploader. + * + * A promise can be used if non-blocking work is required. Processing of this item is deferred until the promise is fullfilled. + * + * If a promise is returned, a call to failure is the same as returning `false` + */ + onSubmit?: OnSubmit; + /** + * Called before an item has been marked for deletion has been submitted to the uploader** + * + * A promise can be used if non-blocking work is required. + * Processing of this item is deferred until the promise is fullfilled. + * If a promise is returned, a call to failure is the same as returning `false`. + * + * Use this callback to influence the delete request. + * For example, you can change the custom parameters sent with the underlying delete request using the `setDeleteParams` API method + */ + onSubmitDelete?: OnSubmitDelete; + /** + * Called when the item has been successfully submitted to the uploader. + * + * The file will upload immediately if there is: + * * a) at least one free connection (see: maxConnections option) and + * * b) autoUpload is set to true (see autoUpload option) + * + * The callback is invoked after the 'submit' event is handled without returning a false value. + * + * In Fine Uploader Core mode it is usually safe to assume that the associated elements in the UI representing the associated file have already been added to the DOM immediately before this callback is invoked + */ + onSubmitted?: OnSubmitted; + /** + * Called during a batch of uploads, as they progress, but only for the AJAX uploader. + * + * This represents the total progress of all files in the batch. Useful for implementing an aggregate progress bar. + */ + onTotalProgress?: OnTotalProgress; + /** + * Called just before an item begins uploading to the server. + */ + onUpload?: OnUpload; + /** + * Called just before a chunk request is sent. + */ + onUploadChunk?: OnUploadChunk; + /** + * This is similar to the `complete` event, except it is invoked after each chunk has been successfully uploaded. + * + * See the `uploadChunk` event for more information on the `chunkData` object + */ + onUploadChunkSuccess?: OnUploadChunkSuccess; + /** + * Called once for each selected, dropped, or `addFiles` submitted file. + * + * This callback is always invoked before the default Fine Uploader validators execute. + * + * This event will not be called if you return `false` in your `validateBatch` event handler, or if the `stopOnFirstInvalidFile` validation option is `true` and the `validate` event handler has returned `false` for an item. + * + * A promise can be used if non-blocking work is required. Processing of this item is deferred until the promise is fullfilled. + * If a promise is returned, a call to `failure` is the same as returning `false`. + * + * A buttonContainer element will be passed as the last argument, provided the file was submitted using a Fine Uploader tracked button. + * + * The `blobData` object has two properties: `name` and `size`. The `size` property will be undefined for browsers without File API support. + */ + onValidate?: OnValidate; + /** + * This callback is always invoked before the default Fine Uploader validators execute. + * + * This event will not be called if you return `false` in your `validateBatch` event handler, or if the `stopOnFirstInvalidFile` validation option is `true` and the `validate` event handler has returned `false` for an item. + * + * A promise can be used if non-blocking work is required. Processing of this item is deferred until the promise is fullfilled. If a promise is returned, a call to `failure` is the same as returning `false`. + * + * A buttonContainer element will be passed as the last argument, provided the file was submitted using a Fine Uploader tracked button. + * + * The `fileOrBlobDataArray` object has two properties: `name` and `size`. The `size` property will be undefined for browsers without File API support. + */ + onValidateBatch?: OnValidateBatch; + } + /* ====================================== END - Core Callback functions ======================================== */ + + /** + * Contains Core options + */ + interface CoreOptions { + /** + * Set to false if you want to be able to upload queued items later by calling the `uploadStoredFiles()` method + * + * @default `true` + */ + autoUpload?: boolean; + /** + * Specify an element to use as the 'select files' button. Cannot be a ` From 2bc30977a0dc7ee96ca27f3f08e7d66941fd1eef Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 15 Feb 2017 00:31:03 -0600 Subject: [PATCH 268/344] chore(package): update clean-css-cli to version 4.0.7 (#1753) https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17230c73c..89e311e2d 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "url": "https://github.com/FineUploader/fine-uploader/issues" }, "devDependencies": { - "clean-css-cli": "4.0.6", + "clean-css-cli": "4.0.7", "jscs": "3.0.7", "jshint": "2.9.4", "karma": "1.4.1", From e3e90b88082314ec717c55a9c43efbed63efb768 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 16 Feb 2017 22:10:50 -0600 Subject: [PATCH 269/344] chore(build): prepare for 5.14.0 release --- client/js/version.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/version.js b/client/js/version.js index 53aa87358..4d8e1c54f 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version = "5.14.0-beta3"; +qq.version = "5.14.0"; diff --git a/package.json b/package.json index 89e311e2d..7409ebb47 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "title": "Fine Uploader", "main": "lib/traditional.js", "types" : "typescript/fine-uploader.d.ts", - "version": "5.14.0-beta3", + "version": "5.14.0", "description": "Multiple file upload plugin with progress-bar, drag-and-drop, direct-to-S3 & Azure uploading, client-side image scaling, preview generation, form support, chunking, auto-resume, and tons of other features.", "keywords": [ "amazon", From dc66e22a7964fa87fe1b3be76dda63698ae5face Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 16 Feb 2017 23:47:05 -0600 Subject: [PATCH 270/344] docs(amazon-s3.jmd): missing linefeeds in v4 signing steps [skip ci] --- docs/endpoint_handlers/amazon-s3.jmd | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/endpoint_handlers/amazon-s3.jmd b/docs/endpoint_handlers/amazon-s3.jmd index 12ef68310..c8a31e95a 100644 --- a/docs/endpoint_handlers/amazon-s3.jmd +++ b/docs/endpoint_handlers/amazon-s3.jmd @@ -177,13 +177,13 @@ If you'd like to see an example of generating a signature for a chunked upload r version 4 signing process, have a look at the [Fine Uploader PHP S3 signature server example][s3-php]. The general process is as follows: -1. Handle a chunked upload signature request from Fine Uploader. Look at the query string to determine if this is a version 4 request, or not. The presence of a `"header"` property in the JSON-encoded message body indicates that this is indeed related to a chunked upload request. -2. Follow the signing process outlined in the diagram on [Amazon's version 4 header-based auth documentation page][s3-version4-auth]. -3. The `"headers"` string provided by Fine Uploader corresponds to the "StringToSign" portion of the diagram on the page referenced in the previous step, with one notable difference. Instead of a hashed "canonical request" at the end of the "StringToSign", Fine Uploader S3 will include the raw [newline-delimited canonical request][s3-canonical-request]. This allows you to properly inspect the request before signing it. - a. Examine the raw canonical request string at the end of the headers string to verify that the request is valid before signing it. - b. Replace the raw canonical request at the end of the "StringToSign" sent by Fine Uploader S3 with a SHA256 hashed version. - c. Now generate the signature using this constructed "StringToSign" by following the logic in the third step (titled "Signature") of the above-mentioned AWS document. -4. Return a JSON response with the generated signature as the value of a `"signature"` property. +1. Handle a chunked upload signature request from Fine Uploader. Look at the query string to determine if this is a version 4 request, or not. The presence of a `"header"` property in the JSON-encoded message body indicates that this is indeed related to a chunked upload request. +2. Follow the signing process outlined in the diagram on [Amazon's version 4 header-based auth documentation page][s3-version4-auth]. +3. The `"headers"` string provided by Fine Uploader corresponds to the "StringToSign" portion of the diagram on the page referenced in the previous step, with one notable difference. Instead of a hashed "canonical request" at the end of the "StringToSign", Fine Uploader S3 will include the raw [newline-delimited canonical request][s3-canonical-request]. This allows you to properly inspect the request before signing it. + a. Examine the raw canonical request string at the end of the headers string to verify that the request is valid before signing it. + b. Replace the raw canonical request at the end of the "StringToSign" sent by Fine Uploader S3 with a SHA256 hashed version. + c. Now generate the signature using this constructed "StringToSign" by following the logic in the third step (titled "Signature") of the above-mentioned AWS document. +4. Return a JSON response with the generated signature as the value of a `"signature"` property. ## Supporting IE9 (and older) and Android 2.3.x (and older) From 74bfb03556cc6156716eb0d9034b8c50e1d45bde Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 16 Feb 2017 23:51:27 -0600 Subject: [PATCH 271/344] docs(amazon-s3.jmd): 2nd attempt at fixing nested list in v4 sig section [skip ci] --- docs/endpoint_handlers/amazon-s3.jmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/endpoint_handlers/amazon-s3.jmd b/docs/endpoint_handlers/amazon-s3.jmd index c8a31e95a..7fbc5e19b 100644 --- a/docs/endpoint_handlers/amazon-s3.jmd +++ b/docs/endpoint_handlers/amazon-s3.jmd @@ -180,9 +180,9 @@ follows: 1. Handle a chunked upload signature request from Fine Uploader. Look at the query string to determine if this is a version 4 request, or not. The presence of a `"header"` property in the JSON-encoded message body indicates that this is indeed related to a chunked upload request. 2. Follow the signing process outlined in the diagram on [Amazon's version 4 header-based auth documentation page][s3-version4-auth]. 3. The `"headers"` string provided by Fine Uploader corresponds to the "StringToSign" portion of the diagram on the page referenced in the previous step, with one notable difference. Instead of a hashed "canonical request" at the end of the "StringToSign", Fine Uploader S3 will include the raw [newline-delimited canonical request][s3-canonical-request]. This allows you to properly inspect the request before signing it. - a. Examine the raw canonical request string at the end of the headers string to verify that the request is valid before signing it. - b. Replace the raw canonical request at the end of the "StringToSign" sent by Fine Uploader S3 with a SHA256 hashed version. - c. Now generate the signature using this constructed "StringToSign" by following the logic in the third step (titled "Signature") of the above-mentioned AWS document. + 1. Examine the raw canonical request string at the end of the headers string to verify that the request is valid before signing it. + 2. Replace the raw canonical request at the end of the "StringToSign" sent by Fine Uploader S3 with a SHA256 hashed version. + 3. Now generate the signature using this constructed "StringToSign" by following the logic in the third step (titled "Signature") of the above-mentioned AWS document. 4. Return a JSON response with the generated signature as the value of a `"signature"` property. From 6267b10a90a408db8d7bec9e1246590b0daed9e5 Mon Sep 17 00:00:00 2001 From: Arturs Vonda Date: Fri, 17 Feb 2017 13:14:38 +0200 Subject: [PATCH 272/344] Add missing return definition My env was complaining about implicit any. --- client/typescript/fine-uploader.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/typescript/fine-uploader.d.ts b/client/typescript/fine-uploader.d.ts index 4ef8d5755..81e9b6f6e 100644 --- a/client/typescript/fine-uploader.d.ts +++ b/client/typescript/fine-uploader.d.ts @@ -2400,7 +2400,7 @@ declare namespace FineUploader { /** * Clears all text for this element** */ - clearText(); + clearText(): void; /** * Inserts the element directly before the passed element in the DOM. From deb8f42c5968a28520fb41d874491995c32227c6 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sat, 18 Feb 2017 20:03:38 -0600 Subject: [PATCH 273/344] docs(navbar.html): prepare for switch to SSL [skip ci] --- docs/_templates/layout.html | 2 +- docs/_templates/navbar.html | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index 4454f5d24..ab838ec5a 100755 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -17,7 +17,7 @@ {% endblock %} {% block css_font %} - + {% endblock %} {% block css_site %} diff --git a/docs/_templates/navbar.html b/docs/_templates/navbar.html index 43dc31edb..21c4f78d4 100644 --- a/docs/_templates/navbar.html +++ b/docs/_templates/navbar.html @@ -11,9 +11,9 @@ From 3465ffffc0ae922f6ed4f312c7f5b6d58b3e0c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Beau?= Date: Sat, 13 Oct 2018 04:36:05 +0200 Subject: [PATCH 338/344] Call target.onload() only when defined (#2056) This removes the "Uncaught TypeError: target.onload is not a function" console error while img preview --- client/js/image-support/megapix-image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/js/image-support/megapix-image.js b/client/js/image-support/megapix-image.js index 4cdfef596..a199e1edc 100644 --- a/client/js/image-support/megapix-image.js +++ b/client/js/image-support/megapix-image.js @@ -383,7 +383,7 @@ renderImageToDataURL(self.srcImage, self.blob, opt, doSquash) .then(function(dataUri) { target.src = dataUri; - oldTargetSrc === target.src && target.onload(); + oldTargetSrc === target.src && target.onload && target.onload(); }); }()); } else if (tagName === "canvas") { From 46abe0094f99e851bfb71cb2e2ab47a2c4186fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Bolvin?= Date: Sun, 28 Oct 2018 01:29:04 +0200 Subject: [PATCH 339/344] consider Firefox when checking for Android Stock Browser (#1978) (#2007) --- client/js/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/js/util.js b/client/js/util.js index f6d5f109c..3ac744943 100644 --- a/client/js/util.js +++ b/client/js/util.js @@ -569,7 +569,7 @@ var qq = function(element) { // We need to identify the Android stock browser via the UA string to work around various bugs in this browser, // such as the one that prevents a `Blob` from being uploaded. qq.androidStock = function() { - return qq.android() && navigator.userAgent.toLowerCase().indexOf("chrome") < 0; + return qq.android() && navigator.userAgent.toLowerCase().indexOf("chrome") < 0 && navigator.userAgent.toLowerCase().indexOf("firefox") < 0; }; qq.ios6 = function() { From 15fb63a94549d6ac5ff9beb6b7d45d320863ef34 Mon Sep 17 00:00:00 2001 From: Herrington Darkholme <2883231+HerringtonDarkholme@users.noreply.github.com> Date: Fri, 2 Nov 2018 00:16:31 +0800 Subject: [PATCH 340/344] feat(dnd.js): add dragEnter and dragLeave callbacks (#2037) * feat(dnd.js): add dragEnter and dragLeave callbacks * add dragEnter/dragLeave doc --- client/js/dnd.js | 4 ++++ docs/features/drag-and-drop.jmd | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/client/js/dnd.js b/client/js/dnd.js index 126df5250..4399d6081 100644 --- a/client/js/dnd.js +++ b/client/js/dnd.js @@ -169,10 +169,12 @@ qq.DragAndDrop = function(o) { element: dropArea, onEnter: function(e) { qq(dropArea).addClass(options.classes.dropActive); + options.callbacks.dragEnter(); e.stopPropagation(); }, onLeaveNotDescendants: function(e) { qq(dropArea).removeClass(options.classes.dropActive); + options.callbacks.dragLeave(); }, onDrop: function(e) { handleDataTransfer(e.dataTransfer, dropZone).then( @@ -317,6 +319,8 @@ qq.DragAndDrop.callbacks = function() { "use strict"; return { + dragEnter: function () {}, + dragLeave: function () {}, processingDroppedFiles: function() {}, processingDroppedFilesComplete: function(files, targetEl) {}, dropError: function(code, errorSpecifics) { diff --git a/docs/features/drag-and-drop.jmd b/docs/features/drag-and-drop.jmd index e1b984ded..fd97c3026 100644 --- a/docs/features/drag-and-drop.jmd +++ b/docs/features/drag-and-drop.jmd @@ -109,6 +109,15 @@ api_event("dropLog", "dropLog", ]) }} +{{ +api_event("dragEnter", "dragEnter", +"Invoked when dragged element enters drop zone.", [], []) +}} +{{ +api_event("dragLeave", "dragLeave", +"Invoked when dragged element leaves drop zone.", [], []) +}} + {% markdown %} ### Methods {% endmarkdown %} From a17fe7b03b87e7c0ae40a1a8f912e1332116053b Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sat, 3 Nov 2018 21:23:13 -0500 Subject: [PATCH 341/344] fix(Makefile): smart_strong import error on docfu install (#2068) Fixed in docfu 1.0.4, which locks us to python-markdown 2.6.11 (the last version to include smart_strong). https://github.com/FineUploader/docfu/releases/tag/1.0.4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 47b25f5e8..d1b97032c 100644 --- a/Makefile +++ b/Makefile @@ -441,7 +441,7 @@ endif .PHONY: docs-travis install-docfu: - git clone --depth 1 -b 1.0.2 https://github.com/FineUploader/docfu + git clone --depth 1 -b 1.0.4 https://github.com/FineUploader/docfu (cd docfu ; python setup.py install) rm -rf docfu .PHONY: install-docfu From fb41fb4ee1e6b900a83f60e7accd944ec4a62d72 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 6 Nov 2018 20:50:35 -0600 Subject: [PATCH 342/344] fix logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef6678c10..7fe88a7c7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - + # Looking for additional maintainers - please [inquire within](https://github.com/FineUploader/fine-uploader/issues/1881). From 5ca11e8c9c61b1bc0a00c0c8556060ccda064822 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 6 Nov 2018 20:51:50 -0600 Subject: [PATCH 343/344] not looking for new maintainers anymore --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7fe88a7c7..29daf0c87 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ -# Looking for additional maintainers - please [inquire within](https://github.com/FineUploader/fine-uploader/issues/1881). - [![Build Status](https://travis-ci.org/FineUploader/fine-uploader.svg?branch=master)](https://travis-ci.org/FineUploader/fine-uploader) [![npm](https://img.shields.io/npm/v/fine-uploader.svg)](https://www.npmjs.com/package/fine-uploader) [![CDNJS](https://img.shields.io/cdnjs/v/file-uploader.svg)](https://cdnjs.com/libraries/file-uploader) From 057cc83a7e7657d032a75cf4a6b3612c7b4191da Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Sun, 18 Nov 2018 18:49:26 -0600 Subject: [PATCH 344/344] bye bye fine uploader! --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 29daf0c87..b032a8735 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**Fine Uploader is no longer maintained and the project has been effectively shut down. For more info, see https://github.com/FineUploader/fine-uploader/issues/2073.** +