mirror of
https://github.com/wekan/wekan.git
synced 2025-12-24 03:10:12 +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
5
packages/wekan-cfs-base-package/.travis.yml
Normal file
5
packages/wekan-cfs-base-package/.travis.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
before_install:
|
||||
- "curl -L http://git.io/s0Zu-w | /bin/sh"
|
||||
20
packages/wekan-cfs-base-package/LICENSE.md
Normal file
20
packages/wekan-cfs-base-package/LICENSE.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2015 [@raix](https://github.com/raix) and [@aldeed](https://github.com/aldeed), aka Morten N.O. Nørgaard Henriksen, mh@gi-software.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
11
packages/wekan-cfs-base-package/README.md
Normal file
11
packages/wekan-cfs-base-package/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
wekan-cfs-base-package
|
||||
=========================
|
||||
|
||||
This is a Meteor package used by
|
||||
[CollectionFS](https://github.com/zcfs/Meteor-CollectionFS).
|
||||
|
||||
You don't need to manually add this package to your app. It is added when you
|
||||
add the `wekan-cfs-standard-packages` package.
|
||||
|
||||
This package provides the `FS` namespace and helper methods used by many
|
||||
CollectionFS packages.
|
||||
213
packages/wekan-cfs-base-package/api.md
Normal file
213
packages/wekan-cfs-base-package/api.md
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
## cfs-base-package Public API ##
|
||||
|
||||
CollectionFS, Base package
|
||||
|
||||
_API documentation automatically generated by [docmeteor](https://github.com/raix/docmeteor)._
|
||||
|
||||
#############################################################################
|
||||
|
||||
HELPERS
|
||||
|
||||
#############################################################################
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.cloneFileRecord"></a>*fsUtility*.cloneFileRecord(rec, [options]) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __cloneFileRecord__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __rec__ *{[FS.File](#FS.File)|[FS.Collection filerecord](#FS.Collection filerecord)}*
|
||||
* __options__ *{Object}* (Optional)
|
||||
* __full__ *{Boolean}* (Optional, Default = false)
|
||||
|
||||
Set `true` to prevent certain properties from being omitted from the clone.
|
||||
|
||||
|
||||
__Returns__ *{Object}*
|
||||
Cloned filerecord
|
||||
|
||||
|
||||
Makes a shallow clone of `rec`, filtering out some properties that might be present if
|
||||
it's an FS.File instance, but which we never want to be part of the stored
|
||||
filerecord.
|
||||
|
||||
This is a blacklist clone rather than a whitelist because we want the user to be able
|
||||
to specify whatever additional properties they wish.
|
||||
|
||||
In general, we expect the following whitelist properties used by the internal and
|
||||
external APIs:
|
||||
|
||||
_id, name, size, type, chunkCount, chunkSize, chunkSum, copies, createdAt, updatedAt, uploadedAt
|
||||
|
||||
Those properties, and any additional properties added by the user, should be present
|
||||
in the returned object, which is suitable for inserting into the backing collection or
|
||||
extending an FS.File instance.
|
||||
|
||||
|
||||
> ```FS.Utility.cloneFileRecord = function(rec, options) { ...``` [base-common.js:71](base-common.js#L71)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.defaultCallback"></a>*fsUtility*.defaultCallback([err]) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __defaultCallback__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __err__ *{[Error](#Error)}* (Optional)
|
||||
|
||||
__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.
|
||||
|
||||
> ```FS.Utility.defaultCallback = function defaultCallback(err) { ...``` [base-common.js:96](base-common.js#L96)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.defaultCallback"></a>*fsUtility*.defaultCallback([f], [err]) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __defaultCallback__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __f__ *{Function}* (Optional)
|
||||
|
||||
A callback function, if you have one. Can be undefined or null.
|
||||
|
||||
* __err__ *{[Meteor.Error ](#Meteor.Error )|[ Error ](# Error )|[ String](# String)}* (Optional)
|
||||
|
||||
Error or error message (string)
|
||||
|
||||
|
||||
__Returns__ *{Any}*
|
||||
the callback result if any
|
||||
|
||||
|
||||
Handle Error, creates an Error instance with the given text. If callback is
|
||||
a function, passes the error to that function. Otherwise throws it. Useful
|
||||
for dealing with errors in methods that optionally accept a callback.
|
||||
|
||||
> ```FS.Utility.handleError = function(f, err, result) { ...``` [base-common.js:120](base-common.js#L120)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.noop"></a>*fsUtility*.noop() <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __noop__ is defined in `FS.Utility`*
|
||||
|
||||
Use this to hand a no operation / empty function
|
||||
|
||||
> ```FS.Utility.noop = function() { ...``` [base-common.js:134](base-common.js#L134)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.getFileExtension"></a>*fsUtility*.getFileExtension(name) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __getFileExtension__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String}*
|
||||
|
||||
A filename, filepath, or URL that may or may not have an extension.
|
||||
|
||||
|
||||
__Returns__ *{String}*
|
||||
The extension or an empty string if no extension found.
|
||||
|
||||
|
||||
> ```FS.Utility.getFileExtension = function utilGetFileExtension(name) { ...``` [base-common.js:205](base-common.js#L205)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.setFileExtension"></a>*fsUtility*.setFileExtension(name, ext) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __setFileExtension__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String}*
|
||||
|
||||
A filename that may or may not already have an extension.
|
||||
|
||||
* __ext__ *{String}*
|
||||
|
||||
An extension without leading period, which you want to be the new extension on `name`.
|
||||
|
||||
|
||||
__Returns__ *{String}*
|
||||
The filename with changed extension.
|
||||
|
||||
|
||||
> ```FS.Utility.setFileExtension = function utilSetFileExtension(name, ext) { ...``` [base-common.js:222](base-common.js#L222)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.binaryToBuffer"></a>*fsUtility*.binaryToBuffer(data) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __binaryToBuffer__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __data__ *{Uint8Array}*
|
||||
|
||||
__Returns__ *{Buffer}*
|
||||
|
||||
|
||||
Converts a Uint8Array instance to a Node Buffer instance
|
||||
|
||||
> ```FS.Utility.binaryToBuffer = function(data) { ...``` [base-server.js:9](base-server.js#L9)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.bufferToBinary"></a>*fsUtility*.bufferToBinary(data) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __bufferToBinary__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __data__ *{Buffer}*
|
||||
|
||||
__Returns__ *{Uint8Array}*
|
||||
|
||||
|
||||
Converts a Node Buffer instance to a Uint8Array instance
|
||||
|
||||
> ```FS.Utility.bufferToBinary = function(data) { ...``` [base-server.js:26](base-server.js#L26)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.eachFile"></a>*fsUtility*.eachFile(e, f) <sub><i>Client</i></sub> ###
|
||||
|
||||
*This method __eachFile__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __e__ *{[Event](#Event)}*
|
||||
|
||||
Browser event
|
||||
|
||||
* __f__ *{Function}*
|
||||
|
||||
Function to run for each file found in the event.
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Utility for iteration over files in event
|
||||
|
||||
> ```FS.Utility.eachFile = function(e, f) { ...``` [base-client.js:37](base-client.js#L37)
|
||||
|
||||
|
||||
51
packages/wekan-cfs-base-package/base-client.js
Normal file
51
packages/wekan-cfs-base-package/base-client.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
//XXX not sure this is still working properly?
|
||||
FS.Utility.connectionLogin = function(connection) {
|
||||
// We check if the accounts package is installed, since we depend on
|
||||
// `Meteor.userId()`
|
||||
if (typeof Accounts !== 'undefined') {
|
||||
// Monitor logout from main connection
|
||||
Meteor.startup(function() {
|
||||
Tracker.autorun(function() {
|
||||
var userId = Meteor.userId();
|
||||
if (userId) {
|
||||
connection.onReconnect = function() {
|
||||
var token = Accounts._storedLoginToken();
|
||||
connection.apply('login', [{resume: token}], function(err, result) {
|
||||
if (!err && result) {
|
||||
connection.setUserId(result.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
} else {
|
||||
connection.onReconnect = null;
|
||||
connection.setUserId(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.eachFile
|
||||
* @public
|
||||
* @param {Event} e - Browser event
|
||||
* @param {Function} f - Function to run for each file found in the event.
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Utility for iteration over files in event
|
||||
*/
|
||||
FS.Utility.eachFile = function(e, f) {
|
||||
var evt = (e.originalEvent || e);
|
||||
|
||||
var files = evt.target.files;
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
files = evt.dataTransfer ? evt.dataTransfer.files : [];
|
||||
}
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
f(files[i], i);
|
||||
}
|
||||
};
|
||||
317
packages/wekan-cfs-base-package/base-common.js
Normal file
317
packages/wekan-cfs-base-package/base-common.js
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
// Exported namespace
|
||||
FS = {};
|
||||
|
||||
// namespace for adapters; XXX should this be added by cfs-storage-adapter pkg instead?
|
||||
FS.Store = {
|
||||
GridFS: function () {
|
||||
throw new Error('To use FS.Store.GridFS, you must add the "wekan-cfs-gridfs" package.');
|
||||
},
|
||||
FileSystem: function () {
|
||||
throw new Error('To use FS.Store.FileSystem, you must add the "wekan-cfs-filesystem" package.');
|
||||
},
|
||||
S3: function () {
|
||||
throw new Error('To use FS.Store.S3, you must add the "wekan-cfs-s3" package.');
|
||||
},
|
||||
WABS: function () {
|
||||
throw new Error('To use FS.Store.WABS, you must add the "wekan-cfs-wabs" package.');
|
||||
},
|
||||
Dropbox: function () {
|
||||
throw new Error('To use FS.Store.Dropbox, you must add the "wekan-cfs-dropbox" package.');
|
||||
}
|
||||
};
|
||||
|
||||
// namespace for access points
|
||||
FS.AccessPoint = {};
|
||||
|
||||
// namespace for utillities
|
||||
FS.Utility = {};
|
||||
|
||||
// A general place for any package to store global config settings
|
||||
FS.config = {};
|
||||
|
||||
// An internal collection reference
|
||||
FS._collections = {};
|
||||
|
||||
// Test scope
|
||||
_Utility = {};
|
||||
|
||||
// #############################################################################
|
||||
//
|
||||
// HELPERS
|
||||
//
|
||||
// #############################################################################
|
||||
|
||||
/** @method _Utility.defaultZero
|
||||
* @private
|
||||
* @param {Any} val Returns number or 0 if value is a falsy
|
||||
*/
|
||||
_Utility.defaultZero = function(val) {
|
||||
return +(val || 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.cloneFileRecord
|
||||
* @public
|
||||
* @param {FS.File|FS.Collection filerecord} rec
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.full=false] Set `true` to prevent certain properties from being omitted from the clone.
|
||||
* @returns {Object} Cloned filerecord
|
||||
*
|
||||
* Makes a shallow clone of `rec`, filtering out some properties that might be present if
|
||||
* it's an FS.File instance, but which we never want to be part of the stored
|
||||
* filerecord.
|
||||
*
|
||||
* This is a blacklist clone rather than a whitelist because we want the user to be able
|
||||
* to specify whatever additional properties they wish.
|
||||
*
|
||||
* In general, we expect the following whitelist properties used by the internal and
|
||||
* external APIs:
|
||||
*
|
||||
* _id, name, size, type, chunkCount, chunkSize, chunkSum, copies, createdAt, updatedAt, uploadedAt
|
||||
*
|
||||
* Those properties, and any additional properties added by the user, should be present
|
||||
* in the returned object, which is suitable for inserting into the backing collection or
|
||||
* extending an FS.File instance.
|
||||
*
|
||||
*/
|
||||
FS.Utility.cloneFileRecord = function(rec, options) {
|
||||
options = options || {};
|
||||
var result = {};
|
||||
// We use this method for two purposes. If using it to clone one FS.File into another, then
|
||||
// we want a full clone. But if using it to get a filerecord object for inserting into the
|
||||
// internal collection, then there are certain properties we want to omit so that they aren't
|
||||
// stored in the collection.
|
||||
var omit = options.full ? [] : ['collectionName', 'collection', 'data', 'createdByTransform'];
|
||||
for (var prop in rec) {
|
||||
if (rec.hasOwnProperty(prop) && !_.contains(omit, prop)) {
|
||||
result[prop] = rec[prop];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.defaultCallback
|
||||
* @public
|
||||
* @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.
|
||||
*/
|
||||
FS.Utility.defaultCallback = 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;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.defaultCallback
|
||||
* @public
|
||||
* @param {Function} [f] A callback function, if you have one. Can be undefined or null.
|
||||
* @param {Meteor.Error | Error | String} [err] Error or error message (string)
|
||||
* @returns {Any} the callback result if any
|
||||
*
|
||||
* Handle Error, creates an Error instance with the given text. If callback is
|
||||
* a function, passes the error to that function. Otherwise throws it. Useful
|
||||
* for dealing with errors in methods that optionally accept a callback.
|
||||
*/
|
||||
FS.Utility.handleError = function(f, err, result) {
|
||||
// Set callback
|
||||
var callback = (typeof f === 'function')? f : FS.Utility.defaultCallback;
|
||||
// Set the err
|
||||
var error = (err === ''+err)? new Error(err) : err;
|
||||
// callback
|
||||
return callback(error, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @method FS.Utility.noop
|
||||
* @public
|
||||
* Use this to hand a no operation / empty function
|
||||
*/
|
||||
FS.Utility.noop = function() {};
|
||||
|
||||
/**
|
||||
* @method validateAction
|
||||
* @private
|
||||
* @param {Object} validators - The validators object to use, with `deny` and `allow` properties.
|
||||
* @param {FS.File} fileObj - Mounted or mountable file object to be passed to validators.
|
||||
* @param {String} userId - The ID of the user who is attempting the action.
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Throws a "400-Bad Request" Meteor error if the file is not mounted or
|
||||
* a "400-Access denied" Meteor error if the action is not allowed.
|
||||
*/
|
||||
FS.Utility.validateAction = function validateAction(validators, fileObj, userId) {
|
||||
var denyValidators = validators.deny;
|
||||
var allowValidators = validators.allow;
|
||||
|
||||
// If insecure package is used and there are no validators defined,
|
||||
// allow the action.
|
||||
if (typeof Package === 'object'
|
||||
&& Package.insecure
|
||||
&& denyValidators.length + allowValidators.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If already mounted, validators should receive a fileObj
|
||||
// that is fully populated
|
||||
if (fileObj.isMounted()) {
|
||||
fileObj.getFileRecord();
|
||||
}
|
||||
|
||||
// Any deny returns true means denied.
|
||||
if (_.any(denyValidators, function(validator) {
|
||||
return validator(userId, fileObj);
|
||||
})) {
|
||||
throw new Meteor.Error(403, "Access denied");
|
||||
}
|
||||
// Any allow returns true means proceed. Throw error if they all fail.
|
||||
if (_.all(allowValidators, function(validator) {
|
||||
return !validator(userId, fileObj);
|
||||
})) {
|
||||
throw new Meteor.Error(403, "Access denied");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.getFileName
|
||||
* @private
|
||||
* @param {String} name - A filename, filepath, or URL
|
||||
* @returns {String} The filename without the URL, filepath, or query string
|
||||
*/
|
||||
FS.Utility.getFileName = function utilGetFileName(name) {
|
||||
// in case it's a URL, strip off potential query string
|
||||
// should have no effect on filepath
|
||||
name = name.split('?')[0];
|
||||
// strip off beginning path or url
|
||||
var lastSlash = name.lastIndexOf('/');
|
||||
if (lastSlash !== -1) {
|
||||
name = name.slice(lastSlash + 1);
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.getFileExtension
|
||||
* @public
|
||||
* @param {String} name - A filename, filepath, or URL that may or may not have an extension.
|
||||
* @returns {String} The extension or an empty string if no extension found.
|
||||
*/
|
||||
FS.Utility.getFileExtension = function utilGetFileExtension(name) {
|
||||
name = FS.Utility.getFileName(name);
|
||||
// Seekout the last '.' if found
|
||||
var found = name.lastIndexOf('.');
|
||||
// Return the extension if found else ''
|
||||
// If found is -1, we return '' because there is no extension
|
||||
// If found is 0, we return '' because it's a hidden file
|
||||
return (found > 0 ? name.slice(found + 1).toLowerCase() : '');
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.setFileExtension
|
||||
* @public
|
||||
* @param {String} name - A filename that may or may not already have an extension.
|
||||
* @param {String} ext - An extension without leading period, which you want to be the new extension on `name`.
|
||||
* @returns {String} The filename with changed extension.
|
||||
*/
|
||||
FS.Utility.setFileExtension = function utilSetFileExtension(name, ext) {
|
||||
if (!name || !name.length) {
|
||||
return name;
|
||||
}
|
||||
var currentExt = FS.Utility.getFileExtension(name);
|
||||
if (currentExt.length) {
|
||||
name = name.slice(0, currentExt.length * -1) + ext;
|
||||
} else {
|
||||
name = name + '.' + ext;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
/*
|
||||
* Borrowed these from http package
|
||||
*/
|
||||
FS.Utility.encodeParams = function encodeParams(params) {
|
||||
var buf = [];
|
||||
_.each(params, function(value, key) {
|
||||
if (buf.length)
|
||||
buf.push('&');
|
||||
buf.push(FS.Utility.encodeString(key), '=', FS.Utility.encodeString(value));
|
||||
});
|
||||
return buf.join('').replace(/%20/g, '+');
|
||||
};
|
||||
|
||||
FS.Utility.encodeString = function encodeString(str) {
|
||||
return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
|
||||
};
|
||||
|
||||
/*
|
||||
* btoa and atob shims for client and server
|
||||
*/
|
||||
|
||||
FS.Utility._btoa = function _fsUtility_btoa(str) {
|
||||
var buffer;
|
||||
|
||||
if (str instanceof Buffer) {
|
||||
buffer = str;
|
||||
} else {
|
||||
buffer = new Buffer(str.toString(), 'binary');
|
||||
}
|
||||
|
||||
return buffer.toString('base64');
|
||||
};
|
||||
|
||||
FS.Utility.btoa = function fsUtility_btoa(str) {
|
||||
if (typeof btoa === 'function') {
|
||||
// Client
|
||||
return btoa(str);
|
||||
} else if (typeof Buffer !== 'undefined') {
|
||||
// Server
|
||||
return FS.Utility._btoa(str);
|
||||
} else {
|
||||
throw new Error('FS.Utility.btoa: Cannot base64 encode on your system');
|
||||
}
|
||||
};
|
||||
|
||||
FS.Utility._atob = function _fsUtility_atob(str) {
|
||||
return new Buffer(str, 'base64').toString('binary');
|
||||
};
|
||||
|
||||
FS.Utility.atob = function fsUtility_atob(str) {
|
||||
if (typeof atob === 'function') {
|
||||
// Client
|
||||
return atob(str);
|
||||
} else if (typeof Buffer !== 'undefined') {
|
||||
// Server
|
||||
return FS.Utility._atob(str);
|
||||
} else {
|
||||
throw new Error('FS.Utility.atob: Cannot base64 encode on your system');
|
||||
}
|
||||
};
|
||||
|
||||
// Api wrap for 3party libs like underscore
|
||||
FS.Utility.extend = _.extend;
|
||||
|
||||
FS.Utility.each = _.each;
|
||||
|
||||
FS.Utility.isEmpty = _.isEmpty;
|
||||
|
||||
FS.Utility.indexOf = _.indexOf;
|
||||
|
||||
FS.Utility.isArray = _.isArray;
|
||||
|
||||
FS.Utility.map = _.map;
|
||||
|
||||
FS.Utility.once = _.once;
|
||||
|
||||
FS.Utility.include = _.include;
|
||||
|
||||
FS.Utility.size = _.size;
|
||||
95
packages/wekan-cfs-base-package/base-server.js
Normal file
95
packages/wekan-cfs-base-package/base-server.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @method FS.Utility.binaryToBuffer
|
||||
* @public
|
||||
* @param {Uint8Array} data
|
||||
* @returns {Buffer}
|
||||
*
|
||||
* Converts a Uint8Array instance to a Node Buffer instance
|
||||
*/
|
||||
FS.Utility.binaryToBuffer = function(data) {
|
||||
var len = data.length;
|
||||
var buffer = new Buffer(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
buffer[i] = data[i];
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.bufferToBinary
|
||||
* @public
|
||||
* @param {Buffer} data
|
||||
* @returns {Uint8Array}
|
||||
*
|
||||
* Converts a Node Buffer instance to a Uint8Array instance
|
||||
*/
|
||||
FS.Utility.bufferToBinary = function(data) {
|
||||
var len = data.length;
|
||||
var binary = EJSON.newBinary(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
binary[i] = data[i];
|
||||
}
|
||||
return binary;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.safeCallback
|
||||
* @public
|
||||
* @param {Function} callback
|
||||
* @returns {Function}
|
||||
*
|
||||
* Makes a callback safe for Meteor code
|
||||
*/
|
||||
FS.Utility.safeCallback = function (callback) {
|
||||
return Meteor.bindEnvironment(callback, function(err) { throw err; });
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.safeStream
|
||||
* @public
|
||||
* @param {Stream} nodestream
|
||||
* @returns {Stream}
|
||||
*
|
||||
* Adds `safeOn` and `safeOnce` methods to a NodeJS Stream
|
||||
* object. These are the same as `on` and `once`, except
|
||||
* that the callback is wrapped for use in Meteor.
|
||||
*/
|
||||
FS.Utility.safeStream = function(nodestream) {
|
||||
if (!nodestream || typeof nodestream.on !== 'function')
|
||||
throw new Error('FS.Utility.safeStream requires a NodeJS Stream');
|
||||
|
||||
// Create Meteor safe events
|
||||
nodestream.safeOn = function(name, callback) {
|
||||
return nodestream.on(name, FS.Utility.safeCallback(callback));
|
||||
};
|
||||
|
||||
// Create Meteor safe events
|
||||
nodestream.safeOnce = function(name, callback) {
|
||||
return nodestream.once(name, FS.Utility.safeCallback(callback));
|
||||
};
|
||||
|
||||
// Return the modified stream - modified anyway
|
||||
return nodestream;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method FS.Utility.eachFileFromPath
|
||||
* @public
|
||||
* @param {String} p - Server path
|
||||
* @param {Function} f - Function to run for each file found in the path.
|
||||
* @returns {undefined}
|
||||
*
|
||||
* Utility for iteration over files from path on server
|
||||
*/
|
||||
FS.Utility.eachFileFromPath = function(p, f) {
|
||||
var fs = Npm.require('fs');
|
||||
var path = Npm.require('path');
|
||||
var files = fs.readdirSync(p);
|
||||
files.map(function (file) {
|
||||
return path.join(p, file);
|
||||
}).filter(function (filePath) {
|
||||
return fs.statSync(filePath).isFile() && path.basename(filePath)[0] !== '.';
|
||||
}).forEach(function (filePath) {
|
||||
f(filePath);
|
||||
});
|
||||
};
|
||||
293
packages/wekan-cfs-base-package/internal.api.md
Normal file
293
packages/wekan-cfs-base-package/internal.api.md
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
## Public and Private API ##
|
||||
|
||||
_API documentation automatically generated by [docmeteor](https://github.com/raix/docmeteor)._
|
||||
|
||||
***
|
||||
|
||||
__File: ["base-common.js"](base-common.js) Where: {server|client}__
|
||||
|
||||
***
|
||||
|
||||
#############################################################################
|
||||
|
||||
HELPERS
|
||||
|
||||
#############################################################################
|
||||
-
|
||||
|
||||
### <a name="_Utility.defaultZero"></a>*_utility*.defaultZero(val) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __defaultZero__ is defined in `_Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __val__ *{Any}*
|
||||
|
||||
Returns number or 0 if value is a falsy
|
||||
|
||||
|
||||
> ```_Utility.defaultZero = function(val) { ...``` [base-common.js:42](base-common.js#L42)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.cloneFileRecord"></a>*fsUtility*.cloneFileRecord(rec, [options]) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __cloneFileRecord__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __rec__ *{[FS.File](#FS.File)|[FS.Collection filerecord](#FS.Collection filerecord)}*
|
||||
* __options__ *{Object}* (Optional)
|
||||
* __full__ *{Boolean}* (Optional, Default = false)
|
||||
|
||||
Set `true` to prevent certain properties from being omitted from the clone.
|
||||
|
||||
|
||||
__Returns__ *{Object}*
|
||||
Cloned filerecord
|
||||
|
||||
|
||||
Makes a shallow clone of `rec`, filtering out some properties that might be present if
|
||||
it's an FS.File instance, but which we never want to be part of the stored
|
||||
filerecord.
|
||||
|
||||
This is a blacklist clone rather than a whitelist because we want the user to be able
|
||||
to specify whatever additional properties they wish.
|
||||
|
||||
In general, we expect the following whitelist properties used by the internal and
|
||||
external APIs:
|
||||
|
||||
_id, name, size, type, chunkCount, chunkSize, chunkSum, copies, createdAt, updatedAt, uploadedAt
|
||||
|
||||
Those properties, and any additional properties added by the user, should be present
|
||||
in the returned object, which is suitable for inserting into the backing collection or
|
||||
extending an FS.File instance.
|
||||
|
||||
|
||||
> ```FS.Utility.cloneFileRecord = function(rec, options) { ...``` [base-common.js:71](base-common.js#L71)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.defaultCallback"></a>*fsUtility*.defaultCallback([err]) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __defaultCallback__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __err__ *{[Error](#Error)}* (Optional)
|
||||
|
||||
__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.
|
||||
|
||||
> ```FS.Utility.defaultCallback = function defaultCallback(err) { ...``` [base-common.js:96](base-common.js#L96)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.defaultCallback"></a>*fsUtility*.defaultCallback([f], [err]) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __defaultCallback__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __f__ *{Function}* (Optional)
|
||||
|
||||
A callback function, if you have one. Can be undefined or null.
|
||||
|
||||
* __err__ *{[Meteor.Error ](#Meteor.Error )|[ Error ](# Error )|[ String](# String)}* (Optional)
|
||||
|
||||
Error or error message (string)
|
||||
|
||||
|
||||
__Returns__ *{Any}*
|
||||
the callback result if any
|
||||
|
||||
|
||||
Handle Error, creates an Error instance with the given text. If callback is
|
||||
a function, passes the error to that function. Otherwise throws it. Useful
|
||||
for dealing with errors in methods that optionally accept a callback.
|
||||
|
||||
> ```FS.Utility.handleError = function(f, err, result) { ...``` [base-common.js:120](base-common.js#L120)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.noop"></a>*fsUtility*.noop() <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __noop__ is defined in `FS.Utility`*
|
||||
|
||||
Use this to hand a no operation / empty function
|
||||
|
||||
> ```FS.Utility.noop = function() { ...``` [base-common.js:134](base-common.js#L134)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="validateAction"></a>validateAction(validators, fileObj, userId) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __validators__ *{Object}*
|
||||
|
||||
The validators object to use, with `deny` and `allow` properties.
|
||||
|
||||
* __fileObj__ *{[FS.File](#FS.File)}*
|
||||
|
||||
Mounted or mountable file object to be passed to validators.
|
||||
|
||||
* __userId__ *{String}*
|
||||
|
||||
The ID of the user who is attempting the action.
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Throws a "400-Bad Request" Meteor error if the file is not mounted or
|
||||
a "400-Access denied" Meteor error if the action is not allowed.
|
||||
|
||||
> ```FS.Utility.validateAction = function validateAction(validators, fileObj, userId) { ...``` [base-common.js:147](base-common.js#L147)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.getFileName"></a>*fsUtility*.getFileName(name) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method is private*
|
||||
*This method __getFileName__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String}*
|
||||
|
||||
A filename, filepath, or URL
|
||||
|
||||
|
||||
__Returns__ *{String}*
|
||||
The filename without the URL, filepath, or query string
|
||||
|
||||
|
||||
> ```FS.Utility.getFileName = function utilGetFileName(name) { ...``` [base-common.js:187](base-common.js#L187)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.getFileExtension"></a>*fsUtility*.getFileExtension(name) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __getFileExtension__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String}*
|
||||
|
||||
A filename, filepath, or URL that may or may not have an extension.
|
||||
|
||||
|
||||
__Returns__ *{String}*
|
||||
The extension or an empty string if no extension found.
|
||||
|
||||
|
||||
> ```FS.Utility.getFileExtension = function utilGetFileExtension(name) { ...``` [base-common.js:205](base-common.js#L205)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.setFileExtension"></a>*fsUtility*.setFileExtension(name, ext) <sub><i>Anywhere</i></sub> ###
|
||||
|
||||
*This method __setFileExtension__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __name__ *{String}*
|
||||
|
||||
A filename that may or may not already have an extension.
|
||||
|
||||
* __ext__ *{String}*
|
||||
|
||||
An extension without leading period, which you want to be the new extension on `name`.
|
||||
|
||||
|
||||
__Returns__ *{String}*
|
||||
The filename with changed extension.
|
||||
|
||||
|
||||
> ```FS.Utility.setFileExtension = function utilSetFileExtension(name, ext) { ...``` [base-common.js:222](base-common.js#L222)
|
||||
|
||||
|
||||
***
|
||||
|
||||
__File: ["base-server.js"](base-server.js) Where: {server}__
|
||||
|
||||
***
|
||||
|
||||
### <a name="FS.Utility.binaryToBuffer"></a>*fsUtility*.binaryToBuffer(data) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __binaryToBuffer__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __data__ *{Uint8Array}*
|
||||
|
||||
__Returns__ *{Buffer}*
|
||||
|
||||
|
||||
Converts a Uint8Array instance to a Node Buffer instance
|
||||
|
||||
> ```FS.Utility.binaryToBuffer = function(data) { ...``` [base-server.js:9](base-server.js#L9)
|
||||
|
||||
|
||||
-
|
||||
|
||||
### <a name="FS.Utility.bufferToBinary"></a>*fsUtility*.bufferToBinary(data) <sub><i>Server</i></sub> ###
|
||||
|
||||
*This method __bufferToBinary__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __data__ *{Buffer}*
|
||||
|
||||
__Returns__ *{Uint8Array}*
|
||||
|
||||
|
||||
Converts a Node Buffer instance to a Uint8Array instance
|
||||
|
||||
> ```FS.Utility.bufferToBinary = function(data) { ...``` [base-server.js:26](base-server.js#L26)
|
||||
|
||||
|
||||
***
|
||||
|
||||
__File: ["base-client.js"](base-client.js) Where: {client}__
|
||||
|
||||
***
|
||||
|
||||
### <a name="FS.Utility.eachFile"></a>*fsUtility*.eachFile(e, f) <sub><i>Client</i></sub> ###
|
||||
|
||||
*This method __eachFile__ is defined in `FS.Utility`*
|
||||
|
||||
__Arguments__
|
||||
|
||||
* __e__ *{[Event](#Event)}*
|
||||
|
||||
Browser event
|
||||
|
||||
* __f__ *{Function}*
|
||||
|
||||
Function to run for each file found in the event.
|
||||
|
||||
|
||||
__Returns__ *{undefined}*
|
||||
|
||||
|
||||
Utility for iteration over files in event
|
||||
|
||||
> ```FS.Utility.eachFile = function(e, f) { ...``` [base-client.js:37](base-client.js#L37)
|
||||
|
||||
|
||||
37
packages/wekan-cfs-base-package/package.js
Normal file
37
packages/wekan-cfs-base-package/package.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
Package.describe({
|
||||
version: '0.0.30',
|
||||
name: 'wekan-cfs-base-package',
|
||||
summary: 'CollectionFS, Base package',
|
||||
git: 'https://github.com/zcfs/Meteor-cfs-base-package.git'
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.versionsFrom('1.0');
|
||||
|
||||
api.use(['deps', 'underscore', 'ejson']);
|
||||
|
||||
if (api.export) {
|
||||
api.export('FS');
|
||||
api.export('_Utility', { testOnly: true });
|
||||
}
|
||||
|
||||
api.addFiles([
|
||||
'base-common.js',
|
||||
'base-server.js'
|
||||
], 'server');
|
||||
|
||||
api.addFiles([
|
||||
'polyfill.base64.js',
|
||||
'base-common.js',
|
||||
'base-client.js'
|
||||
], 'client');
|
||||
});
|
||||
|
||||
// Package.on_test(function (api) {
|
||||
// api.use(['wekan-cfs-base-package', 'cfs-file']);
|
||||
// api.use('test-helpers', 'server');
|
||||
// api.use(['tinytest', 'underscore', 'ejson', 'ordered-dict',
|
||||
// 'random', 'deps']);
|
||||
|
||||
// api.add_files('tests/common-tests.js', ['client', 'server']);
|
||||
// });
|
||||
179
packages/wekan-cfs-base-package/polyfill.base64.js
Normal file
179
packages/wekan-cfs-base-package/polyfill.base64.js
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2010 Nick Galbreath
|
||||
* http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* base64 encode/decode compatible with window.btoa/atob
|
||||
*
|
||||
* window.atob/btoa is a Firefox extension to convert binary data (the "b")
|
||||
* to base64 (ascii, the "a").
|
||||
*
|
||||
* It is also found in Safari and Chrome. It is not available in IE.
|
||||
*
|
||||
* if (!window.btoa) window.btoa = base64.encode
|
||||
* if (!window.atob) window.atob = base64.decode
|
||||
*
|
||||
* The original spec's for atob/btoa are a bit lacking
|
||||
* https://developer.mozilla.org/en/DOM/window.atob
|
||||
* https://developer.mozilla.org/en/DOM/window.btoa
|
||||
*
|
||||
* window.btoa and base64.encode takes a string where charCodeAt is [0,255]
|
||||
* If any character is not [0,255], then an DOMException(5) is thrown.
|
||||
*
|
||||
* window.atob and base64.decode take a base64-encoded string
|
||||
* If the input length is not a multiple of 4, or contains invalid characters
|
||||
* then an DOMException(5) is thrown.
|
||||
*/
|
||||
var base64 = {};
|
||||
base64.PADCHAR = '=';
|
||||
base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
||||
base64.makeDOMException = function() {
|
||||
// sadly in FF,Safari,Chrome you can't make a DOMException
|
||||
var e, tmp;
|
||||
|
||||
try {
|
||||
return new DOMException(DOMException.INVALID_CHARACTER_ERR);
|
||||
} catch (tmp) {
|
||||
// not available, just passback a duck-typed equiv
|
||||
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error
|
||||
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/prototype
|
||||
var ex = new Error("DOM Exception 5");
|
||||
|
||||
// ex.number and ex.description is IE-specific.
|
||||
ex.code = ex.number = 5;
|
||||
ex.name = ex.description = "INVALID_CHARACTER_ERR";
|
||||
|
||||
// Safari/Chrome output format
|
||||
ex.toString = function() { return 'Error: ' + ex.name + ': ' + ex.message; };
|
||||
return ex;
|
||||
}
|
||||
}
|
||||
|
||||
base64.getbyte64 = function(s,i) {
|
||||
// This is oddly fast, except on Chrome/V8.
|
||||
// Minimal or no improvement in performance by using a
|
||||
// object with properties mapping chars to value (eg. 'A': 0)
|
||||
var idx = base64.ALPHA.indexOf(s.charAt(i));
|
||||
if (idx === -1) {
|
||||
throw base64.makeDOMException();
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
base64.decode = function(s) {
|
||||
// convert to string
|
||||
s = '' + s;
|
||||
var getbyte64 = base64.getbyte64;
|
||||
var pads, i, b10;
|
||||
var imax = s.length
|
||||
if (imax === 0) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (imax % 4 !== 0) {
|
||||
throw base64.makeDOMException();
|
||||
}
|
||||
|
||||
pads = 0
|
||||
if (s.charAt(imax - 1) === base64.PADCHAR) {
|
||||
pads = 1;
|
||||
if (s.charAt(imax - 2) === base64.PADCHAR) {
|
||||
pads = 2;
|
||||
}
|
||||
// either way, we want to ignore this last block
|
||||
imax -= 4;
|
||||
}
|
||||
|
||||
var x = [];
|
||||
for (i = 0; i < imax; i += 4) {
|
||||
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
|
||||
(getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
|
||||
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
|
||||
}
|
||||
|
||||
switch (pads) {
|
||||
case 1:
|
||||
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6);
|
||||
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
|
||||
break;
|
||||
case 2:
|
||||
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
|
||||
x.push(String.fromCharCode(b10 >> 16));
|
||||
break;
|
||||
}
|
||||
return x.join('');
|
||||
}
|
||||
|
||||
base64.getbyte = function(s,i) {
|
||||
var x = s.charCodeAt(i);
|
||||
if (x > 255) {
|
||||
throw base64.makeDOMException();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
base64.encode = function(s) {
|
||||
if (arguments.length !== 1) {
|
||||
throw new SyntaxError("Not enough arguments");
|
||||
}
|
||||
var padchar = base64.PADCHAR;
|
||||
var alpha = base64.ALPHA;
|
||||
var getbyte = base64.getbyte;
|
||||
|
||||
var i, b10;
|
||||
var x = [];
|
||||
|
||||
// convert to string
|
||||
s = '' + s;
|
||||
|
||||
var imax = s.length - s.length % 3;
|
||||
|
||||
if (s.length === 0) {
|
||||
return s;
|
||||
}
|
||||
for (i = 0; i < imax; i += 3) {
|
||||
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
|
||||
x.push(alpha.charAt(b10 >> 18));
|
||||
x.push(alpha.charAt((b10 >> 12) & 0x3F));
|
||||
x.push(alpha.charAt((b10 >> 6) & 0x3f));
|
||||
x.push(alpha.charAt(b10 & 0x3f));
|
||||
}
|
||||
switch (s.length - imax) {
|
||||
case 1:
|
||||
b10 = getbyte(s,i) << 16;
|
||||
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
|
||||
padchar + padchar);
|
||||
break;
|
||||
case 2:
|
||||
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
|
||||
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
|
||||
alpha.charAt((b10 >> 6) & 0x3f) + padchar);
|
||||
break;
|
||||
}
|
||||
return x.join('');
|
||||
}
|
||||
|
||||
if (!window.btoa) window.btoa = base64.encode
|
||||
if (!window.atob) window.atob = base64.decode
|
||||
161
packages/wekan-cfs-base-package/tests/common-tests.js
Normal file
161
packages/wekan-cfs-base-package/tests/common-tests.js
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
function equals(a, b) {
|
||||
return EJSON.stringify(a) === EJSON.stringify(b);
|
||||
}
|
||||
|
||||
Tinytest.add('cfs-base-package - test environment', function(test) {
|
||||
test.isTrue(typeof FS !== 'undefined',
|
||||
'FS scope not declared');
|
||||
|
||||
test.isTrue(typeof FS.Store !== 'undefined',
|
||||
'FS scope "FS.Store" not declared');
|
||||
|
||||
test.isTrue(typeof FS.AccessPoint !== 'undefined',
|
||||
'FS scope "FS.AccessPoint" not declared');
|
||||
|
||||
test.isTrue(typeof FS.Utility !== 'undefined',
|
||||
'FS scope "FS.Utility" not declared');
|
||||
|
||||
test.isTrue(typeof FS._collections !== 'undefined',
|
||||
'FS scope "FS._collections" not declared');
|
||||
|
||||
test.isTrue(typeof _Utility !== 'undefined',
|
||||
'_Utility test scope not declared');
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - _Utility.defaultZero', function(test) {
|
||||
test.equal(_Utility.defaultZero(), 0, 'Failes to return 0 when (undefined)');
|
||||
test.equal(_Utility.defaultZero(undefined), 0, 'Failes to return 0 when undefined');
|
||||
test.equal(_Utility.defaultZero(null), 0, 'Failes to return 0 when null');
|
||||
test.equal(_Utility.defaultZero(false), 0, 'Failes to return 0 when false');
|
||||
test.equal(_Utility.defaultZero(0), 0, 'Failes to return 0 when 0');
|
||||
test.equal(_Utility.defaultZero(-1), -1, 'Failes to return -1');
|
||||
test.equal(_Utility.defaultZero(1), 1, 'Failes to return 1');
|
||||
test.equal(_Utility.defaultZero(-0.1), -0.1, 'Failes to return -0.1');
|
||||
test.equal(_Utility.defaultZero(0.1), 0.1, 'Failes to return 0.1');
|
||||
test.equal(_Utility.defaultZero(''), 0, 'Failes to return ""');
|
||||
test.equal(_Utility.defaultZero({}), NaN, 'Failes to return NaN when object');
|
||||
test.equal(_Utility.defaultZero("dfdsfs"), NaN, 'Failes to return NaN when string');
|
||||
test.equal(_Utility.defaultZero("1"), 1, 'Failes to return 1 when string "1"');
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.cloneFileRecord', function(test) {
|
||||
// Given an object with any props, should filter out 'collectionName',
|
||||
// 'collection', 'data', and 'createdByTransform'
|
||||
var result = FS.Utility.cloneFileRecord({a: 1, b: {c: 1}, d: [1, 2], collectionName: 'test', collection: {}, data: {}, createdByTransform: false});
|
||||
test.equal(result, {a: 1, b: {c: 1}, d: [1, 2]});
|
||||
|
||||
// Given an FS.File instance, should filter out 'collectionName',
|
||||
// 'collection', 'data', and 'createdByTransform' and return a plain Object
|
||||
var fileObj = new FS.File({a: 1, b: {c: 1}, d: [1, 2], name: 'name.png', type: 'image/png', size: 100, collectionName: 'test', collection: {}, data: {}, createdByTransform: false});
|
||||
test.isTrue(fileObj instanceof FS.File);
|
||||
var result = FS.Utility.cloneFileRecord(fileObj);
|
||||
test.isFalse(result instanceof FS.File);
|
||||
test.isTrue(equals(result, {a: 1, b: {c: 1}, d: [1, 2], name: 'name.png', type: 'image/png', size: 100}));
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.defaultCallback', function(test) {
|
||||
// should throw an error passed in, but not a Meteor.Error
|
||||
test.throws(function () {
|
||||
var cb = FS.Utility.defaultCallback;
|
||||
cb(new Error('test'));
|
||||
});
|
||||
|
||||
var cb2 = FS.Utility.defaultCallback;
|
||||
test.isUndefined(cb2(new Meteor.Error('test')));
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.handleError', function(test) {
|
||||
test.isTrue(true);
|
||||
// TODO
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.binaryToBuffer', function(test) {
|
||||
test.isTrue(true);
|
||||
// TODO
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.bufferToBinary', function(test) {
|
||||
test.isTrue(true);
|
||||
// TODO
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.connectionLogin', function(test) {
|
||||
test.isTrue(true);
|
||||
// TODO
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.getFileName', function(test) {
|
||||
|
||||
function t(input, expected) {
|
||||
var ext = FS.Utility.getFileName(input);
|
||||
test.equal(ext, expected, 'Got incorrect filename');
|
||||
}
|
||||
|
||||
t('bar.png', 'bar.png');
|
||||
t('foo/bar.png', 'bar.png');
|
||||
t('/foo/foo/bar.png', 'bar.png');
|
||||
t('http://foobar.com/file.png', 'file.png');
|
||||
t('http://foobar.com/file', 'file');
|
||||
t('http://foobar.com/file.png?a=b', 'file.png');
|
||||
t('http://foobar.com/.file?a=b', '.file');
|
||||
t('file', 'file');
|
||||
t('.file', '.file');
|
||||
t('foo/.file', '.file');
|
||||
t('/foo/foo/.file', '.file');
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.getFileExtension', function(test) {
|
||||
|
||||
function t(input, expected) {
|
||||
var ext = FS.Utility.getFileExtension(input);
|
||||
test.equal(ext, expected, 'Got incorrect extension');
|
||||
}
|
||||
|
||||
t('bar.png', 'png');
|
||||
t('foo/bar.png', 'png');
|
||||
t('/foo/foo/bar.png', 'png');
|
||||
t('http://foobar.com/file.png', 'png');
|
||||
t('http://foobar.com/file', '');
|
||||
t('http://foobar.com/file.png?a=b', 'png');
|
||||
t('http://foobar.com/file?a=b', '');
|
||||
t('file', '');
|
||||
t('.file', '');
|
||||
t('foo/.file', '');
|
||||
t('/foo/foo/.file', '');
|
||||
});
|
||||
|
||||
Tinytest.add('cfs-base-package - FS.Utility.setFileExtension', function(test) {
|
||||
|
||||
function t(name, ext, expected) {
|
||||
var newName = FS.Utility.setFileExtension(name, ext);
|
||||
test.equal(newName, expected, 'Extension was not set correctly');
|
||||
}
|
||||
|
||||
t('bar.png', 'jpeg', 'bar.jpeg');
|
||||
t('bar', 'jpeg', 'bar.jpeg');
|
||||
t('.bar', 'jpeg', '.bar.jpeg');
|
||||
t('', 'jpeg', '');
|
||||
t(null, 'jpeg', null);
|
||||
});
|
||||
|
||||
//Test API:
|
||||
//Tinytest.add('', function(test) {});
|
||||
//Tinytest.addAsync('', function(test, onComplete) {});
|
||||
//test.isFalse(v, msg)
|
||||
//test.isTrue(v, msg)
|
||||
//test.equalactual, expected, message, not
|
||||
//test.length(obj, len)
|
||||
//test.include(s, v)
|
||||
//test.isNaN(v, msg)
|
||||
//test.isUndefined(v, msg)
|
||||
//test.isNotNull
|
||||
//test.isNull
|
||||
//test.throws(func)
|
||||
//test.instanceOf(obj, klass)
|
||||
//test.notEqual(actual, expected, message)
|
||||
//test.runId()
|
||||
//test.exception(exception)
|
||||
//test.expect_fail()
|
||||
//test.ok(doc)
|
||||
//test.fail(doc)
|
||||
//test.equal(a, b, msg)
|
||||
Loading…
Add table
Add a link
Reference in a new issue