summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2015-11-23 09:38:01 +0100
committerThomas Müller <thomas.mueller@tmit.eu>2015-11-23 09:38:01 +0100
commit79bbda994bb8dd2231f68f57785237f79f86f6c7 (patch)
tree65585aed0d21cc679cdf7e2509efd6fa3d33b458 /core
parent2f89eef334bd445a7e046d845d5d5d1b3e4b6b8c (diff)
parent418fefc93c3332c77ec617ef108138efb6a34544 (diff)
downloadnextcloud-server-79bbda994bb8dd2231f68f57785237f79f86f6c7.tar.gz
nextcloud-server-79bbda994bb8dd2231f68f57785237f79f86f6c7.zip
Merge pull request #16902 from owncloud/jsocclient
Web UI uses Webdav instead of ajax/* calls
Diffstat (limited to 'core')
-rw-r--r--core/js/core.json8
-rw-r--r--core/js/files/client.js691
-rw-r--r--core/js/files/fileinfo.js138
-rw-r--r--core/js/files/iedavclient.js169
-rw-r--r--core/js/js.js1
-rw-r--r--core/js/oc-dialogs.js2
-rw-r--r--core/js/tests/specHelper.js1
-rw-r--r--core/js/tests/specs/files/clientSpec.js711
-rw-r--r--core/vendor/.gitignore11
-rw-r--r--core/vendor/davclient.js/LICENSE27
-rw-r--r--core/vendor/davclient.js/lib/client.js296
-rw-r--r--core/vendor/es6-promise/.bower.json40
-rw-r--r--core/vendor/es6-promise/.npmignore11
-rw-r--r--core/vendor/es6-promise/.release.json17
-rw-r--r--core/vendor/es6-promise/.spmignore11
-rw-r--r--core/vendor/es6-promise/LICENSE19
-rw-r--r--core/vendor/es6-promise/dist/es6-promise.js972
17 files changed, 3121 insertions, 4 deletions
diff --git a/core/js/core.json b/core/js/core.json
index a80636e8463..c7621a08d62 100644
--- a/core/js/core.json
+++ b/core/js/core.json
@@ -8,7 +8,9 @@
"handlebars/handlebars.js",
"blueimp-md5/js/md5.js",
"bootstrap/js/tooltip.js",
- "backbone/backbone.js"
+ "backbone/backbone.js",
+ "es6-promise/dist/es6-promise.js",
+ "davclient.js/lib/client.js"
],
"libraries": [
"jquery-showpassword.js",
@@ -39,6 +41,8 @@
"setupchecks.js",
"../search/js/search.js",
"mimetype.js",
- "mimetypelist.js"
+ "mimetypelist.js",
+ "files/fileinfo.js",
+ "files/client.js"
]
}
diff --git a/core/js/files/client.js b/core/js/files/client.js
new file mode 100644
index 00000000000..82cf3ff5121
--- /dev/null
+++ b/core/js/files/client.js
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+/* global dav */
+
+(function(OC, FileInfo) {
+ /**
+ * @class OC.Files.Client
+ * @classdesc Client to access files on the server
+ *
+ * @param {Object} options
+ * @param {String} options.host host name
+ * @param {int} [options.port] port
+ * @param {boolean} [options.useHTTPS] whether to use https
+ * @param {String} [options.root] root path
+ * @param {String} [options.userName] user name
+ * @param {String} [options.password] password
+ *
+ * @since 8.2
+ */
+ var Client = function(options) {
+ this._root = options.root;
+ if (this._root.charAt(this._root.length - 1) === '/') {
+ this._root = this._root.substr(0, this._root.length - 1);
+ }
+
+ var url = 'http://';
+ if (options.useHTTPS) {
+ url = 'https://';
+ }
+ var credentials = '';
+ if (options.userName) {
+ credentials += encodeURIComponent(options.userName);
+ }
+ if (options.password) {
+ credentials += ':' + encodeURIComponent(options.password);
+ }
+ if (credentials.length > 0) {
+ url += credentials + '@';
+ }
+
+ url += options.host + this._root;
+ this._defaultHeaders = options.defaultHeaders || {'X-Requested-With': 'XMLHttpRequest'};
+ this._baseUrl = url;
+ this._client = new dav.Client({
+ baseUrl: this._baseUrl,
+ xmlNamespaces: {
+ 'DAV:': 'd',
+ 'http://owncloud.org/ns': 'oc'
+ }
+ });
+ this._client.xhrProvider = _.bind(this._xhrProvider, this);
+ };
+
+ Client.NS_OWNCLOUD = 'http://owncloud.org/ns';
+ Client.NS_DAV = 'DAV:';
+ Client._PROPFIND_PROPERTIES = [
+ /**
+ * Modified time
+ */
+ [Client.NS_DAV, 'getlastmodified'],
+ /**
+ * Etag
+ */
+ [Client.NS_DAV, 'getetag'],
+ /**
+ * Mime type
+ */
+ [Client.NS_DAV, 'getcontenttype'],
+ /**
+ * Resource type "collection" for folders, empty otherwise
+ */
+ [Client.NS_DAV, 'resourcetype'],
+ /**
+ * File id
+ */
+ [Client.NS_OWNCLOUD, 'fileid'],
+ /**
+ * Letter-coded permissions
+ */
+ [Client.NS_OWNCLOUD, 'permissions'],
+ //[Client.NS_OWNCLOUD, 'downloadURL'],
+ /**
+ * Folder sizes
+ */
+ [Client.NS_OWNCLOUD, 'size'],
+ /**
+ * File sizes
+ */
+ [Client.NS_DAV, 'getcontentlength']
+ ];
+
+ /**
+ * @memberof OC.Files
+ */
+ Client.prototype = {
+
+ /**
+ * Root path of the Webdav endpoint
+ *
+ * @type string
+ */
+ _root: null,
+
+ /**
+ * Client from the library
+ *
+ * @type dav.Client
+ */
+ _client: null,
+
+ /**
+ * Array of file info parsing functions.
+ *
+ * @type Array<OC.Files.Client~parseFileInfo>
+ */
+ _fileInfoParsers: [],
+
+ /**
+ * Returns the configured XHR provider for davclient
+ * @return {XMLHttpRequest}
+ */
+ _xhrProvider: function() {
+ var headers = this._defaultHeaders;
+ var xhr = new XMLHttpRequest();
+ var oldOpen = xhr.open;
+ // override open() method to add headers
+ xhr.open = function() {
+ var result = oldOpen.apply(this, arguments);
+ _.each(headers, function(value, key) {
+ xhr.setRequestHeader(key, value);
+ });
+ return result;
+ };
+ return xhr;
+ },
+
+ /**
+ * Prepends the base url to the given path sections
+ *
+ * @param {...String} path sections
+ *
+ * @return {String} base url + joined path, any leading or trailing slash
+ * will be kept
+ */
+ _buildUrl: function() {
+ var path = this._buildPath.apply(this, arguments);
+ if (path.charAt([path.length - 1]) === '/') {
+ path = path.substr(0, path.length - 1);
+ }
+ if (path.charAt(0) === '/') {
+ path = path.substr(1);
+ }
+ return this._baseUrl + '/' + path;
+ },
+
+ /**
+ * Append the path to the root and also encode path
+ * sections
+ *
+ * @param {...String} path sections
+ *
+ * @return {String} joined path, any leading or trailing slash
+ * will be kept
+ */
+ _buildPath: function() {
+ var path = OC.joinPaths.apply(this, arguments);
+ var sections = path.split('/');
+ var i;
+ for (i = 0; i < sections.length; i++) {
+ sections[i] = encodeURIComponent(sections[i]);
+ }
+ path = sections.join('/');
+ return path;
+ },
+
+ /**
+ * Parse headers string into a map
+ *
+ * @param {string} headersString headers list as string
+ *
+ * @return {Object.<String,Array>} map of header name to header contents
+ */
+ _parseHeaders: function(headersString) {
+ var headerRows = headersString.split('\n');
+ var headers = {};
+ for (var i = 0; i < headerRows.length; i++) {
+ var sepPos = headerRows[i].indexOf(':');
+ if (sepPos < 0) {
+ continue;
+ }
+
+ var headerName = headerRows[i].substr(0, sepPos);
+ var headerValue = headerRows[i].substr(sepPos + 2);
+
+ if (!headers[headerName]) {
+ // make it an array
+ headers[headerName] = [];
+ }
+
+ headers[headerName].push(headerValue);
+ }
+ return headers;
+ },
+
+ /**
+ * Parses the etag response which is in double quotes.
+ *
+ * @param {string} etag etag value in double quotes
+ *
+ * @return {string} etag without double quotes
+ */
+ _parseEtag: function(etag) {
+ if (etag.charAt(0) === '"') {
+ return etag.split('"')[1];
+ }
+ return etag;
+ },
+
+ /**
+ * Parse Webdav result
+ *
+ * @param {Object} response XML object
+ *
+ * @return {Array.<FileInfo>} array of file info
+ */
+ _parseFileInfo: function(response) {
+ var path = response.href;
+ if (path.substr(0, this._root.length) === this._root) {
+ path = path.substr(this._root.length);
+ }
+
+ if (path.charAt(path.length - 1) === '/') {
+ path = path.substr(0, path.length - 1);
+ }
+
+ path = '/' + decodeURIComponent(path);
+
+ if (response.propStat.length === 1 && response.propStat[0].status !== 200) {
+ return null;
+ }
+
+ var props = response.propStat[0].properties;
+
+ var data = {
+ id: props['{' + Client.NS_OWNCLOUD + '}fileid'],
+ path: OC.dirname(path) || '/',
+ name: OC.basename(path),
+ mtime: new Date(props['{' + Client.NS_DAV + '}getlastmodified'])
+ };
+
+ var etagProp = props['{' + Client.NS_DAV + '}getetag'];
+ if (!_.isUndefined(etagProp)) {
+ data.etag = this._parseEtag(etagProp);
+ }
+
+ var sizeProp = props['{' + Client.NS_DAV + '}getcontentlength'];
+ if (!_.isUndefined(sizeProp)) {
+ data.size = parseInt(sizeProp, 10);
+ }
+
+ sizeProp = props['{' + Client.NS_OWNCLOUD + '}size'];
+ if (!_.isUndefined(sizeProp)) {
+ data.size = parseInt(sizeProp, 10);
+ }
+
+ var contentType = props['{' + Client.NS_DAV + '}getcontenttype'];
+ if (!_.isUndefined(contentType)) {
+ data.mimetype = contentType;
+ }
+
+ var resType = props['{' + Client.NS_DAV + '}resourcetype'];
+ var isFile = true;
+ if (!data.mimetype && resType) {
+ var xmlvalue = resType[0];
+ if (xmlvalue.namespaceURI === Client.NS_DAV && xmlvalue.nodeName.split(':')[1] === 'collection') {
+ data.mimetype = 'httpd/unix-directory';
+ isFile = false;
+ }
+ }
+
+ data.permissions = OC.PERMISSION_READ;
+ var permissionProp = props['{' + Client.NS_OWNCLOUD + '}permissions'];
+ if (!_.isUndefined(permissionProp)) {
+ var permString = permissionProp || '';
+ data.mountType = null;
+ for (var i = 0; i < permString.length; i++) {
+ var c = permString.charAt(i);
+ switch (c) {
+ // FIXME: twisted permissions
+ case 'C':
+ case 'K':
+ data.permissions |= OC.PERMISSION_CREATE;
+ if (!isFile) {
+ data.permissions |= OC.PERMISSION_UPDATE;
+ }
+ break;
+ case 'W':
+ if (isFile) {
+ // also add create permissions
+ data.permissions |= OC.PERMISSION_CREATE;
+ }
+ data.permissions |= OC.PERMISSION_UPDATE;
+ break;
+ case 'D':
+ data.permissions |= OC.PERMISSION_DELETE;
+ break;
+ case 'R':
+ data.permissions |= OC.PERMISSION_SHARE;
+ break;
+ case 'M':
+ if (!data.mountType) {
+ // TODO: how to identify external-root ?
+ data.mountType = 'external';
+ }
+ break;
+ case 'S':
+ // TODO: how to identify shared-root ?
+ data.mountType = 'shared';
+ break;
+ }
+ }
+ }
+
+ // extend the parsed data using the custom parsers
+ _.each(this._fileInfoParsers, function(parserFunction) {
+ _.extend(data, parserFunction(response) || {});
+ });
+
+ return new FileInfo(data);
+ },
+
+ /**
+ * Parse Webdav multistatus
+ *
+ * @param {Array} responses
+ */
+ _parseResult: function(responses) {
+ var self = this;
+ return _.map(responses, function(response) {
+ return self._parseFileInfo(response);
+ });
+ },
+
+ /**
+ * Returns whether the given status code means success
+ *
+ * @param {int} status status code
+ *
+ * @return true if status code is between 200 and 299 included
+ */
+ _isSuccessStatus: function(status) {
+ return status >= 200 && status <= 299;
+ },
+
+ /**
+ * Returns the default PROPFIND properties to use during a call.
+ *
+ * @return {Array.<Object>} array of properties
+ */
+ getPropfindProperties: function() {
+ if (!this._propfindProperties) {
+ this._propfindProperties = _.map(Client._PROPFIND_PROPERTIES, function(propDef) {
+ return '{' + propDef[0] + '}' + propDef[1];
+ });
+ }
+ return this._propfindProperties;
+ },
+
+ /**
+ * Lists the contents of a directory
+ *
+ * @param {String} path path to retrieve
+ * @param {Object} [options] options
+ * @param {boolean} [options.includeParent=false] set to true to keep
+ * the parent folder in the result list
+ * @param {Array} [options.properties] list of Webdav properties to retrieve
+ *
+ * @return {Promise} promise
+ */
+ getFolderContents: function(path, options) {
+ if (!path) {
+ path = '';
+ }
+ options = options || {};
+ var self = this;
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+ var properties;
+ if (_.isUndefined(options.properties)) {
+ properties = this.getPropfindProperties();
+ } else {
+ properties = options.properties;
+ }
+
+ // TODO: headers
+ this._client.propFind(
+ this._buildUrl(path),
+ properties,
+ 1
+ ).then(function(result) {
+ if (self._isSuccessStatus(result.status)) {
+ var results = self._parseResult(result.body);
+ if (!options || !options.includeParent) {
+ // remove root dir, the first entry
+ results.shift();
+ }
+ deferred.resolve(result.status, results);
+ } else {
+ deferred.reject(result.status);
+ }
+ });
+ return promise;
+ },
+
+ /**
+ * Returns the file info of a given path.
+ *
+ * @param {String} path path
+ * @param {Array} [options.properties] list of Webdav properties to retrieve
+ *
+ * @return {Promise} promise
+ */
+ getFileInfo: function(path, options) {
+ if (!path) {
+ path = '';
+ }
+ options = options || {};
+ var self = this;
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+ var properties;
+ if (_.isUndefined(options.properties)) {
+ properties = this.getPropfindProperties();
+ } else {
+ properties = options.properties;
+ }
+
+ // TODO: headers
+ this._client.propFind(
+ this._buildUrl(path),
+ properties,
+ 0
+ ).then(
+ function(result) {
+ if (self._isSuccessStatus(result.status)) {
+ deferred.resolve(result.status, self._parseResult([result.body])[0]);
+ } else {
+ deferred.reject(result.status);
+ }
+ }
+ );
+ return promise;
+ },
+
+ /**
+ * Returns the contents of the given file.
+ *
+ * @param {String} path path to file
+ *
+ * @return {Promise}
+ */
+ getFileContents: function(path) {
+ if (!path) {
+ throw 'Missing argument "path"';
+ }
+ var self = this;
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+
+ this._client.request(
+ 'GET',
+ this._buildUrl(path),
+ this._defaultHeaders
+ ).then(
+ function(result) {
+ if (self._isSuccessStatus(result.status)) {
+ deferred.resolve(result.status, result.body);
+ } else {
+ deferred.reject(result.status);
+ }
+ }
+ );
+ return promise;
+ },
+
+ /**
+ * Puts the given data into the given file.
+ *
+ * @param {String} path path to file
+ * @param {String} body file body
+ * @param {Object} [options]
+ * @param {String} [options.contentType='text/plain'] content type
+ * @param {bool} [options.overwrite=true] whether to overwrite an existing file
+ *
+ * @return {Promise}
+ */
+ putFileContents: function(path, body, options) {
+ if (!path) {
+ throw 'Missing argument "path"';
+ }
+ var self = this;
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+ options = options || {};
+ var headers = _.extend({}, this._defaultHeaders);
+ var contentType = 'text/plain';
+ if (options.contentType) {
+ contentType = options.contentType;
+ }
+
+ headers['Content-Type'] = contentType;
+
+ if (_.isUndefined(options.overwrite) || options.overwrite) {
+ // will trigger 412 precondition failed if a file already exists
+ headers['If-None-Match'] = '*';
+ }
+
+ this._client.request(
+ 'PUT',
+ this._buildUrl(path),
+ headers,
+ body || ''
+ ).then(
+ function(result) {
+ if (self._isSuccessStatus(result.status)) {
+ deferred.resolve(result.status);
+ } else {
+ deferred.reject(result.status);
+ }
+ }
+ );
+ return promise;
+ },
+
+ _simpleCall: function(method, path) {
+ if (!path) {
+ throw 'Missing argument "path"';
+ }
+
+ var self = this;
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+
+ this._client.request(
+ method,
+ this._buildUrl(path),
+ this._defaultHeaders
+ ).then(
+ function(result) {
+ if (self._isSuccessStatus(result.status)) {
+ deferred.resolve(result.status);
+ } else {
+ deferred.reject(result.status);
+ }
+ }
+ );
+ return promise;
+ },
+
+ /**
+ * Creates a directory
+ *
+ * @param {String} path path to create
+ *
+ * @return {Promise}
+ */
+ createDirectory: function(path) {
+ return this._simpleCall('MKCOL', path);
+ },
+
+ /**
+ * Deletes a file or directory
+ *
+ * @param {String} path path to delete
+ *
+ * @return {Promise}
+ */
+ remove: function(path) {
+ return this._simpleCall('DELETE', path);
+ },
+
+ /**
+ * Moves path to another path
+ *
+ * @param {String} path path to move
+ * @param {String} destinationPath destination path
+ * @param {boolean} [allowOverwrite=false] true to allow overwriting,
+ * false otherwise
+ *
+ * @return {Promise} promise
+ */
+ move: function(path, destinationPath, allowOverwrite) {
+ if (!path) {
+ throw 'Missing argument "path"';
+ }
+ if (!destinationPath) {
+ throw 'Missing argument "destinationPath"';
+ }
+
+ var self = this;
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+ var headers =
+ _.extend({
+ 'Destination' : this._buildUrl(destinationPath)
+ }, this._defaultHeaders);
+
+ if (!allowOverwrite) {
+ headers['Overwrite'] = 'F';
+ }
+
+ this._client.request(
+ 'MOVE',
+ this._buildUrl(path),
+ headers
+ ).then(
+ function(response) {
+ if (self._isSuccessStatus(response.status)) {
+ deferred.resolve(response.status);
+ } else {
+ deferred.reject(response.status);
+ }
+ }
+ );
+ return promise;
+ },
+
+ /**
+ * Add a file info parser function
+ *
+ * @param {OC.Files.Client~parseFileInfo>}
+ */
+ addFileInfoParser: function(parserFunction) {
+ this._fileInfoParsers.push(parserFunction);
+ }
+
+ };
+
+ /**
+ * File info parser function
+ *
+ * This function receives a list of Webdav properties as input and
+ * should return a hash array of parsed properties, if applicable.
+ *
+ * @callback OC.Files.Client~parseFileInfo
+ * @param {Object} XML Webdav properties
+ * @return {Array} array of parsed property values
+ */
+
+ if (!OC.Files) {
+ /**
+ * @namespace OC.Files
+ *
+ * @since 8.2
+ */
+ OC.Files = {};
+ }
+
+ /**
+ * Returns the default instance of the files client
+ *
+ * @return {OC.Files.Client} default client
+ *
+ * @since 8.2
+ */
+ OC.Files.getClient = function() {
+ if (OC.Files._defaultClient) {
+ return OC.Files._defaultClient;
+ }
+
+ var client = new OC.Files.Client({
+ host: OC.getHost(),
+ port: OC.getPort(),
+ root: OC.linkToRemoteBase('webdav'),
+ useHTTPS: OC.getProtocol() === 'https'
+ });
+ OC.Files._defaultClient = client;
+ return client;
+ };
+
+ OC.Files.Client = Client;
+})(OC, OC.Files.FileInfo);
+
diff --git a/core/js/files/fileinfo.js b/core/js/files/fileinfo.js
new file mode 100644
index 00000000000..3bf68d88b15
--- /dev/null
+++ b/core/js/files/fileinfo.js
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function(OC) {
+
+ /**
+ * @class OC.Files.FileInfo
+ * @classdesc File information
+ *
+ * @param {Object} data file data, see attributes for details
+ *
+ * @since 8.2
+ */
+ var FileInfo = function(data) {
+ var self = this;
+ _.each(data, function(value, key) {
+ if (!_.isFunction(value)) {
+ self[key] = value;
+ }
+ });
+
+ if (!_.isUndefined(this.id)) {
+ this.id = parseInt(data.id, 10);
+ }
+
+ // TODO: normalize path
+ this.path = data.path || '';
+
+ if (this.type === 'dir') {
+ this.mimetype = 'httpd/unix-directory';
+ } else {
+ this.mimetype = this.mimetype || 'application/octet-stream';
+ }
+
+ if (!this.type) {
+ if (this.mimetype === 'httpd/unix-directory') {
+ this.type = 'dir';
+ } else {
+ this.type = 'file';
+ }
+ }
+ };
+
+ /**
+ * @memberof OC.Files
+ */
+ FileInfo.prototype = {
+ /**
+ * File id
+ *
+ * @type int
+ */
+ id: null,
+
+ /**
+ * File name
+ *
+ * @type String
+ */
+ name: null,
+
+ /**
+ * Path leading to the file, without the file name,
+ * and with a leading slash.
+ *
+ * @type String
+ */
+ path: null,
+
+ /**
+ * Mime type
+ *
+ * @type String
+ */
+ mimetype: null,
+
+ /**
+ * Icon URL.
+ *
+ * Can be used to override the mime type icon.
+ *
+ * @type String
+ */
+ icon: null,
+
+ /**
+ * File type. 'file' for files, 'dir' for directories.
+ *
+ * @type String
+ * @deprecated rely on mimetype instead
+ */
+ type: null,
+
+ /**
+ * Permissions.
+ *
+ * @see OC#PERMISSION_ALL for permissions
+ * @type int
+ */
+ permissions: null,
+
+ /**
+ * Modification time
+ *
+ * @type int
+ */
+ mtime: null,
+
+ /**
+ * Etag
+ *
+ * @type String
+ */
+ etag: null,
+
+ /**
+ * Mount type.
+ *
+ * One of null, "external-root", "shared" or "shared-root"
+ *
+ * @type string
+ */
+ mountType: null
+ };
+
+ if (!OC.Files) {
+ OC.Files = {};
+ }
+ OC.Files.FileInfo = FileInfo;
+})(OC);
+
diff --git a/core/js/files/iedavclient.js b/core/js/files/iedavclient.js
new file mode 100644
index 00000000000..bc6bce2f9ae
--- /dev/null
+++ b/core/js/files/iedavclient.js
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+/* global dav */
+(function(dav) {
+
+ /**
+ * Override davclient.js methods with IE-compatible logic
+ */
+ dav.Client.prototype = _.extend({}, dav.Client.prototype, {
+
+ /**
+ * Generates a propFind request.
+ *
+ * @param {string} url Url to do the propfind request on
+ * @param {Array} properties List of properties to retrieve.
+ * @return {Promise}
+ */
+ propFind : function(url, properties, depth) {
+
+ if(typeof depth == "undefined") {
+ depth = 0;
+ }
+
+ var headers = {
+ Depth : depth,
+ 'Content-Type' : 'application/xml; charset=utf-8'
+ };
+
+ var body =
+ '<?xml version="1.0"?>\n' +
+ '<d:propfind ';
+
+ var namespace;
+ for (namespace in this.xmlNamespaces) {
+ body += ' xmlns:' + this.xmlNamespaces[namespace] + '="' + namespace + '"';
+ }
+ body += '>\n' +
+ ' <d:prop>\n';
+
+ for(var ii in properties) {
+ var propText = properties[ii];
+ if (typeof propText !== 'string') {
+ // can happen on IE8
+ continue;
+ }
+ var property = this.parseClarkNotation(properties[ii]);
+ if (this.xmlNamespaces[property.namespace]) {
+ body+=' <' + this.xmlNamespaces[property.namespace] + ':' + property.name + ' />\n';
+ } else {
+ body+=' <x:' + property.name + ' xmlns:x="' + property.namespace + '" />\n';
+ }
+
+ }
+ body+=' </d:prop>\n';
+ body+='</d:propfind>';
+
+ return this.request('PROPFIND', url, headers, body).then(
+ function(result) {
+ var elements = this.parseMultiStatus(result.xhr.responseXML);
+ var response;
+ if (depth===0) {
+ response = {
+ status: result.status,
+ body: elements[0]
+ };
+ } else {
+ response = {
+ status: result.status,
+ body: elements
+ };
+ }
+ return response;
+
+ }.bind(this)
+ );
+
+ },
+
+
+ _getElementsByTagName: function(node, name, resolver) {
+ var parts = name.split(':');
+ var tagName = parts[1];
+ var namespace = resolver(parts[0]);
+ if (node.getElementsByTagNameNS) {
+ return node.getElementsByTagNameNS(namespace, tagName);
+ }
+ return node.getElementsByTagName(name);
+ },
+
+ /**
+ * Parses a multi-status response body.
+ *
+ * @param {string} xmlBody
+ * @param {Array}
+ */
+ parseMultiStatus : function(doc) {
+
+ var result = [];
+ var resolver = function(foo) {
+ var ii;
+ for(ii in this.xmlNamespaces) {
+ if (this.xmlNamespaces[ii] === foo) {
+ return ii;
+ }
+ }
+ }.bind(this);
+
+ var responses = this._getElementsByTagName(doc, 'd:response', resolver);
+ var i;
+ for (i = 0; i < responses.length; i++) {
+ var responseNode = responses[i];
+ var response = {
+ href : null,
+ propStat : []
+ };
+
+ var hrefNode = this._getElementsByTagName(responseNode, 'd:href', resolver)[0];
+
+ response.href = hrefNode.textContent || hrefNode.text;
+
+ var propStatNodes = this._getElementsByTagName(responseNode, 'd:propstat', resolver);
+ var j = 0;
+
+ for (j = 0; j < propStatNodes.length; j++) {
+ var propStatNode = propStatNodes[j];
+ var statusNode = this._getElementsByTagName(propStatNode, 'd:status', resolver)[0];
+
+ var propStat = {
+ status : statusNode.textContent || statusNode.text,
+ properties : []
+ };
+
+ var propNode = this._getElementsByTagName(propStatNode, 'd:prop', resolver)[0];
+ if (!propNode) {
+ continue;
+ }
+ var k = 0;
+ for (k = 0; k < propNode.childNodes.length; k++) {
+ var prop = propNode.childNodes[k];
+ var value = prop.textContent || prop.text;
+ if (prop.childNodes && prop.childNodes.length > 0 && prop.childNodes[0].nodeType === 1) {
+ value = prop.childNodes;
+ }
+ propStat.properties['{' + prop.namespaceURI + '}' + (prop.localName || prop.baseName)] = value;
+
+ }
+ response.propStat.push(propStat);
+ }
+
+ result.push(response);
+ }
+
+ return result;
+
+ }
+
+
+ });
+
+})(dav);
+
diff --git a/core/js/js.js b/core/js/js.js
index 57c9871233b..ce552bb8ea2 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1428,7 +1428,6 @@ function initCore() {
$('body').delegate('#app-content', 'apprendered appresized', adjustControlsWidth);
}
-
}
$(document).ready(initCore);
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index 4448b813021..fe93d0ea657 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -759,7 +759,7 @@ var OCdialogs = {
filename: entry.name,
date: OC.Util.relativeModifiedDate(entry.mtime)
});
- if (entry.isPreviewAvailable) {
+ if (entry.type === 'file') {
var urlSpec = {
file: dir + '/' + entry.name
};
diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js
index cd387d76ce8..f09a7054c9f 100644
--- a/core/js/tests/specHelper.js
+++ b/core/js/tests/specHelper.js
@@ -86,6 +86,7 @@ window.firstDay = 0;
// setup dummy webroots
/* jshint camelcase: false */
window.oc_debug = true;
+// FIXME: oc_webroot is supposed to be only the path!!!
window.oc_webroot = location.href + '/';
window.oc_appswebroots = {
"files": window.oc_webroot + '/apps/files/'
diff --git a/core/js/tests/specs/files/clientSpec.js b/core/js/tests/specs/files/clientSpec.js
new file mode 100644
index 00000000000..3a3181d8426
--- /dev/null
+++ b/core/js/tests/specs/files/clientSpec.js
@@ -0,0 +1,711 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2015 Vincent Petry <pvince81@owncloud.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+describe('OC.Files.Client tests', function() {
+ var Client = OC.Files.Client;
+ var baseUrl;
+ var client;
+
+ beforeEach(function() {
+ baseUrl = 'https://testhost/owncloud/remote.php/webdav/';
+
+ client = new Client({
+ host: 'testhost',
+ root: '/owncloud/remote.php/webdav',
+ useHTTPS: true
+ });
+ });
+ afterEach(function() {
+ client = null;
+ });
+
+ /**
+ * Send an status response and check that the given
+ * promise gets its success handler called with the error
+ * status code
+ *
+ * @param {Promise} promise promise
+ * @param {int} status status to test
+ */
+ function respondAndCheckStatus(promise, status) {
+ var successHandler = sinon.stub();
+ var failHandler = sinon.stub();
+ promise.done(successHandler);
+ promise.fail(failHandler);
+
+ fakeServer.requests[0].respond(
+ status,
+ {'Content-Type': 'application/xml'},
+ ''
+ );
+
+ promise.then(function() {
+ expect(successHandler.calledOnce).toEqual(true);
+ expect(successHandler.getCall(0).args[0]).toEqual(status);
+
+ expect(failHandler.notCalled).toEqual(true);
+ });
+
+ return promise;
+ }
+
+ /**
+ * Send an error response and check that the given
+ * promise gets its fail handler called with the error
+ * status code
+ *
+ * @param {Promise} promise promise object
+ * @param {int} status error status to test
+ */
+ function respondAndCheckError(promise, status) {
+ var successHandler = sinon.stub();
+ var failHandler = sinon.stub();
+ promise.done(successHandler);
+ promise.fail(failHandler);
+
+ fakeServer.requests[0].respond(
+ status,
+ {'Content-Type': 'application/xml'},
+ ''
+ );
+
+ promise.then(function() {
+ expect(failHandler.calledOnce).toEqual(true);
+ expect(failHandler.calledWith(status)).toEqual(true);
+
+ expect(successHandler.notCalled).toEqual(true);
+
+ fulfill();
+ });
+
+ return promise;
+ }
+
+ /**
+ * Returns a list of request properties parsed from the given request body.
+ *
+ * @param {string} requestBody request XML
+ *
+ * @return {Array.<String>} array of request properties in the format
+ * "{NS:}propname"
+ */
+ function getRequestedProperties(requestBody) {
+ var doc = (new window.DOMParser()).parseFromString(
+ requestBody,
+ 'application/xml'
+ );
+ var propRoots = doc.getElementsByTagNameNS('DAV:', 'prop');
+ var propsList = propRoots.item(0).childNodes;
+ return _.map(propsList, function(propNode) {
+ return '{' + propNode.namespaceURI + '}' + propNode.localName;
+ });
+ }
+
+ function makePropBlock(props) {
+ var s = '<d:prop>\n';
+
+ _.each(props, function(value, key) {
+ s += '<' + key + '>' + value + '</' + key + '>\n';
+ });
+
+ return s + '</d:prop>\n';
+ }
+
+ function makeResponseBlock(href, props, failedProps) {
+ var s = '<d:response>\n';
+ s += '<d:href>' + href + '</d:href>\n';
+ s += '<d:propstat>\n';
+ s += makePropBlock(props);
+ s += '<d:status>HTTP/1.1 200 OK</d:status>';
+ s += '</d:propstat>\n';
+ if (failedProps) {
+ s += '<d:propstat>\n';
+ _.each(failedProps, function(prop) {
+ s += '<' + prop + '/>\n';
+ });
+ s += '<d:status>HTTP/1.1 404 Not Found</d:status>\n';
+ s += '</d:propstat>\n';
+ }
+ return s + '</d:response>\n';
+ }
+
+ describe('file listing', function() {
+
+ var folderContentsXml =
+ '<?xml version="1.0" encoding="utf-8"?>' +
+ '<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
+ makeResponseBlock(
+ '/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/',
+ {
+ 'd:getlastmodified': 'Fri, 10 Jul 2015 10:00:05 GMT',
+ 'd:getetag': '"56cfcabd79abb"',
+ 'd:resourcetype': '<d:collection/>',
+ 'oc:id': '00000011oc2d13a6a068',
+ 'oc:permissions': 'RDNVCK',
+ 'oc:size': 120
+ },
+ [
+ 'd:getcontenttype',
+ 'd:getcontentlength'
+ ]
+ ) +
+ makeResponseBlock(
+ '/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt',
+ {
+ 'd:getlastmodified': 'Fri, 10 Jul 2015 13:38:05 GMT',
+ 'd:getetag': '"559fcabd79a38"',
+ 'd:getcontenttype': 'text/plain',
+ 'd:getcontentlength': 250,
+ 'd:resourcetype': '',
+ 'oc:id': '00000051oc2d13a6a068',
+ 'oc:permissions': 'RDNVW'
+ },
+ [
+ 'oc:size',
+ ]
+ ) +
+ makeResponseBlock(
+ '/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/sub',
+ {
+ 'd:getlastmodified': 'Fri, 10 Jul 2015 14:00:00 GMT',
+ 'd:getetag': '"66cfcabd79abb"',
+ 'd:resourcetype': '<d:collection/>',
+ 'oc:id': '00000015oc2d13a6a068',
+ 'oc:permissions': 'RDNVCK',
+ 'oc:size': 100
+ },
+ [
+ 'd:getcontenttype',
+ 'd:getcontentlength'
+ ]
+ ) +
+ '</d:multistatus>';
+
+ it('sends PROPFIND with explicit properties to get file list', function() {
+ client.getFolderContents('path/to space/文件夹');
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('PROPFIND');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
+ expect(fakeServer.requests[0].requestHeaders.Depth).toEqual(1);
+
+ var props = getRequestedProperties(fakeServer.requests[0].requestBody);
+ expect(props).toContain('{DAV:}getlastmodified');
+ expect(props).toContain('{DAV:}getcontentlength');
+ expect(props).toContain('{DAV:}getcontenttype');
+ expect(props).toContain('{DAV:}getetag');
+ expect(props).toContain('{DAV:}resourcetype');
+ expect(props).toContain('{http://owncloud.org/ns}fileid');
+ expect(props).toContain('{http://owncloud.org/ns}size');
+ expect(props).toContain('{http://owncloud.org/ns}permissions');
+ });
+ it('sends PROPFIND to base url when empty path given', function() {
+ client.getFolderContents('');
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].url).toEqual(baseUrl);
+ });
+ it('sends PROPFIND to base url when root path given', function() {
+ client.getFolderContents('/');
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].url).toEqual(baseUrl);
+ });
+ it('parses the result list into a FileInfo array', function() {
+ var promise = client.getFolderContents('path/to space/文件夹');
+
+ expect(fakeServer.requests.length).toEqual(1);
+
+ fakeServer.requests[0].respond(
+ 207,
+ {'Content-Type': 'application/xml'},
+ folderContentsXml
+ );
+
+ promise.then(function(status, response) {
+ expect(status).toEqual(207);
+ expect(_.isArray(response)).toEqual(true);
+
+ expect(response.length).toEqual(2);
+
+ // file entry
+ var info = response[0];
+ expect(info instanceof OC.Files.FileInfo).toEqual(true);
+ expect(info.id).toEqual(51);
+ expect(info.path).toEqual('/path/to space/文件夹');
+ expect(info.name).toEqual('One.txt');
+ expect(info.permissions).toEqual(31);
+ expect(info.size).toEqual(250);
+ expect(info.mtime.getTime()).toEqual(1436535485000);
+ expect(info.mimetype).toEqual('text/plain');
+ expect(info.etag).toEqual('559fcabd79a38');
+
+ // sub entry
+ info = response[1];
+ expect(info instanceof OC.Files.FileInfo).toEqual(true);
+ expect(info.id).toEqual(15);
+ expect(info.path).toEqual('/path/to space/文件夹');
+ expect(info.name).toEqual('sub');
+ expect(info.permissions).toEqual(31);
+ expect(info.size).toEqual(100);
+ expect(info.mtime.getTime()).toEqual(1436536800000);
+ expect(info.mimetype).toEqual('httpd/unix-directory');
+ expect(info.etag).toEqual('66cfcabd79abb');
+ });
+ return promise.promise();
+ });
+ it('returns parent node in result if specified', function() {
+ var promise = client.getFolderContents('path/to space/文件夹', {includeParent: true});
+
+ expect(fakeServer.requests.length).toEqual(1);
+
+ fakeServer.requests[0].respond(
+ 207,
+ {'Content-Type': 'application/xml'},
+ folderContentsXml
+ );
+
+ promise.then(function(status, response) {
+ expect(status).toEqual(207);
+ expect(_.isArray(response)).toEqual(true);
+
+ expect(response.length).toEqual(3);
+
+ // root entry
+ var info = response[0];
+ expect(info instanceof OC.Files.FileInfo).toEqual(true);
+ expect(info.id).toEqual(11);
+ expect(info.path).toEqual('/path/to space');
+ expect(info.name).toEqual('文件夹');
+ expect(info.permissions).toEqual(31);
+ expect(info.size).toEqual(120);
+ expect(info.mtime.getTime()).toEqual(1436522405000);
+ expect(info.mimetype).toEqual('httpd/unix-directory');
+ expect(info.etag).toEqual('56cfcabd79abb');
+
+ // the two other entries follow
+ expect(response[1].id).toEqual(51);
+ expect(response[2].id).toEqual(15);
+ });
+
+ return promise;
+ });
+ it('rejects promise when an error occurred', function() {
+ var promise = client.getFolderContents('path/to space/文件夹', {includeParent: true});
+ return respondAndCheckError(promise, 404);
+ });
+ it('throws exception if arguments are missing', function() {
+ // TODO
+ });
+ });
+
+ describe('file info', function() {
+ var responseXml =
+ '<?xml version="1.0" encoding="utf-8"?>' +
+ '<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
+ makeResponseBlock(
+ '/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/',
+ {
+ 'd:getlastmodified': 'Fri, 10 Jul 2015 10:00:05 GMT',
+ 'd:getetag': '"56cfcabd79abb"',
+ 'd:resourcetype': '<d:collection/>',
+ 'oc:id': '00000011oc2d13a6a068',
+ 'oc:permissions': 'RDNVCK',
+ 'oc:size': 120
+ },
+ [
+ 'd:getcontenttype',
+ 'd:getcontentlength'
+ ]
+ ) +
+ '</d:multistatus>';
+
+ it('sends PROPFIND with zero depth to get single file info', function() {
+ client.getFileInfo('path/to space/文件夹');
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('PROPFIND');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
+ expect(fakeServer.requests[0].requestHeaders.Depth).toEqual(0);
+
+ var props = getRequestedProperties(fakeServer.requests[0].requestBody);
+ expect(props).toContain('{DAV:}getlastmodified');
+ expect(props).toContain('{DAV:}getcontentlength');
+ expect(props).toContain('{DAV:}getcontenttype');
+ expect(props).toContain('{DAV:}getetag');
+ expect(props).toContain('{DAV:}resourcetype');
+ expect(props).toContain('{http://owncloud.org/ns}fileid');
+ expect(props).toContain('{http://owncloud.org/ns}size');
+ expect(props).toContain('{http://owncloud.org/ns}permissions');
+ });
+ it('parses the result into a FileInfo', function() {
+ var promise = client.getFileInfo('path/to space/文件夹');
+
+ expect(fakeServer.requests.length).toEqual(1);
+
+ fakeServer.requests[0].respond(
+ 207,
+ {'Content-Type': 'application/xml'},
+ responseXml
+ );
+
+ promise.then(function(status, response) {
+ expect(status).toEqual(207);
+ expect(_.isArray(response)).toEqual(false);
+
+ var info = response;
+ expect(info instanceof OC.Files.FileInfo).toEqual(true);
+ expect(info.id).toEqual(11);
+ expect(info.path).toEqual('/path/to space');
+ expect(info.name).toEqual('文件夹');
+ expect(info.permissions).toEqual(31);
+ expect(info.size).toEqual(120);
+ expect(info.mtime.getTime()).toEqual(1436522405000);
+ expect(info.mimetype).toEqual('httpd/unix-directory');
+ expect(info.etag).toEqual('56cfcabd79abb');
+ });
+
+ return promise;
+ });
+ it('properly parses entry inside root', function() {
+ var responseXml =
+ '<?xml version="1.0" encoding="utf-8"?>' +
+ '<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
+ makeResponseBlock(
+ '/owncloud/remote.php/webdav/in%20root',
+ {
+ 'd:getlastmodified': 'Fri, 10 Jul 2015 10:00:05 GMT',
+ 'd:getetag': '"56cfcabd79abb"',
+ 'd:resourcetype': '<d:collection/>',
+ 'oc:id': '00000011oc2d13a6a068',
+ 'oc:permissions': 'RDNVCK',
+ 'oc:size': 120
+ },
+ [
+ 'd:getcontenttype',
+ 'd:getcontentlength'
+ ]
+ ) +
+ '</d:multistatus>';
+
+ var promise = client.getFileInfo('in root');
+
+ expect(fakeServer.requests.length).toEqual(1);
+
+ fakeServer.requests[0].respond(
+ 207,
+ {'Content-Type': 'application/xml'},
+ responseXml
+ );
+
+ promise.then(function(status, response) {
+ expect(status).toEqual(207);
+ expect(_.isArray(response)).toEqual(false);
+
+ var info = response;
+ expect(info instanceof OC.Files.FileInfo).toEqual(true);
+ expect(info.id).toEqual(11);
+ expect(info.path).toEqual('/');
+ expect(info.name).toEqual('in root');
+ expect(info.permissions).toEqual(31);
+ expect(info.size).toEqual(120);
+ expect(info.mtime.getTime()).toEqual(1436522405000);
+ expect(info.mimetype).toEqual('httpd/unix-directory');
+ expect(info.etag).toEqual('56cfcabd79abb');
+ });
+
+ return promise;
+ });
+ it('rejects promise when an error occurred', function() {
+ var promise = client.getFileInfo('path/to space/文件夹');
+ return respondAndCheckError(promise, 404);
+ });
+ it('throws exception if arguments are missing', function() {
+ // TODO
+ });
+ });
+
+ describe('permissions', function() {
+
+ function getFileInfoWithPermission(webdavPerm, isFile) {
+ var props = {
+ 'd:getlastmodified': 'Fri, 10 Jul 2015 13:38:05 GMT',
+ 'd:getetag': '"559fcabd79a38"',
+ 'd:getcontentlength': 250,
+ 'oc:id': '00000051oc2d13a6a068',
+ 'oc:permissions': webdavPerm,
+ };
+
+ if (isFile) {
+ props['d:getcontenttype'] = 'text/plain';
+ } else {
+ props['d:resourcetype'] = '<d:collection/>';
+ }
+
+ var responseXml =
+ '<?xml version="1.0" encoding="utf-8"?>' +
+ '<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
+ makeResponseBlock(
+ '/owncloud/remote.php/webdav/file.txt',
+ props
+ ) +
+ '</d:multistatus>';
+ var promise = client.getFileInfo('file.txt');
+
+ expect(fakeServer.requests.length).toEqual(1);
+ fakeServer.requests[0].respond(
+ 207,
+ {'Content-Type': 'application/xml'},
+ responseXml
+ );
+
+ fakeServer.restore();
+ fakeServer = sinon.fakeServer.create();
+
+ return promise;
+ }
+
+ function testPermission(permission, isFile, expectedPermissions) {
+ var promise = getFileInfoWithPermission(permission, isFile);
+ promise.then(function(result) {
+ expect(result.permissions).toEqual(expectedPermissions);
+ });
+ return promise;
+ }
+
+ function testMountType(permission, isFile, expectedMountType) {
+ var promise = getFileInfoWithPermission(permission, isFile);
+ promise.then(function(result) {
+ expect(result.mountType).toEqual(expectedMountType);
+ });
+ return promise;
+ }
+
+ it('properly parses file permissions', function() {
+ // permission, isFile, expectedPermissions
+ var testCases = [
+ ['', true, OC.PERMISSION_READ],
+ ['C', true, OC.PERMISSION_READ | OC.PERMISSION_CREATE],
+ ['K', true, OC.PERMISSION_READ | OC.PERMISSION_CREATE],
+ ['W', true, OC.PERMISSION_READ | OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE],
+ ['D', true, OC.PERMISSION_READ | OC.PERMISSION_DELETE],
+ ['R', true, OC.PERMISSION_READ | OC.PERMISSION_SHARE],
+ ['CKWDR', true, OC.PERMISSION_ALL]
+ ];
+ return Promise.all(
+ _.map(testCases, function(testCase) {
+ return testPermission.apply(testCase);
+ })
+ );
+ });
+ it('properly parses folder permissions', function() {
+ var testCases = [
+ ['', false, OC.PERMISSION_READ],
+ ['C', false, OC.PERMISSION_READ | OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE],
+ ['K', false, OC.PERMISSION_READ | OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE],
+ ['W', false, OC.PERMISSION_READ | OC.PERMISSION_UPDATE],
+ ['D', false, OC.PERMISSION_READ | OC.PERMISSION_DELETE],
+ ['R', false, OC.PERMISSION_READ | OC.PERMISSION_SHARE],
+ ['CKWDR', false, OC.PERMISSION_ALL]
+ ];
+
+ return Promise.all(
+ _.map(testCases, function(testCase) {
+ return testPermission.apply(testCase);
+ })
+ );
+ });
+ it('properly parses mount types', function() {
+ var testCases = [
+ ['CKWDR', false, null],
+ ['M', false, 'external'],
+ ['S', false, 'shared'],
+ ['SM', false, 'shared']
+ ];
+
+ return Promise.all(
+ _.map(testCases, function(testCase) {
+ return testMountType.apply(testCase);
+ })
+ );
+ });
+ });
+
+ describe('get file contents', function() {
+ it('returns file contents', function() {
+ var promise = client.getFileContents('path/to space/文件夹/One.txt');
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('GET');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt');
+
+ fakeServer.requests[0].respond(
+ 200,
+ {'Content-Type': 'text/plain'},
+ 'some contents'
+ );
+
+ promise.then(function(status, response) {
+ expect(status).toEqual(200);
+ expect(response).toEqual('some contents');
+ });
+
+ return promise;
+ });
+ it('rejects promise when an error occurred', function() {
+ var promise = client.getFileContents('path/to space/文件夹/One.txt');
+ return respondAndCheckError(promise, 409);
+ });
+ it('throws exception if arguments are missing', function() {
+ // TODO
+ });
+ });
+
+ describe('put file contents', function() {
+ it('sends PUT with file contents', function() {
+ var promise = client.putFileContents(
+ 'path/to space/文件夹/One.txt',
+ 'some contents'
+ );
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('PUT');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt');
+ expect(fakeServer.requests[0].requestBody).toEqual('some contents');
+ expect(fakeServer.requests[0].requestHeaders['If-None-Match']).toEqual('*');
+ expect(fakeServer.requests[0].requestHeaders['Content-Type']).toEqual('text/plain;charset=utf-8');
+
+ return respondAndCheckStatus(promise, 201);
+ });
+ it('sends PUT with file contents with headers matching options', function() {
+ var promise = client.putFileContents(
+ 'path/to space/文件夹/One.txt',
+ 'some contents',
+ {
+ overwrite: false,
+ contentType: 'text/markdown'
+ }
+ );
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('PUT');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt');
+ expect(fakeServer.requests[0].requestBody).toEqual('some contents');
+ expect(fakeServer.requests[0].requestHeaders['If-None-Match']).not.toBeDefined();
+ expect(fakeServer.requests[0].requestHeaders['Content-Type']).toEqual('text/markdown;charset=utf-8');
+
+ return respondAndCheckStatus(promise, 201);
+ });
+ it('rejects promise when an error occurred', function() {
+ var promise = client.putFileContents(
+ 'path/to space/文件夹/One.txt',
+ 'some contents'
+ );
+ return respondAndCheckError(promise, 409);
+ });
+ it('throws exception if arguments are missing', function() {
+ // TODO
+ });
+ });
+
+ describe('create directory', function() {
+ it('sends MKCOL with specified path', function() {
+ var promise = client.createDirectory('path/to space/文件夹/new dir');
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('MKCOL');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/new%20dir');
+
+ return respondAndCheckStatus(promise, 201);
+ });
+ it('rejects promise when an error occurred', function() {
+ var promise = client.createDirectory('path/to space/文件夹/new dir');
+ return respondAndCheckError(promise, 404);
+ });
+ it('throws exception if arguments are missing', function() {
+ // TODO
+ });
+ });
+
+ describe('deletion', function() {
+ it('sends DELETE with specified path', function() {
+ var promise = client.remove('path/to space/文件夹');
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('DELETE');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
+
+ return respondAndCheckStatus(promise, 201);
+ });
+ it('rejects promise when an error occurred', function() {
+ var promise = client.remove('path/to space/文件夹');
+ return respondAndCheckError(promise, 404);
+ });
+ it('throws exception if arguments are missing', function() {
+ // TODO
+ });
+ });
+
+ describe('move', function() {
+ it('sends MOVE with specified paths with fail on overwrite by default', function() {
+ var promise = client.move(
+ 'path/to space/文件夹',
+ 'path/to space/anotherdir/文件夹'
+ );
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('MOVE');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
+ expect(fakeServer.requests[0].requestHeaders.Destination)
+ .toEqual(baseUrl + 'path/to%20space/anotherdir/%E6%96%87%E4%BB%B6%E5%A4%B9');
+ expect(fakeServer.requests[0].requestHeaders.Overwrite)
+ .toEqual('F');
+
+ return respondAndCheckStatus(promise, 201);
+ });
+ it('sends MOVE with silent overwrite mode when specified', function() {
+ var promise = client.move(
+ 'path/to space/文件夹',
+ 'path/to space/anotherdir/文件夹',
+ {allowOverwrite: true}
+ );
+
+ expect(fakeServer.requests.length).toEqual(1);
+ expect(fakeServer.requests[0].method).toEqual('MOVE');
+ expect(fakeServer.requests[0].url).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
+ expect(fakeServer.requests[0].requestHeaders.Destination)
+ .toEqual(baseUrl + 'path/to%20space/anotherdir/%E6%96%87%E4%BB%B6%E5%A4%B9');
+ expect(fakeServer.requests[0].requestHeaders.Overwrite)
+ .not.toBeDefined();
+
+ return respondAndCheckStatus(promise, 201);
+ });
+ it('rejects promise when an error occurred', function() {
+ var promise = client.move(
+ 'path/to space/文件夹',
+ 'path/to space/anotherdir/文件夹',
+ {allowOverwrite: true}
+ );
+ return respondAndCheckError(promise, 404);
+ });
+ it('throws exception if arguments are missing', function() {
+ // TODO
+ });
+ });
+});
diff --git a/core/vendor/.gitignore b/core/vendor/.gitignore
index bcbb59b6f24..09b6a47c72d 100644
--- a/core/vendor/.gitignore
+++ b/core/vendor/.gitignore
@@ -122,3 +122,14 @@ bootstrap/js/*
# backbone
backbone/backbone-min*
+
+# davclient.js
+davclient.js/**
+!davclient.js/lib/*
+!davclient.js/LICENSE
+
+# es6-promise
+es6-promise/**
+!es6-promise/LICENSE
+!es6-promise/dist/es6-promise.js
+
diff --git a/core/vendor/davclient.js/LICENSE b/core/vendor/davclient.js/LICENSE
new file mode 100644
index 00000000000..fd7293e8f32
--- /dev/null
+++ b/core/vendor/davclient.js/LICENSE
@@ -0,0 +1,27 @@
+Copyright (C) 2013-2014 fruux GmbH (https://fruux.com/)
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name Sabre nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
diff --git a/core/vendor/davclient.js/lib/client.js b/core/vendor/davclient.js/lib/client.js
new file mode 100644
index 00000000000..121b5dcab5f
--- /dev/null
+++ b/core/vendor/davclient.js/lib/client.js
@@ -0,0 +1,296 @@
+if (typeof dav == 'undefined') { dav = {}; };
+
+dav.Client = function(options) {
+ var i;
+ for(i in options) {
+ this[i] = options[i];
+ }
+
+};
+
+dav.Client.prototype = {
+
+ baseUrl : null,
+
+ userName : null,
+
+ password : null,
+
+
+ xmlNamespaces : {
+ 'DAV:' : 'd'
+ },
+
+ /**
+ * Generates a propFind request.
+ *
+ * @param {string} url Url to do the propfind request on
+ * @param {Array} properties List of properties to retrieve.
+ * @return {Promise}
+ */
+ propFind : function(url, properties, depth) {
+
+ if(typeof depth == "undefined") {
+ depth = 0;
+ }
+
+ var headers = {
+ Depth : depth,
+ 'Content-Type' : 'application/xml; charset=utf-8'
+ };
+
+ var body =
+ '<?xml version="1.0"?>\n' +
+ '<d:propfind ';
+ var namespace;
+ for (namespace in this.xmlNamespaces) {
+ body += ' xmlns:' + this.xmlNamespaces[namespace] + '="' + namespace + '"';
+ }
+ body += '>\n' +
+ ' <d:prop>\n';
+
+ for(var ii in properties) {
+
+ var property = this.parseClarkNotation(properties[ii]);
+ if (this.xmlNamespaces[property.namespace]) {
+ body+=' <' + this.xmlNamespaces[property.namespace] + ':' + property.name + ' />\n';
+ } else {
+ body+=' <x:' + property.name + ' xmlns:x="' + property.namespace + '" />\n';
+ }
+
+ }
+ body+=' </d:prop>\n';
+ body+='</d:propfind>';
+
+ return this.request('PROPFIND', url, headers, body).then(
+ function(result) {
+
+ var resultBody = this.parseMultiStatus(result.body);
+ if (depth===0) {
+ return {
+ status: result.status,
+ body: resultBody[0],
+ xhr: result.xhr
+ };
+ } else {
+ return {
+ status: result.status,
+ body: resultBody,
+ xhr: result.xhr
+ };
+ }
+
+ }.bind(this)
+ );
+
+ },
+
+ /**
+ * Performs a HTTP request, and returns a Promise
+ *
+ * @param {string} method HTTP method
+ * @param {string} url Relative or absolute url
+ * @param {Object} headers HTTP headers as an object.
+ * @param {string} body HTTP request body.
+ * @return {Promise}
+ */
+ request : function(method, url, headers, body) {
+
+ var xhr = this.xhrProvider();
+
+ if (this.userName) {
+ headers['Authorization'] = 'Basic ' + btoa(this.userName + ':' + this.password);
+ // xhr.open(method, this.resolveUrl(url), true, this.userName, this.password);
+ }
+ xhr.open(method, this.resolveUrl(url), true);
+ var ii;
+ for(ii in headers) {
+ xhr.setRequestHeader(ii, headers[ii]);
+ }
+ xhr.send(body);
+
+ return new Promise(function(fulfill, reject) {
+
+ xhr.onreadystatechange = function() {
+
+ if (xhr.readyState !== 4) {
+ return;
+ }
+
+ fulfill({
+ body: xhr.response,
+ status: xhr.status,
+ xhr: xhr
+ });
+
+ };
+
+ xhr.ontimeout = function() {
+
+ reject(new Error('Timeout exceeded'));
+
+ };
+
+ });
+
+ },
+
+ /**
+ * Returns an XMLHttpRequest object.
+ *
+ * This is in its own method, so it can be easily overridden.
+ *
+ * @return {XMLHttpRequest}
+ */
+ xhrProvider : function() {
+
+ return new XMLHttpRequest();
+
+ },
+
+
+ /**
+ * Parses a multi-status response body.
+ *
+ * @param {string} xmlBody
+ * @param {Array}
+ */
+ parseMultiStatus : function(xmlBody) {
+
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(xmlBody, "application/xml");
+
+ var resolver = function(foo) {
+ var ii;
+ for(ii in this.xmlNamespaces) {
+ if (this.xmlNamespaces[ii] === foo) {
+ return ii;
+ }
+ }
+ }.bind(this);
+
+ var responseIterator = doc.evaluate('/d:multistatus/d:response', doc, resolver, XPathResult.ANY_TYPE, null);
+
+ var result = [];
+ var responseNode = responseIterator.iterateNext();
+
+ while(responseNode) {
+
+ var response = {
+ href : null,
+ propStat : []
+ };
+
+ response.href = doc.evaluate('string(d:href)', responseNode, resolver, XPathResult.ANY_TYPE, null).stringValue;
+
+ var propStatIterator = doc.evaluate('d:propstat', responseNode, resolver, XPathResult.ANY_TYPE, null);
+ var propStatNode = propStatIterator.iterateNext();
+
+ while(propStatNode) {
+
+ var propStat = {
+ status : doc.evaluate('string(d:status)', propStatNode, resolver, XPathResult.ANY_TYPE, null).stringValue,
+ properties : [],
+ };
+
+ var propIterator = doc.evaluate('d:prop/*', propStatNode, resolver, XPathResult.ANY_TYPE, null);
+
+ var propNode = propIterator.iterateNext();
+ while(propNode) {
+ var content = propNode.textContent;
+ if (propNode.childNodes && propNode.childNodes.length > 0 && propNode.childNodes[0].nodeType === 1) {
+ content = propNode.childNodes;
+ }
+
+ propStat.properties['{' + propNode.namespaceURI + '}' + propNode.localName] = content;
+ propNode = propIterator.iterateNext();
+
+ }
+ response.propStat.push(propStat);
+ propStatNode = propStatIterator.iterateNext();
+
+
+ }
+
+ result.push(response);
+ responseNode = responseIterator.iterateNext();
+
+ }
+
+ return result;
+
+ },
+
+ /**
+ * Takes a relative url, and maps it to an absolute url, using the baseUrl
+ *
+ * @param {string} url
+ * @return {string}
+ */
+ resolveUrl : function(url) {
+
+ // Note: this is rudamentary.. not sure yet if it handles every case.
+ if (/^https?:\/\//i.test(url)) {
+ // absolute
+ return url;
+ }
+
+ var baseParts = this.parseUrl(this.baseUrl);
+ if (url.charAt('/')) {
+ // Url starts with a slash
+ return baseParts.root + url;
+ }
+
+ // Url does not start with a slash, we need grab the base url right up until the last slash.
+ var newUrl = baseParts.root + '/';
+ if (baseParts.path.lastIndexOf('/')!==-1) {
+ newUrl = newUrl = baseParts.path.subString(0, baseParts.path.lastIndexOf('/')) + '/';
+ }
+ newUrl+=url;
+ return url;
+
+ },
+
+ /**
+ * Parses a url and returns its individual components.
+ *
+ * @param {String} url
+ * @return {Object}
+ */
+ parseUrl : function(url) {
+
+ var parts = url.match(/^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/);
+ var result = {
+ url : parts[0],
+ scheme : parts[1],
+ host : parts[3],
+ port : parts[4],
+ path : parts[5],
+ query : parts[6],
+ fragment : parts[7],
+ };
+ result.root =
+ result.scheme + '://' +
+ result.host +
+ (result.port ? ':' + result.port : '');
+
+ return result;
+
+ },
+
+ parseClarkNotation : function(propertyName) {
+
+ var result = propertyName.match(/^{([^}]+)}(.*)$/);
+ if (!result) {
+ return;
+ }
+
+ return {
+ name : result[2],
+ namespace : result[1]
+ };
+
+ }
+
+};
+
diff --git a/core/vendor/es6-promise/.bower.json b/core/vendor/es6-promise/.bower.json
new file mode 100644
index 00000000000..f8c28b04e53
--- /dev/null
+++ b/core/vendor/es6-promise/.bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "es6-promise",
+ "namespace": "Promise",
+ "version": "2.3.0",
+ "description": "A polyfill for ES6-style Promises, tracking rsvp",
+ "authors": [
+ "Stefan Penner <stefan.penner@gmail.com>"
+ ],
+ "main": "dist/es6-promise.js",
+ "keywords": [
+ "promise"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/jakearchibald/ES6-Promises.git"
+ },
+ "bugs": {
+ "url": "https://github.com/jakearchibald/ES6-Promises/issues"
+ },
+ "license": "MIT",
+ "ignore": [
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests",
+ "vendor",
+ "tasks"
+ ],
+ "homepage": "https://github.com/jakearchibald/es6-promise",
+ "_release": "2.3.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "2.3.0",
+ "commit": "fcbab11a1a981eb2290bfff89017cb764335a2a5"
+ },
+ "_source": "https://github.com/jakearchibald/es6-promise.git",
+ "_target": "~2.3.0",
+ "_originalSource": "https://github.com/jakearchibald/es6-promise.git",
+ "_direct": true
+} \ No newline at end of file
diff --git a/core/vendor/es6-promise/.npmignore b/core/vendor/es6-promise/.npmignore
new file mode 100644
index 00000000000..7a758111e9e
--- /dev/null
+++ b/core/vendor/es6-promise/.npmignore
@@ -0,0 +1,11 @@
+/node_modules/
+/tmp
+/tasks
+/test
+/vendor
+/.jshintrc
+/.npmignore
+/.travis.yml
+/Gruntfile.js
+/component.json
+/index.html
diff --git a/core/vendor/es6-promise/.release.json b/core/vendor/es6-promise/.release.json
new file mode 100644
index 00000000000..dee8cbc5d92
--- /dev/null
+++ b/core/vendor/es6-promise/.release.json
@@ -0,0 +1,17 @@
+{
+ "non-interactive": true,
+ "dry-run": false,
+ "verbose": false,
+ "force": false,
+ "pkgFiles": ["package.json", "bower.json"],
+ "increment": "patch",
+ "commitMessage": "Release %s",
+ "tagName": "%s",
+ "tagAnnotation": "Release %s",
+ "buildCommand": "npm run-script build-all",
+ "distRepo": "git@github.com:components/rsvp.js.git",
+ "distStageDir": "tmp/stage",
+ "distBase": "dist",
+ "distFiles": ["**/*", "../package.json", "../bower.json"],
+ "publish": false
+}
diff --git a/core/vendor/es6-promise/.spmignore b/core/vendor/es6-promise/.spmignore
new file mode 100644
index 00000000000..7a758111e9e
--- /dev/null
+++ b/core/vendor/es6-promise/.spmignore
@@ -0,0 +1,11 @@
+/node_modules/
+/tmp
+/tasks
+/test
+/vendor
+/.jshintrc
+/.npmignore
+/.travis.yml
+/Gruntfile.js
+/component.json
+/index.html
diff --git a/core/vendor/es6-promise/LICENSE b/core/vendor/es6-promise/LICENSE
new file mode 100644
index 00000000000..954ec5992df
--- /dev/null
+++ b/core/vendor/es6-promise/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
+
+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.
diff --git a/core/vendor/es6-promise/dist/es6-promise.js b/core/vendor/es6-promise/dist/es6-promise.js
new file mode 100644
index 00000000000..aff0482ee5e
--- /dev/null
+++ b/core/vendor/es6-promise/dist/es6-promise.js
@@ -0,0 +1,972 @@
+/*!
+ * @overview es6-promise - a tiny implementation of Promises/A+.
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
+ * @license Licensed under MIT license
+ * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
+ * @version 2.3.0
+ */
+
+(function() {
+ "use strict";
+ function lib$es6$promise$utils$$objectOrFunction(x) {
+ return typeof x === 'function' || (typeof x === 'object' && x !== null);
+ }
+
+ function lib$es6$promise$utils$$isFunction(x) {
+ return typeof x === 'function';
+ }
+
+ function lib$es6$promise$utils$$isMaybeThenable(x) {
+ return typeof x === 'object' && x !== null;
+ }
+
+ var lib$es6$promise$utils$$_isArray;
+ if (!Array.isArray) {
+ lib$es6$promise$utils$$_isArray = function (x) {
+ return Object.prototype.toString.call(x) === '[object Array]';
+ };
+ } else {
+ lib$es6$promise$utils$$_isArray = Array.isArray;
+ }
+
+ var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray;
+ var lib$es6$promise$asap$$len = 0;
+ var lib$es6$promise$asap$$toString = {}.toString;
+ var lib$es6$promise$asap$$vertxNext;
+ var lib$es6$promise$asap$$customSchedulerFn;
+
+ var lib$es6$promise$asap$$asap = function asap(callback, arg) {
+ lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback;
+ lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg;
+ lib$es6$promise$asap$$len += 2;
+ if (lib$es6$promise$asap$$len === 2) {
+ // If len is 2, that means that we need to schedule an async flush.
+ // If additional callbacks are queued before the queue is flushed, they
+ // will be processed by this flush that we are scheduling.
+ if (lib$es6$promise$asap$$customSchedulerFn) {
+ lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush);
+ } else {
+ lib$es6$promise$asap$$scheduleFlush();
+ }
+ }
+ }
+
+ function lib$es6$promise$asap$$setScheduler(scheduleFn) {
+ lib$es6$promise$asap$$customSchedulerFn = scheduleFn;
+ }
+
+ function lib$es6$promise$asap$$setAsap(asapFn) {
+ lib$es6$promise$asap$$asap = asapFn;
+ }
+
+ var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined;
+ var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {};
+ var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver;
+ var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
+
+ // test for web worker but not in IE10
+ var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
+ typeof importScripts !== 'undefined' &&
+ typeof MessageChannel !== 'undefined';
+
+ // node
+ function lib$es6$promise$asap$$useNextTick() {
+ var nextTick = process.nextTick;
+ // node version 0.10.x displays a deprecation warning when nextTick is used recursively
+ // setImmediate should be used instead instead
+ var version = process.versions.node.match(/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/);
+ if (Array.isArray(version) && version[1] === '0' && version[2] === '10') {
+ nextTick = setImmediate;
+ }
+ return function() {
+ nextTick(lib$es6$promise$asap$$flush);
+ };
+ }
+
+ // vertx
+ function lib$es6$promise$asap$$useVertxTimer() {
+ return function() {
+ lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush);
+ };
+ }
+
+ function lib$es6$promise$asap$$useMutationObserver() {
+ var iterations = 0;
+ var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush);
+ var node = document.createTextNode('');
+ observer.observe(node, { characterData: true });
+
+ return function() {
+ node.data = (iterations = ++iterations % 2);
+ };
+ }
+
+ // web worker
+ function lib$es6$promise$asap$$useMessageChannel() {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = lib$es6$promise$asap$$flush;
+ return function () {
+ channel.port2.postMessage(0);
+ };
+ }
+
+ function lib$es6$promise$asap$$useSetTimeout() {
+ return function() {
+ setTimeout(lib$es6$promise$asap$$flush, 1);
+ };
+ }
+
+ var lib$es6$promise$asap$$queue = new Array(1000);
+ function lib$es6$promise$asap$$flush() {
+ for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) {
+ var callback = lib$es6$promise$asap$$queue[i];
+ var arg = lib$es6$promise$asap$$queue[i+1];
+
+ callback(arg);
+
+ lib$es6$promise$asap$$queue[i] = undefined;
+ lib$es6$promise$asap$$queue[i+1] = undefined;
+ }
+
+ lib$es6$promise$asap$$len = 0;
+ }
+
+ function lib$es6$promise$asap$$attemptVertex() {
+ try {
+ var r = require;
+ var vertx = r('vertx');
+ lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext;
+ return lib$es6$promise$asap$$useVertxTimer();
+ } catch(e) {
+ return lib$es6$promise$asap$$useSetTimeout();
+ }
+ }
+
+ var lib$es6$promise$asap$$scheduleFlush;
+ // Decide what async method to use to triggering processing of queued callbacks:
+ if (lib$es6$promise$asap$$isNode) {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick();
+ } else if (lib$es6$promise$asap$$BrowserMutationObserver) {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver();
+ } else if (lib$es6$promise$asap$$isWorker) {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel();
+ } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertex();
+ } else {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout();
+ }
+
+ function lib$es6$promise$$internal$$noop() {}
+
+ var lib$es6$promise$$internal$$PENDING = void 0;
+ var lib$es6$promise$$internal$$FULFILLED = 1;
+ var lib$es6$promise$$internal$$REJECTED = 2;
+
+ var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject();
+
+ function lib$es6$promise$$internal$$selfFullfillment() {
+ return new TypeError("You cannot resolve a promise with itself");
+ }
+
+ function lib$es6$promise$$internal$$cannotReturnOwn() {
+ return new TypeError('A promises callback cannot return that same promise.');
+ }
+
+ function lib$es6$promise$$internal$$getThen(promise) {
+ try {
+ return promise.then;
+ } catch(error) {
+ lib$es6$promise$$internal$$GET_THEN_ERROR.error = error;
+ return lib$es6$promise$$internal$$GET_THEN_ERROR;
+ }
+ }
+
+ function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
+ try {
+ then.call(value, fulfillmentHandler, rejectionHandler);
+ } catch(e) {
+ return e;
+ }
+ }
+
+ function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) {
+ lib$es6$promise$asap$$asap(function(promise) {
+ var sealed = false;
+ var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) {
+ if (sealed) { return; }
+ sealed = true;
+ if (thenable !== value) {
+ lib$es6$promise$$internal$$resolve(promise, value);
+ } else {
+ lib$es6$promise$$internal$$fulfill(promise, value);
+ }
+ }, function(reason) {
+ if (sealed) { return; }
+ sealed = true;
+
+ lib$es6$promise$$internal$$reject(promise, reason);
+ }, 'Settle: ' + (promise._label || ' unknown promise'));
+
+ if (!sealed && error) {
+ sealed = true;
+ lib$es6$promise$$internal$$reject(promise, error);
+ }
+ }, promise);
+ }
+
+ function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) {
+ if (thenable._state === lib$es6$promise$$internal$$FULFILLED) {
+ lib$es6$promise$$internal$$fulfill(promise, thenable._result);
+ } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) {
+ lib$es6$promise$$internal$$reject(promise, thenable._result);
+ } else {
+ lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) {
+ lib$es6$promise$$internal$$resolve(promise, value);
+ }, function(reason) {
+ lib$es6$promise$$internal$$reject(promise, reason);
+ });
+ }
+ }
+
+ function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable) {
+ if (maybeThenable.constructor === promise.constructor) {
+ lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable);
+ } else {
+ var then = lib$es6$promise$$internal$$getThen(maybeThenable);
+
+ if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) {
+ lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error);
+ } else if (then === undefined) {
+ lib$es6$promise$$internal$$fulfill(promise, maybeThenable);
+ } else if (lib$es6$promise$utils$$isFunction(then)) {
+ lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then);
+ } else {
+ lib$es6$promise$$internal$$fulfill(promise, maybeThenable);
+ }
+ }
+ }
+
+ function lib$es6$promise$$internal$$resolve(promise, value) {
+ if (promise === value) {
+ lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFullfillment());
+ } else if (lib$es6$promise$utils$$objectOrFunction(value)) {
+ lib$es6$promise$$internal$$handleMaybeThenable(promise, value);
+ } else {
+ lib$es6$promise$$internal$$fulfill(promise, value);
+ }
+ }
+
+ function lib$es6$promise$$internal$$publishRejection(promise) {
+ if (promise._onerror) {
+ promise._onerror(promise._result);
+ }
+
+ lib$es6$promise$$internal$$publish(promise);
+ }
+
+ function lib$es6$promise$$internal$$fulfill(promise, value) {
+ if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; }
+
+ promise._result = value;
+ promise._state = lib$es6$promise$$internal$$FULFILLED;
+
+ if (promise._subscribers.length !== 0) {
+ lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise);
+ }
+ }
+
+ function lib$es6$promise$$internal$$reject(promise, reason) {
+ if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; }
+ promise._state = lib$es6$promise$$internal$$REJECTED;
+ promise._result = reason;
+
+ lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise);
+ }
+
+ function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
+ var subscribers = parent._subscribers;
+ var length = subscribers.length;
+
+ parent._onerror = null;
+
+ subscribers[length] = child;
+ subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment;
+ subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection;
+
+ if (length === 0 && parent._state) {
+ lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent);
+ }
+ }
+
+ function lib$es6$promise$$internal$$publish(promise) {
+ var subscribers = promise._subscribers;
+ var settled = promise._state;
+
+ if (subscribers.length === 0) { return; }
+
+ var child, callback, detail = promise._result;
+
+ for (var i = 0; i < subscribers.length; i += 3) {
+ child = subscribers[i];
+ callback = subscribers[i + settled];
+
+ if (child) {
+ lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail);
+ } else {
+ callback(detail);
+ }
+ }
+
+ promise._subscribers.length = 0;
+ }
+
+ function lib$es6$promise$$internal$$ErrorObject() {
+ this.error = null;
+ }
+
+ var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject();
+
+ function lib$es6$promise$$internal$$tryCatch(callback, detail) {
+ try {
+ return callback(detail);
+ } catch(e) {
+ lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e;
+ return lib$es6$promise$$internal$$TRY_CATCH_ERROR;
+ }
+ }
+
+ function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) {
+ var hasCallback = lib$es6$promise$utils$$isFunction(callback),
+ value, error, succeeded, failed;
+
+ if (hasCallback) {
+ value = lib$es6$promise$$internal$$tryCatch(callback, detail);
+
+ if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) {
+ failed = true;
+ error = value.error;
+ value = null;
+ } else {
+ succeeded = true;
+ }
+
+ if (promise === value) {
+ lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn());
+ return;
+ }
+
+ } else {
+ value = detail;
+ succeeded = true;
+ }
+
+ if (promise._state !== lib$es6$promise$$internal$$PENDING) {
+ // noop
+ } else if (hasCallback && succeeded) {
+ lib$es6$promise$$internal$$resolve(promise, value);
+ } else if (failed) {
+ lib$es6$promise$$internal$$reject(promise, error);
+ } else if (settled === lib$es6$promise$$internal$$FULFILLED) {
+ lib$es6$promise$$internal$$fulfill(promise, value);
+ } else if (settled === lib$es6$promise$$internal$$REJECTED) {
+ lib$es6$promise$$internal$$reject(promise, value);
+ }
+ }
+
+ function lib$es6$promise$$internal$$initializePromise(promise, resolver) {
+ try {
+ resolver(function resolvePromise(value){
+ lib$es6$promise$$internal$$resolve(promise, value);
+ }, function rejectPromise(reason) {
+ lib$es6$promise$$internal$$reject(promise, reason);
+ });
+ } catch(e) {
+ lib$es6$promise$$internal$$reject(promise, e);
+ }
+ }
+
+ function lib$es6$promise$enumerator$$Enumerator(Constructor, input) {
+ var enumerator = this;
+
+ enumerator._instanceConstructor = Constructor;
+ enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop);
+
+ if (enumerator._validateInput(input)) {
+ enumerator._input = input;
+ enumerator.length = input.length;
+ enumerator._remaining = input.length;
+
+ enumerator._init();
+
+ if (enumerator.length === 0) {
+ lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result);
+ } else {
+ enumerator.length = enumerator.length || 0;
+ enumerator._enumerate();
+ if (enumerator._remaining === 0) {
+ lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result);
+ }
+ }
+ } else {
+ lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError());
+ }
+ }
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) {
+ return lib$es6$promise$utils$$isArray(input);
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() {
+ return new Error('Array Methods must be provided an Array');
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._init = function() {
+ this._result = new Array(this.length);
+ };
+
+ var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator;
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() {
+ var enumerator = this;
+
+ var length = enumerator.length;
+ var promise = enumerator.promise;
+ var input = enumerator._input;
+
+ for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) {
+ enumerator._eachEntry(input[i], i);
+ }
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
+ var enumerator = this;
+ var c = enumerator._instanceConstructor;
+
+ if (lib$es6$promise$utils$$isMaybeThenable(entry)) {
+ if (entry.constructor === c && entry._state !== lib$es6$promise$$internal$$PENDING) {
+ entry._onerror = null;
+ enumerator._settledAt(entry._state, i, entry._result);
+ } else {
+ enumerator._willSettleAt(c.resolve(entry), i);
+ }
+ } else {
+ enumerator._remaining--;
+ enumerator._result[i] = entry;
+ }
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
+ var enumerator = this;
+ var promise = enumerator.promise;
+
+ if (promise._state === lib$es6$promise$$internal$$PENDING) {
+ enumerator._remaining--;
+
+ if (state === lib$es6$promise$$internal$$REJECTED) {
+ lib$es6$promise$$internal$$reject(promise, value);
+ } else {
+ enumerator._result[i] = value;
+ }
+ }
+
+ if (enumerator._remaining === 0) {
+ lib$es6$promise$$internal$$fulfill(promise, enumerator._result);
+ }
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
+ var enumerator = this;
+
+ lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) {
+ enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value);
+ }, function(reason) {
+ enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason);
+ });
+ };
+ function lib$es6$promise$promise$all$$all(entries) {
+ return new lib$es6$promise$enumerator$$default(this, entries).promise;
+ }
+ var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all;
+ function lib$es6$promise$promise$race$$race(entries) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ var promise = new Constructor(lib$es6$promise$$internal$$noop);
+
+ if (!lib$es6$promise$utils$$isArray(entries)) {
+ lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.'));
+ return promise;
+ }
+
+ var length = entries.length;
+
+ function onFulfillment(value) {
+ lib$es6$promise$$internal$$resolve(promise, value);
+ }
+
+ function onRejection(reason) {
+ lib$es6$promise$$internal$$reject(promise, reason);
+ }
+
+ for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) {
+ lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection);
+ }
+
+ return promise;
+ }
+ var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race;
+ function lib$es6$promise$promise$resolve$$resolve(object) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ if (object && typeof object === 'object' && object.constructor === Constructor) {
+ return object;
+ }
+
+ var promise = new Constructor(lib$es6$promise$$internal$$noop);
+ lib$es6$promise$$internal$$resolve(promise, object);
+ return promise;
+ }
+ var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve;
+ function lib$es6$promise$promise$reject$$reject(reason) {
+ /*jshint validthis:true */
+ var Constructor = this;
+ var promise = new Constructor(lib$es6$promise$$internal$$noop);
+ lib$es6$promise$$internal$$reject(promise, reason);
+ return promise;
+ }
+ var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject;
+
+ var lib$es6$promise$promise$$counter = 0;
+
+ function lib$es6$promise$promise$$needsResolver() {
+ throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
+ }
+
+ function lib$es6$promise$promise$$needsNew() {
+ throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
+ }
+
+ var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise;
+ /**
+ Promise objects represent the eventual result of an asynchronous operation. The
+ primary way of interacting with a promise is through its `then` method, which
+ registers callbacks to receive either a promise's eventual value or the reason
+ why the promise cannot be fulfilled.
+
+ Terminology
+ -----------
+
+ - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
+ - `thenable` is an object or function that defines a `then` method.
+ - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
+ - `exception` is a value that is thrown using the throw statement.
+ - `reason` is a value that indicates why a promise was rejected.
+ - `settled` the final resting state of a promise, fulfilled or rejected.
+
+ A promise can be in one of three states: pending, fulfilled, or rejected.
+
+ Promises that are fulfilled have a fulfillment value and are in the fulfilled
+ state. Promises that are rejected have a rejection reason and are in the
+ rejected state. A fulfillment value is never a thenable.
+
+ Promises can also be said to *resolve* a value. If this value is also a
+ promise, then the original promise's settled state will match the value's
+ settled state. So a promise that *resolves* a promise that rejects will
+ itself reject, and a promise that *resolves* a promise that fulfills will
+ itself fulfill.
+
+
+ Basic Usage:
+ ------------
+
+ ```js
+ var promise = new Promise(function(resolve, reject) {
+ // on success
+ resolve(value);
+
+ // on failure
+ reject(reason);
+ });
+
+ promise.then(function(value) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Advanced Usage:
+ ---------------
+
+ Promises shine when abstracting away asynchronous interactions such as
+ `XMLHttpRequest`s.
+
+ ```js
+ function getJSON(url) {
+ return new Promise(function(resolve, reject){
+ var xhr = new XMLHttpRequest();
+
+ xhr.open('GET', url);
+ xhr.onreadystatechange = handler;
+ xhr.responseType = 'json';
+ xhr.setRequestHeader('Accept', 'application/json');
+ xhr.send();
+
+ function handler() {
+ if (this.readyState === this.DONE) {
+ if (this.status === 200) {
+ resolve(this.response);
+ } else {
+ reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
+ }
+ }
+ };
+ });
+ }
+
+ getJSON('/posts.json').then(function(json) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Unlike callbacks, promises are great composable primitives.
+
+ ```js
+ Promise.all([
+ getJSON('/posts'),
+ getJSON('/comments')
+ ]).then(function(values){
+ values[0] // => postsJSON
+ values[1] // => commentsJSON
+
+ return values;
+ });
+ ```
+
+ @class Promise
+ @param {function} resolver
+ Useful for tooling.
+ @constructor
+ */
+ function lib$es6$promise$promise$$Promise(resolver) {
+ this._id = lib$es6$promise$promise$$counter++;
+ this._state = undefined;
+ this._result = undefined;
+ this._subscribers = [];
+
+ if (lib$es6$promise$$internal$$noop !== resolver) {
+ if (!lib$es6$promise$utils$$isFunction(resolver)) {
+ lib$es6$promise$promise$$needsResolver();
+ }
+
+ if (!(this instanceof lib$es6$promise$promise$$Promise)) {
+ lib$es6$promise$promise$$needsNew();
+ }
+
+ lib$es6$promise$$internal$$initializePromise(this, resolver);
+ }
+ }
+
+ lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default;
+ lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default;
+ lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default;
+ lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default;
+ lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler;
+ lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap;
+ lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap;
+
+ lib$es6$promise$promise$$Promise.prototype = {
+ constructor: lib$es6$promise$promise$$Promise,
+
+ /**
+ The primary way of interacting with a promise is through its `then` method,
+ which registers callbacks to receive either a promise's eventual value or the
+ reason why the promise cannot be fulfilled.
+
+ ```js
+ findUser().then(function(user){
+ // user is available
+ }, function(reason){
+ // user is unavailable, and you are given the reason why
+ });
+ ```
+
+ Chaining
+ --------
+
+ The return value of `then` is itself a promise. This second, 'downstream'
+ promise is resolved with the return value of the first promise's fulfillment
+ or rejection handler, or rejected if the handler throws an exception.
+
+ ```js
+ findUser().then(function (user) {
+ return user.name;
+ }, function (reason) {
+ return 'default name';
+ }).then(function (userName) {
+ // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
+ // will be `'default name'`
+ });
+
+ findUser().then(function (user) {
+ throw new Error('Found user, but still unhappy');
+ }, function (reason) {
+ throw new Error('`findUser` rejected and we're unhappy');
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
+ // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
+ });
+ ```
+ If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
+
+ ```js
+ findUser().then(function (user) {
+ throw new PedagogicalException('Upstream error');
+ }).then(function (value) {
+ // never reached
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // The `PedgagocialException` is propagated all the way down to here
+ });
+ ```
+
+ Assimilation
+ ------------
+
+ Sometimes the value you want to propagate to a downstream promise can only be
+ retrieved asynchronously. This can be achieved by returning a promise in the
+ fulfillment or rejection handler. The downstream promise will then be pending
+ until the returned promise is settled. This is called *assimilation*.
+
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // The user's comments are now available
+ });
+ ```
+
+ If the assimliated promise rejects, then the downstream promise will also reject.
+
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // If `findCommentsByAuthor` fulfills, we'll have the value here
+ }, function (reason) {
+ // If `findCommentsByAuthor` rejects, we'll have the reason here
+ });
+ ```
+
+ Simple Example
+ --------------
+
+ Synchronous Example
+
+ ```javascript
+ var result;
+
+ try {
+ result = findResult();
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+
+ Errback Example
+
+ ```js
+ findResult(function(result, err){
+ if (err) {
+ // failure
+ } else {
+ // success
+ }
+ });
+ ```
+
+ Promise Example;
+
+ ```javascript
+ findResult().then(function(result){
+ // success
+ }, function(reason){
+ // failure
+ });
+ ```
+
+ Advanced Example
+ --------------
+
+ Synchronous Example
+
+ ```javascript
+ var author, books;
+
+ try {
+ author = findAuthor();
+ books = findBooksByAuthor(author);
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+
+ Errback Example
+
+ ```js
+
+ function foundBooks(books) {
+
+ }
+
+ function failure(reason) {
+
+ }
+
+ findAuthor(function(author, err){
+ if (err) {
+ failure(err);
+ // failure
+ } else {
+ try {
+ findBoooksByAuthor(author, function(books, err) {
+ if (err) {
+ failure(err);
+ } else {
+ try {
+ foundBooks(books);
+ } catch(reason) {
+ failure(reason);
+ }
+ }
+ });
+ } catch(error) {
+ failure(err);
+ }
+ // success
+ }
+ });
+ ```
+
+ Promise Example;
+
+ ```javascript
+ findAuthor().
+ then(findBooksByAuthor).
+ then(function(books){
+ // found books
+ }).catch(function(reason){
+ // something went wrong
+ });
+ ```
+
+ @method then
+ @param {Function} onFulfilled
+ @param {Function} onRejected
+ Useful for tooling.
+ @return {Promise}
+ */
+ then: function(onFulfillment, onRejection) {
+ var parent = this;
+ var state = parent._state;
+
+ if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) {
+ return this;
+ }
+
+ var child = new this.constructor(lib$es6$promise$$internal$$noop);
+ var result = parent._result;
+
+ if (state) {
+ var callback = arguments[state - 1];
+ lib$es6$promise$asap$$asap(function(){
+ lib$es6$promise$$internal$$invokeCallback(state, child, callback, result);
+ });
+ } else {
+ lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection);
+ }
+
+ return child;
+ },
+
+ /**
+ `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
+ as the catch block of a try/catch statement.
+
+ ```js
+ function findAuthor(){
+ throw new Error('couldn't find that author');
+ }
+
+ // synchronous
+ try {
+ findAuthor();
+ } catch(reason) {
+ // something went wrong
+ }
+
+ // async with promises
+ findAuthor().catch(function(reason){
+ // something went wrong
+ });
+ ```
+
+ @method catch
+ @param {Function} onRejection
+ Useful for tooling.
+ @return {Promise}
+ */
+ 'catch': function(onRejection) {
+ return this.then(null, onRejection);
+ }
+ };
+ function lib$es6$promise$polyfill$$polyfill() {
+ var local;
+
+ if (typeof global !== 'undefined') {
+ local = global;
+ } else if (typeof self !== 'undefined') {
+ local = self;
+ } else {
+ try {
+ local = Function('return this')();
+ } catch (e) {
+ throw new Error('polyfill failed because global object is unavailable in this environment');
+ }
+ }
+
+ var P = local.Promise;
+
+ if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) {
+ return;
+ }
+
+ local.Promise = lib$es6$promise$promise$$default;
+ }
+ var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill;
+
+ var lib$es6$promise$umd$$ES6Promise = {
+ 'Promise': lib$es6$promise$promise$$default,
+ 'polyfill': lib$es6$promise$polyfill$$default
+ };
+
+ /* global define:true module:true window: true */
+ if (typeof define === 'function' && define['amd']) {
+ define(function() { return lib$es6$promise$umd$$ES6Promise; });
+ } else if (typeof module !== 'undefined' && module['exports']) {
+ module['exports'] = lib$es6$promise$umd$$ES6Promise;
+ } else if (typeof this !== 'undefined') {
+ this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise;
+ }
+
+ lib$es6$promise$polyfill$$default();
+}).call(this);
+