mirror of
https://github.com/wekan/wekan.git
synced 2025-12-23 02:40:14 +01:00
Fixed Non-ASCII attachment filename will crash when downloading.
Thanks to xet7 ! Fixes #2759
This commit is contained in:
parent
843ff8eaaa
commit
c2da477735
277 changed files with 30568 additions and 52 deletions
166
packages/wekan-cfs-data-man/client/Blob.js
Normal file
166
packages/wekan-cfs-data-man/client/Blob.js
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/* Blob.js
|
||||
* A Blob implementation.
|
||||
* 2013-12-27
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
* By Devin Samarin, https://github.com/eboyjr
|
||||
* License: X11/MIT
|
||||
* See LICENSE.md
|
||||
*/
|
||||
|
||||
/*global self, unescape */
|
||||
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
|
||||
plusplus: true */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
|
||||
|
||||
if (!(typeof Blob === "function" || typeof Blob === "object") || typeof URL === "undefined")
|
||||
if ((typeof Blob === "function" || typeof Blob === "object") && typeof webkitURL !== "undefined") self.URL = webkitURL;
|
||||
else var Blob = (function (view) {
|
||||
"use strict";
|
||||
|
||||
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || view.MSBlobBuilder || (function(view) {
|
||||
var
|
||||
get_class = function(object) {
|
||||
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
|
||||
}
|
||||
, FakeBlobBuilder = function BlobBuilder() {
|
||||
this.data = [];
|
||||
}
|
||||
, FakeBlob = function Blob(data, type, encoding) {
|
||||
this.data = data;
|
||||
this.size = data.length;
|
||||
this.type = type;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
, FBB_proto = FakeBlobBuilder.prototype
|
||||
, FB_proto = FakeBlob.prototype
|
||||
, FileReaderSync = view.FileReaderSync
|
||||
, FileException = function(type) {
|
||||
this.code = this[this.name = type];
|
||||
}
|
||||
, file_ex_codes = (
|
||||
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
|
||||
+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
|
||||
).split(" ")
|
||||
, file_ex_code = file_ex_codes.length
|
||||
, real_URL = view.URL || view.webkitURL || view
|
||||
, real_create_object_URL = real_URL.createObjectURL
|
||||
, real_revoke_object_URL = real_URL.revokeObjectURL
|
||||
, URL = real_URL
|
||||
, btoa = view.btoa
|
||||
, atob = view.atob
|
||||
|
||||
, ArrayBuffer = view.ArrayBuffer
|
||||
, Uint8Array = view.Uint8Array
|
||||
;
|
||||
FakeBlob.fake = FB_proto.fake = true;
|
||||
while (file_ex_code--) {
|
||||
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
|
||||
}
|
||||
if (!real_URL.createObjectURL) {
|
||||
URL = view.URL = {};
|
||||
}
|
||||
URL.createObjectURL = function(blob) {
|
||||
var
|
||||
type = blob.type
|
||||
, data_URI_header
|
||||
;
|
||||
if (type === null) {
|
||||
type = "application/octet-stream";
|
||||
}
|
||||
if (blob instanceof FakeBlob) {
|
||||
data_URI_header = "data:" + type;
|
||||
if (blob.encoding === "base64") {
|
||||
return data_URI_header + ";base64," + blob.data;
|
||||
} else if (blob.encoding === "URI") {
|
||||
return data_URI_header + "," + decodeURIComponent(blob.data);
|
||||
} if (btoa) {
|
||||
return data_URI_header + ";base64," + btoa(blob.data);
|
||||
} else {
|
||||
return data_URI_header + "," + encodeURIComponent(blob.data);
|
||||
}
|
||||
} else if (real_create_object_URL) {
|
||||
return real_create_object_URL.call(real_URL, blob);
|
||||
}
|
||||
};
|
||||
URL.revokeObjectURL = function(object_URL) {
|
||||
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
|
||||
real_revoke_object_URL.call(real_URL, object_URL);
|
||||
}
|
||||
};
|
||||
FBB_proto.append = function(data/*, endings*/) {
|
||||
var bb = this.data;
|
||||
// decode data to a binary string
|
||||
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
|
||||
var
|
||||
str = ""
|
||||
, buf = new Uint8Array(data)
|
||||
, i = 0
|
||||
, buf_len = buf.length
|
||||
;
|
||||
for (; i < buf_len; i++) {
|
||||
str += String.fromCharCode(buf[i]);
|
||||
}
|
||||
bb.push(str);
|
||||
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
|
||||
if (FileReaderSync) {
|
||||
var fr = new FileReaderSync;
|
||||
bb.push(fr.readAsBinaryString(data));
|
||||
} else {
|
||||
// async FileReader won't work as BlobBuilder is sync
|
||||
throw new FileException("NOT_READABLE_ERR");
|
||||
}
|
||||
} else if (data instanceof FakeBlob) {
|
||||
if (data.encoding === "base64" && atob) {
|
||||
bb.push(atob(data.data));
|
||||
} else if (data.encoding === "URI") {
|
||||
bb.push(decodeURIComponent(data.data));
|
||||
} else if (data.encoding === "raw") {
|
||||
bb.push(data.data);
|
||||
}
|
||||
} else {
|
||||
if (typeof data !== "string") {
|
||||
data += ""; // convert unsupported types to strings
|
||||
}
|
||||
// decode UTF-16 to binary string
|
||||
bb.push(unescape(encodeURIComponent(data)));
|
||||
}
|
||||
};
|
||||
FBB_proto.getBlob = function(type) {
|
||||
if (!arguments.length) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(this.data.join(""), type, "raw");
|
||||
};
|
||||
FBB_proto.toString = function() {
|
||||
return "[object BlobBuilder]";
|
||||
};
|
||||
FB_proto.slice = function(start, end, type) {
|
||||
var args = arguments.length;
|
||||
if (args < 3) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(
|
||||
this.data.slice(start, args > 1 ? end : this.data.length)
|
||||
, type
|
||||
, this.encoding
|
||||
);
|
||||
};
|
||||
FB_proto.toString = function() {
|
||||
return "[object Blob]";
|
||||
};
|
||||
return FakeBlobBuilder;
|
||||
}(view));
|
||||
|
||||
return function Blob(blobParts, options) {
|
||||
var type = options ? (options.type || "") : "";
|
||||
var builder = new BlobBuilder();
|
||||
if (blobParts) {
|
||||
for (var i = 0, len = blobParts.length; i < len; i++) {
|
||||
builder.append(blobParts[i]);
|
||||
}
|
||||
}
|
||||
return builder.getBlob(type);
|
||||
};
|
||||
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
|
||||
302
packages/wekan-cfs-data-man/client/data-man-api.js
Normal file
302
packages/wekan-cfs-data-man/client/data-man-api.js
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
/**
|
||||
* @method DataMan
|
||||
* @public
|
||||
* @constructor
|
||||
* @param {File|Blob|ArrayBuffer|Uint8Array|String} data The data that you want to manipulate.
|
||||
* @param {String} [type] The data content (MIME) type, if known. Required if the first argument is an ArrayBuffer, Uint8Array, or URL
|
||||
*/
|
||||
DataMan = function DataMan(data, type) {
|
||||
var self = this;
|
||||
|
||||
if (!data) {
|
||||
throw new Error("DataMan constructor requires a data argument");
|
||||
}
|
||||
|
||||
// The end result of all this is that we will have one of the following set:
|
||||
// - self.blob
|
||||
// - self.url
|
||||
// Unless we already have in-memory data, we don't load anything into memory
|
||||
// and instead rely on obtaining a read stream when the time comes.
|
||||
if (typeof File !== "undefined" && data instanceof File) {
|
||||
self.blob = data; // File inherits from Blob so this is OK
|
||||
self._type = data.type;
|
||||
} else if (typeof Blob !== "undefined" && data instanceof Blob) {
|
||||
self.blob = data;
|
||||
self._type = data.type;
|
||||
} else if (typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer || EJSON.isBinary(data)) {
|
||||
if (typeof Blob === "undefined") {
|
||||
throw new Error("Browser must support Blobs to handle an ArrayBuffer or Uint8Array");
|
||||
}
|
||||
if (!type) {
|
||||
throw new Error("DataMan constructor requires a type argument when passed an ArrayBuffer or Uint8Array");
|
||||
}
|
||||
self.blob = new Blob([data], {type: type});
|
||||
self._type = type;
|
||||
} else if (typeof data === "string") {
|
||||
if (data.slice(0, 5) === "data:") {
|
||||
self._type = data.slice(5, data.indexOf(';'));
|
||||
self.blob = dataURItoBlob(data, self._type);
|
||||
} else if (data.slice(0, 5) === "http:" || data.slice(0, 6) === "https:") {
|
||||
if (!type) {
|
||||
throw new Error("DataMan constructor requires a type argument when passed a URL");
|
||||
}
|
||||
self.url = data;
|
||||
self._type = type;
|
||||
} else {
|
||||
throw new Error("DataMan constructor received unrecognized data string");
|
||||
}
|
||||
} else {
|
||||
throw new Error("DataMan constructor received data that it doesn't support");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method DataMan.prototype.getBlob
|
||||
* @public
|
||||
* @param {Function} [callback] - callback(error, blob)
|
||||
* @returns {undefined|Blob}
|
||||
*
|
||||
* Passes a Blob representing this data to a callback or returns
|
||||
* the Blob if no callback is provided. A callback is required
|
||||
* if getting a Blob for a URL.
|
||||
*/
|
||||
DataMan.prototype.getBlob = function dataManGetBlob(callback) {
|
||||
var self = this;
|
||||
|
||||
if (callback) {
|
||||
if (self.blob) {
|
||||
callback(null, self.blob);
|
||||
} else if (self.url) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', self.url, true);
|
||||
xhr.responseType = "blob";
|
||||
xhr.onload = function(data) {
|
||||
self.blob = xhr.response;
|
||||
callback(null, self.blob);
|
||||
};
|
||||
xhr.onerror = function(err) {
|
||||
callback(err);
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
} else {
|
||||
if (self.url)
|
||||
throw new Error('DataMan.getBlob requires a callback when managing a URL');
|
||||
return self.blob;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method DataMan.prototype.getBinary
|
||||
* @public
|
||||
* @param {Number} [start] - First byte position to read.
|
||||
* @param {Number} [end] - Last byte position to read.
|
||||
* @param {Function} callback - callback(error, binaryData)
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Passes a Uint8Array representing this data to a callback.
|
||||
*/
|
||||
DataMan.prototype.getBinary = function dataManGetBinary(start, end, callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof start === "function") {
|
||||
callback = start;
|
||||
}
|
||||
callback = callback || defaultCallback;
|
||||
|
||||
function read(blob) {
|
||||
if (typeof FileReader === "undefined") {
|
||||
callback(new Error("Browser does not support FileReader"));
|
||||
return;
|
||||
}
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(evt) {
|
||||
callback(null, new Uint8Array(evt.target.result));
|
||||
};
|
||||
reader.onerror = function(err) {
|
||||
callback(err);
|
||||
};
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
|
||||
self.getBlob(function (error, blob) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
if (typeof start === "number" && typeof end === "number") {
|
||||
var size = blob.size;
|
||||
// Return the requested chunk of binary data
|
||||
if (start >= size) {
|
||||
callback(new Error("DataMan.getBinary: start position beyond end of data (" + size + ")"));
|
||||
return;
|
||||
}
|
||||
end = Math.min(size, end);
|
||||
|
||||
var slice = blob.slice || blob.webkitSlice || blob.mozSlice;
|
||||
if (typeof slice === 'undefined') {
|
||||
callback(new Error('Browser does not support File.slice'));
|
||||
return;
|
||||
}
|
||||
|
||||
read(slice.call(blob, start, end, self._type));
|
||||
} else {
|
||||
// Return the entire binary data
|
||||
read(blob);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/** @method DataMan.prototype.saveAs
|
||||
* @public
|
||||
* @param {String} [filename]
|
||||
* @return {undefined}
|
||||
*
|
||||
* Tells the browser to save the data like a normal downloaded file,
|
||||
* using the provided filename.
|
||||
*
|
||||
*/
|
||||
DataMan.prototype.saveAs = function dataManSaveAs(filename) {
|
||||
var self = this;
|
||||
|
||||
if (typeof window === "undefined")
|
||||
throw new Error("window must be defined to use saveLocal");
|
||||
|
||||
if (!window.saveAs) {
|
||||
console.warn('DataMan.saveAs: window.saveAs not supported by this browser - add cfs-filesaver package');
|
||||
return;
|
||||
}
|
||||
|
||||
self.getBlob(function (error, blob) {
|
||||
if (error) {
|
||||
throw error;
|
||||
} else {
|
||||
window.saveAs(blob, filename);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @method DataMan.prototype.getDataUri
|
||||
* @public
|
||||
* @param {function} callback callback(err, dataUri)
|
||||
*/
|
||||
DataMan.prototype.getDataUri = function dataManGetDataUri(callback) {
|
||||
// XXX: We could consider using: URL.createObjectURL(blob);
|
||||
// This will create a reference to the blob data instead of a clone
|
||||
// This is part of the File API - as the rest - Not sure how to generally
|
||||
// support from IE10, FF26, Chrome 31, safari 7, opera 19, ios 6, android 4
|
||||
|
||||
var self = this;
|
||||
|
||||
if (typeof callback !== 'function')
|
||||
throw new Error("getDataUri requires callback function");
|
||||
|
||||
if (typeof FileReader === "undefined") {
|
||||
callback(new Error("Browser does not support FileReader"));
|
||||
return;
|
||||
}
|
||||
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function(event) {
|
||||
var dataUri = event.target.result;
|
||||
callback(null, dataUri);
|
||||
};
|
||||
fileReader.onerror = function(err) {
|
||||
callback(err);
|
||||
};
|
||||
|
||||
self.getBlob(function (error, blob) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
fileReader.readAsDataURL(blob);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @method DataMan.prototype.size
|
||||
* @public
|
||||
* @param {function} [callback] callback(err, size)
|
||||
*
|
||||
* Passes the size of the data to the callback, if provided,
|
||||
* or returns it. A callback is required to get the size of a URL on the client.
|
||||
*/
|
||||
DataMan.prototype.size = function dataManSize(callback) {
|
||||
var self = this;
|
||||
|
||||
if (callback) {
|
||||
if (typeof self._size === "number") {
|
||||
callback(null, self._size);
|
||||
} else {
|
||||
self.getBlob(function (error, blob) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
} else {
|
||||
self._size = blob.size;
|
||||
callback(null, self._size);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (self.url) {
|
||||
throw new Error("On the client, DataMan.size requires a callback when getting size for a URL on the client");
|
||||
} else if (typeof self._size === "number") {
|
||||
return self._size;
|
||||
} else {
|
||||
var blob = self.getBlob();
|
||||
self._size = blob.size;
|
||||
return self._size;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method DataMan.prototype.type
|
||||
* @public
|
||||
*
|
||||
* Returns the type of the data.
|
||||
*/
|
||||
DataMan.prototype.type = function dataManType() {
|
||||
return this._type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method dataURItoBlob
|
||||
* @private
|
||||
* @param {String} dataURI The data URI
|
||||
* @param {String} dataTYPE The content type
|
||||
* @returns {Blob} A new Blob instance
|
||||
*
|
||||
* Converts a data URI to a Blob.
|
||||
*/
|
||||
function dataURItoBlob(dataURI, dataTYPE) {
|
||||
var str = atob(dataURI.split(',')[1]), array = [];
|
||||
for(var i = 0; i < str.length; i++) array.push(str.charCodeAt(i));
|
||||
return new Blob([new Uint8Array(array)], {type: dataTYPE});
|
||||
}
|
||||
|
||||
/**
|
||||
* @method defaultCallback
|
||||
* @private
|
||||
* @param {Error} [err]
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Can be used as a default callback for client methods that need a callback.
|
||||
* Simply throws the provided error if there is one.
|
||||
*/
|
||||
function defaultCallback(err) {
|
||||
if (err) {
|
||||
// Show gentle error if Meteor error
|
||||
if (err instanceof Meteor.Error) {
|
||||
console.error(err.message);
|
||||
} else {
|
||||
// Normal error, just throw error
|
||||
throw err;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue