Adding mapbox-gl branch

This commit is contained in:
Andreas Hocevar
2015-03-16 18:50:27 +01:00
parent 7985f030fa
commit 57ee7f52fd
3109 changed files with 943365 additions and 0 deletions
@@ -0,0 +1,272 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrappers for HTML5 Entry objects. These are all in the same
* file to avoid circular dependency issues.
*
* When adding or modifying functionality in this namespace, be sure to update
* the mock counterparts in goog.testing.fs.
*
*/
goog.provide('goog.fs.DirectoryEntry');
goog.provide('goog.fs.DirectoryEntry.Behavior');
goog.provide('goog.fs.Entry');
goog.provide('goog.fs.FileEntry');
/**
* The interface for entries in the filesystem.
* @interface
*/
goog.fs.Entry = function() {};
/**
* @return {boolean} Whether or not this entry is a file.
*/
goog.fs.Entry.prototype.isFile = function() {};
/**
* @return {boolean} Whether or not this entry is a directory.
*/
goog.fs.Entry.prototype.isDirectory = function() {};
/**
* @return {string} The name of this entry.
*/
goog.fs.Entry.prototype.getName = function() {};
/**
* @return {string} The full path to this entry.
*/
goog.fs.Entry.prototype.getFullPath = function() {};
/**
* @return {!goog.fs.FileSystem} The filesystem backing this entry.
*/
goog.fs.Entry.prototype.getFileSystem = function() {};
/**
* Retrieves the last modified date for this entry.
*
* @return {!goog.async.Deferred} The deferred Date for this entry. If an error
* occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.Entry.prototype.getLastModified = function() {};
/**
* Retrieves the metadata for this entry.
*
* @return {!goog.async.Deferred} The deferred Metadata for this entry. If an
* error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.Entry.prototype.getMetadata = function() {};
/**
* Move this entry to a new location.
*
* @param {!goog.fs.DirectoryEntry} parent The new parent directory.
* @param {string=} opt_newName The new name of the entry. If omitted, the entry
* retains its original name.
* @return {!goog.async.Deferred} The deferred {@link goog.fs.FileEntry} or
* {@link goog.fs.DirectoryEntry} for the new entry. If an error occurs, the
* errback is called with a {@link goog.fs.Error}.
*/
goog.fs.Entry.prototype.moveTo = function(parent, opt_newName) {};
/**
* Copy this entry to a new location.
*
* @param {!goog.fs.DirectoryEntry} parent The new parent directory.
* @param {string=} opt_newName The name of the new entry. If omitted, the new
* entry has the same name as the original.
* @return {!goog.async.Deferred} The deferred {@link goog.fs.FileEntry} or
* {@link goog.fs.DirectoryEntry} for the new entry. If an error occurs, the
* errback is called with a {@link goog.fs.Error}.
*/
goog.fs.Entry.prototype.copyTo = function(parent, opt_newName) {};
/**
* Wrap an HTML5 entry object in an appropriate subclass instance.
*
* @param {!Entry} entry The underlying Entry object.
* @return {!goog.fs.Entry} The appropriate subclass wrapper.
* @protected
*/
goog.fs.Entry.prototype.wrapEntry = function(entry) {};
/**
* Get the URL for this file.
*
* @param {string=} opt_mimeType The MIME type that will be served for the URL.
* @return {string} The URL.
*/
goog.fs.Entry.prototype.toUrl = function(opt_mimeType) {};
/**
* Get the URI for this file.
*
* @deprecated Use {@link #toUrl} instead.
* @param {string=} opt_mimeType The MIME type that will be served for the URI.
* @return {string} The URI.
*/
goog.fs.Entry.prototype.toUri = function(opt_mimeType) {};
/**
* Remove this entry.
*
* @return {!goog.async.Deferred} A deferred object. If the removal succeeds,
* the callback is called with true. If an error occurs, the errback is
* called a {@link goog.fs.Error}.
*/
goog.fs.Entry.prototype.remove = function() {};
/**
* Gets the parent directory.
*
* @return {!goog.async.Deferred} The deferred {@link goog.fs.DirectoryEntry}.
* If an error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.Entry.prototype.getParent = function() {};
/**
* A directory in a local FileSystem.
*
* @interface
* @extends {goog.fs.Entry}
*/
goog.fs.DirectoryEntry = function() {};
/**
* Behaviors for getting files and directories.
* @enum {number}
*/
goog.fs.DirectoryEntry.Behavior = {
/**
* Get the file if it exists, error out if it doesn't.
*/
DEFAULT: 1,
/**
* Get the file if it exists, create it if it doesn't.
*/
CREATE: 2,
/**
* Error out if the file exists, create it if it doesn't.
*/
CREATE_EXCLUSIVE: 3
};
/**
* Get a file in the directory.
*
* @param {string} path The path to the file, relative to this directory.
* @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
* handling an existing file, or the lack thereof.
* @return {!goog.async.Deferred} The deferred {@link goog.fs.FileEntry}. If an
* error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.DirectoryEntry.prototype.getFile = function(path, opt_behavior) {};
/**
* Get a directory within this directory.
*
* @param {string} path The path to the directory, relative to this directory.
* @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
* handling an existing directory, or the lack thereof.
* @return {!goog.async.Deferred} The deferred {@link goog.fs.DirectoryEntry}.
* If an error occurs, the errback is called a {@link goog.fs.Error}.
*/
goog.fs.DirectoryEntry.prototype.getDirectory = function(path, opt_behavior) {};
/**
* Opens the directory for the specified path, creating the directory and any
* intermediate directories as necessary.
*
* @param {string} path The directory path to create. May be absolute or
* relative to the current directory. The parent directory ".." and current
* directory "." are supported.
* @return {!goog.async.Deferred} A deferred {@link goog.fs.DirectoryEntry} for
* the requested path. If an error occurs, the errback is called with a
* {@link goog.fs.Error}.
*/
goog.fs.DirectoryEntry.prototype.createPath = function(path) {};
/**
* Gets a list of all entries in this directory.
*
* @return {!goog.async.Deferred} The deferred list of {@link goog.fs.Entry}
* results. If an error occurs, the errback is called with a
* {@link goog.fs.Error}.
*/
goog.fs.DirectoryEntry.prototype.listDirectory = function() {};
/**
* Removes this directory and all its contents.
*
* @return {!goog.async.Deferred} A deferred object. If the removal succeeds,
* the callback is called with true. If an error occurs, the errback is
* called a {@link goog.fs.Error}.
*/
goog.fs.DirectoryEntry.prototype.removeRecursively = function() {};
/**
* A file in a local filesystem.
*
* @interface
* @extends {goog.fs.Entry}
*/
goog.fs.FileEntry = function() {};
/**
* Create a writer for writing to the file.
*
* @return {!goog.async.Deferred<!goog.fs.FileWriter>} If an error occurs, the
* errback is called with a {@link goog.fs.Error}.
*/
goog.fs.FileEntry.prototype.createWriter = function() {};
/**
* Get the file contents as a File blob.
*
* @return {!goog.async.Deferred<!File>} If an error occurs, the errback is
* called with a {@link goog.fs.Error}.
*/
goog.fs.FileEntry.prototype.file = function() {};
@@ -0,0 +1,405 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Concrete implementations of the
* goog.fs.DirectoryEntry, and goog.fs.FileEntry interfaces.
*/
goog.provide('goog.fs.DirectoryEntryImpl');
goog.provide('goog.fs.EntryImpl');
goog.provide('goog.fs.FileEntryImpl');
goog.require('goog.array');
goog.require('goog.async.Deferred');
goog.require('goog.fs.DirectoryEntry');
goog.require('goog.fs.Entry');
goog.require('goog.fs.Error');
goog.require('goog.fs.FileEntry');
goog.require('goog.fs.FileWriter');
goog.require('goog.functions');
goog.require('goog.string');
/**
* Base class for concrete implementations of goog.fs.Entry.
* @param {!goog.fs.FileSystem} fs The wrapped filesystem.
* @param {!Entry} entry The underlying Entry object.
* @constructor
* @implements {goog.fs.Entry}
*/
goog.fs.EntryImpl = function(fs, entry) {
/**
* The wrapped filesystem.
*
* @type {!goog.fs.FileSystem}
* @private
*/
this.fs_ = fs;
/**
* The underlying Entry object.
*
* @type {!Entry}
* @private
*/
this.entry_ = entry;
};
/** @override */
goog.fs.EntryImpl.prototype.isFile = function() {
return this.entry_.isFile;
};
/** @override */
goog.fs.EntryImpl.prototype.isDirectory = function() {
return this.entry_.isDirectory;
};
/** @override */
goog.fs.EntryImpl.prototype.getName = function() {
return this.entry_.name;
};
/** @override */
goog.fs.EntryImpl.prototype.getFullPath = function() {
return this.entry_.fullPath;
};
/** @override */
goog.fs.EntryImpl.prototype.getFileSystem = function() {
return this.fs_;
};
/** @override */
goog.fs.EntryImpl.prototype.getLastModified = function() {
return this.getMetadata().addCallback(function(metadata) {
return metadata.modificationTime;
});
};
/** @override */
goog.fs.EntryImpl.prototype.getMetadata = function() {
var d = new goog.async.Deferred();
this.entry_.getMetadata(
function(metadata) { d.callback(metadata); },
goog.bind(function(err) {
var msg = 'retrieving metadata for ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/** @override */
goog.fs.EntryImpl.prototype.moveTo = function(parent, opt_newName) {
var d = new goog.async.Deferred();
this.entry_.moveTo(
parent.dir_, opt_newName,
goog.bind(function(entry) { d.callback(this.wrapEntry(entry)); }, this),
goog.bind(function(err) {
var msg = 'moving ' + this.getFullPath() + ' into ' +
parent.getFullPath() +
(opt_newName ? ', renaming to ' + opt_newName : '');
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/** @override */
goog.fs.EntryImpl.prototype.copyTo = function(parent, opt_newName) {
var d = new goog.async.Deferred();
this.entry_.copyTo(
parent.dir_, opt_newName,
goog.bind(function(entry) { d.callback(this.wrapEntry(entry)); }, this),
goog.bind(function(err) {
var msg = 'copying ' + this.getFullPath() + ' into ' +
parent.getFullPath() +
(opt_newName ? ', renaming to ' + opt_newName : '');
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/** @override */
goog.fs.EntryImpl.prototype.wrapEntry = function(entry) {
return entry.isFile ?
new goog.fs.FileEntryImpl(this.fs_, /** @type {!FileEntry} */ (entry)) :
new goog.fs.DirectoryEntryImpl(
this.fs_, /** @type {!DirectoryEntry} */ (entry));
};
/** @override */
goog.fs.EntryImpl.prototype.toUrl = function(opt_mimeType) {
return this.entry_.toURL(opt_mimeType);
};
/** @override */
goog.fs.EntryImpl.prototype.toUri = goog.fs.EntryImpl.prototype.toUrl;
/** @override */
goog.fs.EntryImpl.prototype.remove = function() {
var d = new goog.async.Deferred();
this.entry_.remove(
goog.bind(d.callback, d, true /* result */),
goog.bind(function(err) {
var msg = 'removing ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/** @override */
goog.fs.EntryImpl.prototype.getParent = function() {
var d = new goog.async.Deferred();
this.entry_.getParent(
goog.bind(function(parent) {
d.callback(new goog.fs.DirectoryEntryImpl(this.fs_, parent));
}, this),
goog.bind(function(err) {
var msg = 'getting parent of ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/**
* A directory in a local FileSystem.
*
* This should not be instantiated directly. Instead, it should be accessed via
* {@link goog.fs.FileSystem#getRoot} or
* {@link goog.fs.DirectoryEntry#getDirectoryEntry}.
*
* @param {!goog.fs.FileSystem} fs The wrapped filesystem.
* @param {!DirectoryEntry} dir The underlying DirectoryEntry object.
* @constructor
* @extends {goog.fs.EntryImpl}
* @implements {goog.fs.DirectoryEntry}
* @final
*/
goog.fs.DirectoryEntryImpl = function(fs, dir) {
goog.fs.DirectoryEntryImpl.base(this, 'constructor', fs, dir);
/**
* The underlying DirectoryEntry object.
*
* @type {!DirectoryEntry}
* @private
*/
this.dir_ = dir;
};
goog.inherits(goog.fs.DirectoryEntryImpl, goog.fs.EntryImpl);
/** @override */
goog.fs.DirectoryEntryImpl.prototype.getFile = function(path, opt_behavior) {
var d = new goog.async.Deferred();
this.dir_.getFile(
path, this.getOptions_(opt_behavior),
goog.bind(function(entry) {
d.callback(new goog.fs.FileEntryImpl(this.fs_, entry));
}, this),
goog.bind(function(err) {
var msg = 'loading file ' + path + ' from ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/** @override */
goog.fs.DirectoryEntryImpl.prototype.getDirectory =
function(path, opt_behavior) {
var d = new goog.async.Deferred();
this.dir_.getDirectory(
path, this.getOptions_(opt_behavior),
goog.bind(function(entry) {
d.callback(new goog.fs.DirectoryEntryImpl(this.fs_, entry));
}, this),
goog.bind(function(err) {
var msg = 'loading directory ' + path + ' from ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/** @override */
goog.fs.DirectoryEntryImpl.prototype.createPath = function(path) {
// If the path begins at the root, reinvoke createPath on the root directory.
if (goog.string.startsWith(path, '/')) {
var root = this.getFileSystem().getRoot();
if (this.getFullPath() != root.getFullPath()) {
return root.createPath(path);
}
}
// Filter out any empty path components caused by '//' or a leading slash.
var parts = goog.array.filter(path.split('/'), goog.functions.identity);
var existed = [];
/**
* @param {goog.fs.DirectoryEntryImpl} dir
* @return {!goog.async.Deferred}
*/
function getNextDirectory(dir) {
if (!parts.length) {
return goog.async.Deferred.succeed(dir);
}
var def;
var nextDir = parts.shift();
if (nextDir == '..') {
def = dir.getParent();
} else if (nextDir == '.') {
def = goog.async.Deferred.succeed(dir);
} else {
def = dir.getDirectory(nextDir, goog.fs.DirectoryEntry.Behavior.CREATE);
}
return def.addCallback(getNextDirectory);
}
return getNextDirectory(this);
};
/** @override */
goog.fs.DirectoryEntryImpl.prototype.listDirectory = function() {
var d = new goog.async.Deferred();
var reader = this.dir_.createReader();
var results = [];
var errorCallback = goog.bind(function(err) {
var msg = 'listing directory ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this);
var successCallback = goog.bind(function(entries) {
if (entries.length) {
for (var i = 0, entry; entry = entries[i]; i++) {
results.push(this.wrapEntry(entry));
}
reader.readEntries(successCallback, errorCallback);
} else {
d.callback(results);
}
}, this);
reader.readEntries(successCallback, errorCallback);
return d;
};
/** @override */
goog.fs.DirectoryEntryImpl.prototype.removeRecursively = function() {
var d = new goog.async.Deferred();
this.dir_.removeRecursively(
goog.bind(d.callback, d, true /* result */),
goog.bind(function(err) {
var msg = 'removing ' + this.getFullPath() + ' recursively';
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/**
* Converts a value in the Behavior enum into an options object expected by the
* File API.
*
* @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
* existing files.
* @return {!Object<boolean>} The options object expected by the File API.
* @private
*/
goog.fs.DirectoryEntryImpl.prototype.getOptions_ = function(opt_behavior) {
if (opt_behavior == goog.fs.DirectoryEntry.Behavior.CREATE) {
return {'create': true};
} else if (opt_behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE) {
return {'create': true, 'exclusive': true};
} else {
return {};
}
};
/**
* A file in a local filesystem.
*
* This should not be instantiated directly. Instead, it should be accessed via
* {@link goog.fs.DirectoryEntry#getFile}.
*
* @param {!goog.fs.FileSystem} fs The wrapped filesystem.
* @param {!FileEntry} file The underlying FileEntry object.
* @constructor
* @extends {goog.fs.EntryImpl}
* @implements {goog.fs.FileEntry}
* @final
*/
goog.fs.FileEntryImpl = function(fs, file) {
goog.fs.FileEntryImpl.base(this, 'constructor', fs, file);
/**
* The underlying FileEntry object.
*
* @type {!FileEntry}
* @private
*/
this.file_ = file;
};
goog.inherits(goog.fs.FileEntryImpl, goog.fs.EntryImpl);
/** @override */
goog.fs.FileEntryImpl.prototype.createWriter = function() {
var d = new goog.async.Deferred();
this.file_.createWriter(
function(w) { d.callback(new goog.fs.FileWriter(w)); },
goog.bind(function(err) {
var msg = 'creating writer for ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
/** @override */
goog.fs.FileEntryImpl.prototype.file = function() {
var d = new goog.async.Deferred();
this.file_.file(
function(f) { d.callback(f); },
goog.bind(function(err) {
var msg = 'getting file for ' + this.getFullPath();
d.errback(new goog.fs.Error(err, msg));
}, this));
return d;
};
@@ -0,0 +1,181 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A wrapper for the HTML5 FileError object.
*
*/
goog.provide('goog.fs.Error');
goog.provide('goog.fs.Error.ErrorCode');
goog.require('goog.debug.Error');
goog.require('goog.object');
goog.require('goog.string');
/**
* A filesystem error. Since the filesystem API is asynchronous, stack traces
* are less useful for identifying where errors come from, so this includes a
* large amount of metadata in the message.
*
* @param {!DOMError} error
* @param {string} action The action being undertaken when the error was raised.
* @constructor
* @extends {goog.debug.Error}
* @final
*/
goog.fs.Error = function(error, action) {
/** @type {string} */
this.name;
/**
* @type {goog.fs.Error.ErrorCode}
* @deprecated Use the 'name' or 'message' field instead.
*/
this.code;
if (goog.isDef(error.name)) {
this.name = error.name;
// TODO(user): Remove warning suppression after JSCompiler stops
// firing a spurious warning here.
/** @suppress {deprecated} */
this.code = goog.fs.Error.getCodeFromName_(error.name);
} else {
this.code = error.code;
this.name = goog.fs.Error.getNameFromCode_(error.code);
}
goog.fs.Error.base(this, 'constructor',
goog.string.subs('%s %s', this.name, action));
};
goog.inherits(goog.fs.Error, goog.debug.Error);
/**
* Names of errors that may be thrown by the File API, the File System API, or
* the File Writer API.
*
* @see http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException
* @see http://www.w3.org/TR/file-system-api/#definitions
* @see http://dev.w3.org/2009/dap/file-system/file-writer.html#definitions
* @enum {string}
*/
goog.fs.Error.ErrorName = {
ABORT: 'AbortError',
ENCODING: 'EncodingError',
INVALID_MODIFICATION: 'InvalidModificationError',
INVALID_STATE: 'InvalidStateError',
NOT_FOUND: 'NotFoundError',
NOT_READABLE: 'NotReadableError',
NO_MODIFICATION_ALLOWED: 'NoModificationAllowedError',
PATH_EXISTS: 'PathExistsError',
QUOTA_EXCEEDED: 'QuotaExceededError',
SECURITY: 'SecurityError',
SYNTAX: 'SyntaxError',
TYPE_MISMATCH: 'TypeMismatchError'
};
/**
* Error codes for file errors.
* @see http://www.w3.org/TR/file-system-api/#idl-def-FileException
*
* @enum {number}
* @deprecated Use the 'name' or 'message' attribute instead.
*/
goog.fs.Error.ErrorCode = {
NOT_FOUND: 1,
SECURITY: 2,
ABORT: 3,
NOT_READABLE: 4,
ENCODING: 5,
NO_MODIFICATION_ALLOWED: 6,
INVALID_STATE: 7,
SYNTAX: 8,
INVALID_MODIFICATION: 9,
QUOTA_EXCEEDED: 10,
TYPE_MISMATCH: 11,
PATH_EXISTS: 12
};
/**
* @param {goog.fs.Error.ErrorCode} code
* @return {string} name
* @private
*/
goog.fs.Error.getNameFromCode_ = function(code) {
var name = goog.object.findKey(goog.fs.Error.NameToCodeMap_, function(c) {
return code == c;
});
if (!goog.isDef(name)) {
throw new Error('Invalid code: ' + code);
}
return name;
};
/**
* Returns the code that corresponds to the given name.
* @param {string} name
* @return {goog.fs.Error.ErrorCode} code
* @private
*/
goog.fs.Error.getCodeFromName_ = function(name) {
return goog.fs.Error.NameToCodeMap_[name];
};
/**
* Mapping from error names to values from the ErrorCode enum.
* @see http://www.w3.org/TR/file-system-api/#definitions.
* @private {!Object<string, goog.fs.Error.ErrorCode>}
*/
goog.fs.Error.NameToCodeMap_ = goog.object.create(
goog.fs.Error.ErrorName.ABORT,
goog.fs.Error.ErrorCode.ABORT,
goog.fs.Error.ErrorName.ENCODING,
goog.fs.Error.ErrorCode.ENCODING,
goog.fs.Error.ErrorName.INVALID_MODIFICATION,
goog.fs.Error.ErrorCode.INVALID_MODIFICATION,
goog.fs.Error.ErrorName.INVALID_STATE,
goog.fs.Error.ErrorCode.INVALID_STATE,
goog.fs.Error.ErrorName.NOT_FOUND,
goog.fs.Error.ErrorCode.NOT_FOUND,
goog.fs.Error.ErrorName.NOT_READABLE,
goog.fs.Error.ErrorCode.NOT_READABLE,
goog.fs.Error.ErrorName.NO_MODIFICATION_ALLOWED,
goog.fs.Error.ErrorCode.NO_MODIFICATION_ALLOWED,
goog.fs.Error.ErrorName.PATH_EXISTS,
goog.fs.Error.ErrorCode.PATH_EXISTS,
goog.fs.Error.ErrorName.QUOTA_EXCEEDED,
goog.fs.Error.ErrorCode.QUOTA_EXCEEDED,
goog.fs.Error.ErrorName.SECURITY,
goog.fs.Error.ErrorCode.SECURITY,
goog.fs.Error.ErrorName.SYNTAX,
goog.fs.Error.ErrorCode.SYNTAX,
goog.fs.Error.ErrorName.TYPE_MISMATCH,
goog.fs.Error.ErrorCode.TYPE_MISMATCH);
@@ -0,0 +1,288 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A wrapper for the HTML5 FileReader object.
*
*/
goog.provide('goog.fs.FileReader');
goog.provide('goog.fs.FileReader.EventType');
goog.provide('goog.fs.FileReader.ReadyState');
goog.require('goog.async.Deferred');
goog.require('goog.events.EventTarget');
goog.require('goog.fs.Error');
goog.require('goog.fs.ProgressEvent');
/**
* An object for monitoring the reading of files. This emits ProgressEvents of
* the types listed in {@link goog.fs.FileReader.EventType}.
*
* @constructor
* @extends {goog.events.EventTarget}
* @final
*/
goog.fs.FileReader = function() {
goog.fs.FileReader.base(this, 'constructor');
/**
* The underlying FileReader object.
*
* @type {!FileReader}
* @private
*/
this.reader_ = new FileReader();
this.reader_.onloadstart = goog.bind(this.dispatchProgressEvent_, this);
this.reader_.onprogress = goog.bind(this.dispatchProgressEvent_, this);
this.reader_.onload = goog.bind(this.dispatchProgressEvent_, this);
this.reader_.onabort = goog.bind(this.dispatchProgressEvent_, this);
this.reader_.onerror = goog.bind(this.dispatchProgressEvent_, this);
this.reader_.onloadend = goog.bind(this.dispatchProgressEvent_, this);
};
goog.inherits(goog.fs.FileReader, goog.events.EventTarget);
/**
* Possible states for a FileReader.
*
* @enum {number}
*/
goog.fs.FileReader.ReadyState = {
/**
* The object has been constructed, but there is no pending read.
*/
INIT: 0,
/**
* Data is being read.
*/
LOADING: 1,
/**
* The data has been read from the file, the read was aborted, or an error
* occurred.
*/
DONE: 2
};
/**
* Events emitted by a FileReader.
*
* @enum {string}
*/
goog.fs.FileReader.EventType = {
/**
* Emitted when the reading begins. readyState will be LOADING.
*/
LOAD_START: 'loadstart',
/**
* Emitted when progress has been made in reading the file. readyState will be
* LOADING.
*/
PROGRESS: 'progress',
/**
* Emitted when the data has been successfully read. readyState will be
* LOADING.
*/
LOAD: 'load',
/**
* Emitted when the reading has been aborted. readyState will be LOADING.
*/
ABORT: 'abort',
/**
* Emitted when an error is encountered or the reading has been aborted.
* readyState will be LOADING.
*/
ERROR: 'error',
/**
* Emitted when the reading is finished, whether successfully or not.
* readyState will be DONE.
*/
LOAD_END: 'loadend'
};
/**
* Abort the reading of the file.
*/
goog.fs.FileReader.prototype.abort = function() {
try {
this.reader_.abort();
} catch (e) {
throw new goog.fs.Error(e, 'aborting read');
}
};
/**
* @return {goog.fs.FileReader.ReadyState} The current state of the FileReader.
*/
goog.fs.FileReader.prototype.getReadyState = function() {
return /** @type {goog.fs.FileReader.ReadyState} */ (this.reader_.readyState);
};
/**
* @return {*} The result of the file read.
*/
goog.fs.FileReader.prototype.getResult = function() {
return this.reader_.result;
};
/**
* @return {goog.fs.Error} The error encountered while reading, if any.
*/
goog.fs.FileReader.prototype.getError = function() {
return this.reader_.error &&
new goog.fs.Error(this.reader_.error, 'reading file');
};
/**
* Wrap a progress event emitted by the underlying file reader and re-emit it.
*
* @param {!ProgressEvent} event The underlying event.
* @private
*/
goog.fs.FileReader.prototype.dispatchProgressEvent_ = function(event) {
this.dispatchEvent(new goog.fs.ProgressEvent(event, this));
};
/** @override */
goog.fs.FileReader.prototype.disposeInternal = function() {
goog.fs.FileReader.base(this, 'disposeInternal');
delete this.reader_;
};
/**
* Starts reading a blob as a binary string.
* @param {!Blob} blob The blob to read.
*/
goog.fs.FileReader.prototype.readAsBinaryString = function(blob) {
this.reader_.readAsBinaryString(blob);
};
/**
* Reads a blob as a binary string.
* @param {!Blob} blob The blob to read.
* @return {!goog.async.Deferred} The deferred Blob contents as a binary string.
* If an error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.FileReader.readAsBinaryString = function(blob) {
var reader = new goog.fs.FileReader();
var d = goog.fs.FileReader.createDeferred_(reader);
reader.readAsBinaryString(blob);
return d;
};
/**
* Starts reading a blob as an array buffer.
* @param {!Blob} blob The blob to read.
*/
goog.fs.FileReader.prototype.readAsArrayBuffer = function(blob) {
this.reader_.readAsArrayBuffer(blob);
};
/**
* Reads a blob as an array buffer.
* @param {!Blob} blob The blob to read.
* @return {!goog.async.Deferred} The deferred Blob contents as an array buffer.
* If an error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.FileReader.readAsArrayBuffer = function(blob) {
var reader = new goog.fs.FileReader();
var d = goog.fs.FileReader.createDeferred_(reader);
reader.readAsArrayBuffer(blob);
return d;
};
/**
* Starts reading a blob as text.
* @param {!Blob} blob The blob to read.
* @param {string=} opt_encoding The name of the encoding to use.
*/
goog.fs.FileReader.prototype.readAsText = function(blob, opt_encoding) {
this.reader_.readAsText(blob, opt_encoding);
};
/**
* Reads a blob as text.
* @param {!Blob} blob The blob to read.
* @param {string=} opt_encoding The name of the encoding to use.
* @return {!goog.async.Deferred} The deferred Blob contents as text.
* If an error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.FileReader.readAsText = function(blob, opt_encoding) {
var reader = new goog.fs.FileReader();
var d = goog.fs.FileReader.createDeferred_(reader);
reader.readAsText(blob, opt_encoding);
return d;
};
/**
* Starts reading a blob as a data URL.
* @param {!Blob} blob The blob to read.
*/
goog.fs.FileReader.prototype.readAsDataUrl = function(blob) {
this.reader_.readAsDataURL(blob);
};
/**
* Reads a blob as a data URL.
* @param {!Blob} blob The blob to read.
* @return {!goog.async.Deferred} The deferred Blob contents as a data URL.
* If an error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.FileReader.readAsDataUrl = function(blob) {
var reader = new goog.fs.FileReader();
var d = goog.fs.FileReader.createDeferred_(reader);
reader.readAsDataUrl(blob);
return d;
};
/**
* Creates a new deferred object for the results of a read method.
* @param {goog.fs.FileReader} reader The reader to create a deferred for.
* @return {!goog.async.Deferred} The deferred results.
* @private
*/
goog.fs.FileReader.createDeferred_ = function(reader) {
var deferred = new goog.async.Deferred();
reader.listen(goog.fs.FileReader.EventType.LOAD_END,
goog.partial(function(d, r, e) {
var result = r.getResult();
var error = r.getError();
if (result != null && !error) {
d.callback(result);
} else {
d.errback(error);
}
r.dispose();
}, deferred, reader));
return deferred;
};
@@ -0,0 +1,166 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A wrapper for the HTML5 FileSaver object.
*
*/
goog.provide('goog.fs.FileSaver');
goog.provide('goog.fs.FileSaver.EventType');
goog.provide('goog.fs.FileSaver.ReadyState');
goog.require('goog.events.EventTarget');
goog.require('goog.fs.Error');
goog.require('goog.fs.ProgressEvent');
/**
* An object for monitoring the saving of files. This emits ProgressEvents of
* the types listed in {@link goog.fs.FileSaver.EventType}.
*
* This should not be instantiated directly. Instead, its subclass
* {@link goog.fs.FileWriter} should be accessed via
* {@link goog.fs.FileEntry#createWriter}.
*
* @param {!FileSaver} fileSaver The underlying FileSaver object.
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.fs.FileSaver = function(fileSaver) {
goog.fs.FileSaver.base(this, 'constructor');
/**
* The underlying FileSaver object.
*
* @type {!FileSaver}
* @private
*/
this.saver_ = fileSaver;
this.saver_.onwritestart = goog.bind(this.dispatchProgressEvent_, this);
this.saver_.onprogress = goog.bind(this.dispatchProgressEvent_, this);
this.saver_.onwrite = goog.bind(this.dispatchProgressEvent_, this);
this.saver_.onabort = goog.bind(this.dispatchProgressEvent_, this);
this.saver_.onerror = goog.bind(this.dispatchProgressEvent_, this);
this.saver_.onwriteend = goog.bind(this.dispatchProgressEvent_, this);
};
goog.inherits(goog.fs.FileSaver, goog.events.EventTarget);
/**
* Possible states for a FileSaver.
*
* @enum {number}
*/
goog.fs.FileSaver.ReadyState = {
/**
* The object has been constructed, but there is no pending write.
*/
INIT: 0,
/**
* Data is being written.
*/
WRITING: 1,
/**
* The data has been written to the file, the write was aborted, or an error
* occurred.
*/
DONE: 2
};
/**
* Events emitted by a FileSaver.
*
* @enum {string}
*/
goog.fs.FileSaver.EventType = {
/**
* Emitted when the writing begins. readyState will be WRITING.
*/
WRITE_START: 'writestart',
/**
* Emitted when progress has been made in saving the file. readyState will be
* WRITING.
*/
PROGRESS: 'progress',
/**
* Emitted when the data has been successfully written. readyState will be
* WRITING.
*/
WRITE: 'write',
/**
* Emitted when the writing has been aborted. readyState will be WRITING.
*/
ABORT: 'abort',
/**
* Emitted when an error is encountered or the writing has been aborted.
* readyState will be WRITING.
*/
ERROR: 'error',
/**
* Emitted when the writing is finished, whether successfully or not.
* readyState will be DONE.
*/
WRITE_END: 'writeend'
};
/**
* Abort the writing of the file.
*/
goog.fs.FileSaver.prototype.abort = function() {
try {
this.saver_.abort();
} catch (e) {
throw new goog.fs.Error(e, 'aborting save');
}
};
/**
* @return {goog.fs.FileSaver.ReadyState} The current state of the FileSaver.
*/
goog.fs.FileSaver.prototype.getReadyState = function() {
return /** @type {goog.fs.FileSaver.ReadyState} */ (this.saver_.readyState);
};
/**
* @return {goog.fs.Error} The error encountered while writing, if any.
*/
goog.fs.FileSaver.prototype.getError = function() {
return this.saver_.error &&
new goog.fs.Error(this.saver_.error, 'saving file');
};
/**
* Wrap a progress event emitted by the underlying file saver and re-emit it.
*
* @param {!ProgressEvent} event The underlying event.
* @private
*/
goog.fs.FileSaver.prototype.dispatchProgressEvent_ = function(event) {
this.dispatchEvent(new goog.fs.ProgressEvent(event, this));
};
/** @override */
goog.fs.FileSaver.prototype.disposeInternal = function() {
delete this.saver_;
goog.fs.FileSaver.base(this, 'disposeInternal');
};
@@ -0,0 +1,41 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A wrapper for the HTML5 FileSystem object.
*
*/
goog.provide('goog.fs.FileSystem');
/**
* A local filesystem.
*
* @interface
*/
goog.fs.FileSystem = function() {};
/**
* @return {string} The name of the filesystem.
*/
goog.fs.FileSystem.prototype.getName = function() {};
/**
* @return {!goog.fs.DirectoryEntry} The root directory of the filesystem.
*/
goog.fs.FileSystem.prototype.getRoot = function() {};
@@ -0,0 +1,65 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Concrete implementation of the goog.fs.FileSystem interface
* using an HTML FileSystem object.
*/
goog.provide('goog.fs.FileSystemImpl');
goog.require('goog.fs.DirectoryEntryImpl');
goog.require('goog.fs.FileSystem');
/**
* A local filesystem.
*
* This shouldn't be instantiated directly. Instead, it should be accessed via
* {@link goog.fs.getTemporary} or {@link goog.fs.getPersistent}.
*
* @param {!FileSystem} fs The underlying FileSystem object.
* @constructor
* @implements {goog.fs.FileSystem}
* @final
*/
goog.fs.FileSystemImpl = function(fs) {
/**
* The underlying FileSystem object.
*
* @type {!FileSystem}
* @private
*/
this.fs_ = fs;
};
/** @override */
goog.fs.FileSystemImpl.prototype.getName = function() {
return this.fs_.name;
};
/** @override */
goog.fs.FileSystemImpl.prototype.getRoot = function() {
return new goog.fs.DirectoryEntryImpl(this, this.fs_.root);
};
/**
* @return {!FileSystem} The underlying FileSystem object.
*/
goog.fs.FileSystemImpl.prototype.getBrowserFileSystem = function() {
return this.fs_;
};
@@ -0,0 +1,111 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A wrapper for the HTML5 FileWriter object.
*
* When adding or modifying functionality in this namespace, be sure to update
* the mock counterparts in goog.testing.fs.
*
*/
goog.provide('goog.fs.FileWriter');
goog.require('goog.fs.Error');
goog.require('goog.fs.FileSaver');
/**
* An object for monitoring the saving of files, as well as other fine-grained
* writing operations.
*
* This should not be instantiated directly. Instead, it should be accessed via
* {@link goog.fs.FileEntry#createWriter}.
*
* @param {!FileWriter} writer The underlying FileWriter object.
* @constructor
* @extends {goog.fs.FileSaver}
* @final
*/
goog.fs.FileWriter = function(writer) {
goog.fs.FileWriter.base(this, 'constructor', writer);
/**
* The underlying FileWriter object.
*
* @type {!FileWriter}
* @private
*/
this.writer_ = writer;
};
goog.inherits(goog.fs.FileWriter, goog.fs.FileSaver);
/**
* @return {number} The byte offset at which the next write will occur.
*/
goog.fs.FileWriter.prototype.getPosition = function() {
return this.writer_.position;
};
/**
* @return {number} The length of the file.
*/
goog.fs.FileWriter.prototype.getLength = function() {
return this.writer_.length;
};
/**
* Write data to the file.
*
* @param {!Blob} blob The data to write.
*/
goog.fs.FileWriter.prototype.write = function(blob) {
try {
this.writer_.write(blob);
} catch (e) {
throw new goog.fs.Error(e, 'writing file');
}
};
/**
* Set the file position at which the next write will occur.
*
* @param {number} offset An absolute byte offset into the file.
*/
goog.fs.FileWriter.prototype.seek = function(offset) {
try {
this.writer_.seek(offset);
} catch (e) {
throw new goog.fs.Error(e, 'seeking in file');
}
};
/**
* Changes the length of the file to that specified.
*
* @param {number} size The new size of the file, in bytes.
*/
goog.fs.FileWriter.prototype.truncate = function(size) {
try {
this.writer_.truncate(size);
} catch (e) {
throw new goog.fs.Error(e, 'truncating file');
}
};
@@ -0,0 +1,319 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrappers for the HTML5 File API. These wrappers closely mirror
* the underlying APIs, but use Closure-style events and Deferred return values.
* Their existence also makes it possible to mock the FileSystem API for testing
* in browsers that don't support it natively.
*
* When adding public functions to anything under this namespace, be sure to add
* its mock counterpart to goog.testing.fs.
*
*/
goog.provide('goog.fs');
goog.require('goog.array');
goog.require('goog.async.Deferred');
goog.require('goog.fs.Error');
goog.require('goog.fs.FileReader');
goog.require('goog.fs.FileSystemImpl');
goog.require('goog.userAgent');
/**
* Get a wrapped FileSystem object.
*
* @param {goog.fs.FileSystemType_} type The type of the filesystem to get.
* @param {number} size The size requested for the filesystem, in bytes.
* @return {!goog.async.Deferred} The deferred {@link goog.fs.FileSystem}. If an
* error occurs, the errback is called with a {@link goog.fs.Error}.
* @private
*/
goog.fs.get_ = function(type, size) {
var requestFileSystem = goog.global.requestFileSystem ||
goog.global.webkitRequestFileSystem;
if (!goog.isFunction(requestFileSystem)) {
return goog.async.Deferred.fail(new Error('File API unsupported'));
}
var d = new goog.async.Deferred();
requestFileSystem(type, size, function(fs) {
d.callback(new goog.fs.FileSystemImpl(fs));
}, function(err) {
d.errback(new goog.fs.Error(err, 'requesting filesystem'));
});
return d;
};
/**
* The two types of filesystem.
*
* @enum {number}
* @private
*/
goog.fs.FileSystemType_ = {
/**
* A temporary filesystem may be deleted by the user agent at its discretion.
*/
TEMPORARY: 0,
/**
* A persistent filesystem will never be deleted without the user's or
* application's authorization.
*/
PERSISTENT: 1
};
/**
* Returns a temporary FileSystem object. A temporary filesystem may be deleted
* by the user agent at its discretion.
*
* @param {number} size The size requested for the filesystem, in bytes.
* @return {!goog.async.Deferred} The deferred {@link goog.fs.FileSystem}. If an
* error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.getTemporary = function(size) {
return goog.fs.get_(goog.fs.FileSystemType_.TEMPORARY, size);
};
/**
* Returns a persistent FileSystem object. A persistent filesystem will never be
* deleted without the user's or application's authorization.
*
* @param {number} size The size requested for the filesystem, in bytes.
* @return {!goog.async.Deferred} The deferred {@link goog.fs.FileSystem}. If an
* error occurs, the errback is called with a {@link goog.fs.Error}.
*/
goog.fs.getPersistent = function(size) {
return goog.fs.get_(goog.fs.FileSystemType_.PERSISTENT, size);
};
/**
* Creates a blob URL for a blob object.
* Throws an error if the browser does not support Object Urls.
*
* @param {!Blob} blob The object for which to create the URL.
* @return {string} The URL for the object.
*/
goog.fs.createObjectUrl = function(blob) {
return goog.fs.getUrlObject_().createObjectURL(blob);
};
/**
* Revokes a URL created by {@link goog.fs.createObjectUrl}.
* Throws an error if the browser does not support Object Urls.
*
* @param {string} url The URL to revoke.
*/
goog.fs.revokeObjectUrl = function(url) {
goog.fs.getUrlObject_().revokeObjectURL(url);
};
/**
* @typedef {{createObjectURL: (function(!Blob): string),
* revokeObjectURL: function(string): void}}
*/
goog.fs.UrlObject_;
/**
* Get the object that has the createObjectURL and revokeObjectURL functions for
* this browser.
*
* @return {goog.fs.UrlObject_} The object for this browser.
* @private
*/
goog.fs.getUrlObject_ = function() {
var urlObject = goog.fs.findUrlObject_();
if (urlObject != null) {
return urlObject;
} else {
throw Error('This browser doesn\'t seem to support blob URLs');
}
};
/**
* Finds the object that has the createObjectURL and revokeObjectURL functions
* for this browser.
*
* @return {?goog.fs.UrlObject_} The object for this browser or null if the
* browser does not support Object Urls.
* @private
*/
goog.fs.findUrlObject_ = function() {
// This is what the spec says to do
// http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL
if (goog.isDef(goog.global.URL) &&
goog.isDef(goog.global.URL.createObjectURL)) {
return /** @type {goog.fs.UrlObject_} */ (goog.global.URL);
// This is what Chrome does (as of 10.0.648.6 dev)
} else if (goog.isDef(goog.global.webkitURL) &&
goog.isDef(goog.global.webkitURL.createObjectURL)) {
return /** @type {goog.fs.UrlObject_} */ (goog.global.webkitURL);
// This is what the spec used to say to do
} else if (goog.isDef(goog.global.createObjectURL)) {
return /** @type {goog.fs.UrlObject_} */ (goog.global);
} else {
return null;
}
};
/**
* Checks whether this browser supports Object Urls. If not, calls to
* createObjectUrl and revokeObjectUrl will result in an error.
*
* @return {boolean} True if this browser supports Object Urls.
*/
goog.fs.browserSupportsObjectUrls = function() {
return goog.fs.findUrlObject_() != null;
};
/**
* Concatenates one or more values together and converts them to a Blob.
*
* @param {...(string|!Blob|!ArrayBuffer)} var_args The values that will make up
* the resulting blob.
* @return {!Blob} The blob.
*/
goog.fs.getBlob = function(var_args) {
var BlobBuilder = goog.global.BlobBuilder || goog.global.WebKitBlobBuilder;
if (goog.isDef(BlobBuilder)) {
var bb = new BlobBuilder();
for (var i = 0; i < arguments.length; i++) {
bb.append(arguments[i]);
}
return bb.getBlob();
} else {
return goog.fs.getBlobWithProperties(goog.array.toArray(arguments));
}
};
/**
* Creates a blob with the given properties.
* See https://developer.mozilla.org/en-US/docs/Web/API/Blob for more details.
*
* @param {Array<string|!Blob>} parts The values that will make up the
* resulting blob.
* @param {string=} opt_type The MIME type of the Blob.
* @param {string=} opt_endings Specifies how strings containing newlines are to
* be written out.
* @return {!Blob} The blob.
*/
goog.fs.getBlobWithProperties = function(parts, opt_type, opt_endings) {
var BlobBuilder = goog.global.BlobBuilder || goog.global.WebKitBlobBuilder;
if (goog.isDef(BlobBuilder)) {
var bb = new BlobBuilder();
for (var i = 0; i < parts.length; i++) {
bb.append(parts[i], opt_endings);
}
return bb.getBlob(opt_type);
} else if (goog.isDef(goog.global.Blob)) {
var properties = {};
if (opt_type) {
properties['type'] = opt_type;
}
if (opt_endings) {
properties['endings'] = opt_endings;
}
return new Blob(parts, properties);
} else {
throw Error('This browser doesn\'t seem to support creating Blobs');
}
};
/**
* Converts a Blob or a File into a string. This should only be used when the
* blob is known to be small.
*
* @param {!Blob} blob The blob to convert.
* @param {string=} opt_encoding The name of the encoding to use.
* @return {!goog.async.Deferred} The deferred string. If an error occurrs, the
* errback is called with a {@link goog.fs.Error}.
* @deprecated Use {@link goog.fs.FileReader.readAsText} instead.
*/
goog.fs.blobToString = function(blob, opt_encoding) {
return goog.fs.FileReader.readAsText(blob, opt_encoding);
};
/**
* Slices the blob. The returned blob contains data from the start byte
* (inclusive) till the end byte (exclusive). Negative indices can be used
* to count bytes from the end of the blob (-1 == blob.size - 1). Indices
* are always clamped to blob range. If end is omitted, all the data till
* the end of the blob is taken.
*
* @param {!Blob} blob The blob to be sliced.
* @param {number} start Index of the starting byte.
* @param {number=} opt_end Index of the ending byte.
* @return {Blob} The blob slice or null if not supported.
*/
goog.fs.sliceBlob = function(blob, start, opt_end) {
if (!goog.isDef(opt_end)) {
opt_end = blob.size;
}
if (blob.webkitSlice) {
// Natively accepts negative indices, clamping to the blob range and
// range end is optional. See http://trac.webkit.org/changeset/83873
return blob.webkitSlice(start, opt_end);
} else if (blob.mozSlice) {
// Natively accepts negative indices, clamping to the blob range and
// range end is optional. See https://developer.mozilla.org/en/DOM/Blob
// and http://hg.mozilla.org/mozilla-central/rev/dae833f4d934
return blob.mozSlice(start, opt_end);
} else if (blob.slice) {
// Old versions of Firefox and Chrome use the original specification.
// Negative indices are not accepted, only range end is clamped and
// range end specification is obligatory.
// See http://www.w3.org/TR/2009/WD-FileAPI-20091117/
if ((goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('13.0')) ||
(goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('537.1'))) {
if (start < 0) {
start += blob.size;
}
if (start < 0) {
start = 0;
}
if (opt_end < 0) {
opt_end += blob.size;
}
if (opt_end < start) {
opt_end = start;
}
return blob.slice(start, opt_end - start);
}
// IE and the latest versions of Firefox and Chrome use the new
// specification. Natively accepts negative indices, clamping to the blob
// range and range end is optional.
// See http://dev.w3.org/2006/webapi/FileAPI/
return blob.slice(start, opt_end);
}
return null;
};
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2011 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<!--
-->
<head>
<title>
Closure Integration Tests - goog.fs
</title>
<script src="../base.js">
</script>
<script>
goog.require('goog.fsTest');
</script>
</head>
<body>
<div id="closureTestRunnerLog">
</div>
</body>
</html>
@@ -0,0 +1,842 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.fsTest');
goog.setTestOnly('goog.fsTest');
goog.require('goog.array');
goog.require('goog.async.Deferred');
goog.require('goog.async.DeferredList');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.fs');
goog.require('goog.fs.DirectoryEntry');
goog.require('goog.fs.Error');
goog.require('goog.fs.FileReader');
goog.require('goog.fs.FileSaver');
goog.require('goog.string');
goog.require('goog.testing.AsyncTestCase');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.jsunit');
var TEST_DIR = 'goog-fs-test-dir';
var fsExists = goog.isDef(goog.global.requestFileSystem) ||
goog.isDef(goog.global.webkitRequestFileSystem);
var deferredFs = fsExists ? goog.fs.getTemporary() : null;
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
asyncTestCase.stepTimeout = 5000;
var stubs = new goog.testing.PropertyReplacer();
function setUpPage() {
if (!fsExists) {
return;
}
loadTestDir().addErrback(function(err) {
var msg;
if (err.code == goog.fs.Error.ErrorCode.QUOTA_EXCEEDED) {
msg = err.message + '. If you\'re using Chrome, you probably need to ' +
'pass --unlimited-quota-for-files on the command line.';
} else if (err.code == goog.fs.Error.ErrorCode.SECURITY &&
window.location.href.match(/^file:/)) {
msg = err.message + '. file:// URLs can\'t access the filesystem API.';
} else {
msg = err.message;
}
var body = goog.dom.getDocument().body;
goog.dom.insertSiblingBefore(
goog.dom.createDom('h1', {}, msg), body.childNodes[0]);
});
}
function tearDown() {
if (!fsExists) {
return;
}
loadTestDir().
addCallback(function(dir) { return dir.removeRecursively(); }).
addBoth(continueTesting);
asyncTestCase.waitForAsync('removing filesystem');
}
function testUnavailableTemporaryFilesystem() {
stubs.set(goog.global, 'requestFileSystem', null);
stubs.set(goog.global, 'webkitRequestFileSystem', null);
asyncTestCase.waitForAsync('testUnavailableTemporaryFilesystem');
goog.fs.getTemporary(1024).addErrback(function(e) {
assertEquals('File API unsupported', e.message);
continueTesting();
});
}
function testUnavailablePersistentFilesystem() {
stubs.set(goog.global, 'requestFileSystem', null);
stubs.set(goog.global, 'webkitRequestFileSystem', null);
asyncTestCase.waitForAsync('testUnavailablePersistentFilesystem');
goog.fs.getPersistent(2048).addErrback(function(e) {
assertEquals('File API unsupported', e.message);
continueTesting();
});
}
function testIsFile() {
if (!fsExists) {
return;
}
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(function(fileEntry) {
assertFalse(fileEntry.isDirectory());
assertTrue(fileEntry.isFile());
}).addBoth(continueTesting);
asyncTestCase.waitForAsync('testIsFile');
}
function testIsDirectory() {
if (!fsExists) {
return;
}
loadDirectory('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(function(fileEntry) {
assertTrue(fileEntry.isDirectory());
assertFalse(fileEntry.isFile());
}).addBoth(continueTesting);
asyncTestCase.waitForAsync('testIsDirectory');
}
function testReadFileUtf16() {
if (!fsExists) {
return;
}
var str = 'test content';
var buf = new ArrayBuffer(str.length * 2);
var arr = new Uint16Array(buf);
for (var i = 0; i < str.length; i++) {
arr[i] = str.charCodeAt(i);
}
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(goog.partial(writeToFile, arr.buffer)).
addCallback(goog.partial(checkFileContentWithEncoding, str, 'UTF-16')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testReadFile');
}
function testReadFileUtf8() {
if (!fsExists) {
return;
}
var str = 'test content';
var buf = new ArrayBuffer(str.length);
var arr = new Uint8Array(buf);
for (var i = 0; i < str.length; i++) {
arr[i] = str.charCodeAt(i) & 0xff;
}
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(goog.partial(writeToFile, arr.buffer)).
addCallback(goog.partial(checkFileContentWithEncoding, str, 'UTF-8')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testReadFileUtf8');
}
function testReadFileAsArrayBuffer() {
if (!fsExists) {
return;
}
var str = 'test content';
var buf = new ArrayBuffer(str.length);
var arr = new Uint8Array(buf);
for (var i = 0; i < str.length; i++) {
arr[i] = str.charCodeAt(i) & 0xff;
}
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(goog.partial(writeToFile, arr.buffer)).
addCallback(goog.partial(checkFileContentAs, arr.buffer, 'ArrayBuffer',
undefined)).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testReadFileAsArrayBuffer');
}
function testReadFileAsBinaryString() {
if (!fsExists) {
return;
}
var str = 'test content';
var buf = new ArrayBuffer(str.length);
var arr = new Uint8Array(buf);
for (var i = 0; i < str.length; i++) {
arr[i] = str.charCodeAt(i);
}
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(goog.partial(writeToFile, arr.buffer)).
addCallback(goog.partial(checkFileContentAs, str, 'BinaryString',
undefined)).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testReadFileAsArrayBuffer');
}
function testWriteFile() {
if (!fsExists) {
return;
}
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(goog.partial(writeToFile, 'test content')).
addCallback(goog.partial(checkFileContent, 'test content')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testWriteFile');
}
function testRemoveFile() {
if (!fsExists) {
return;
}
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(goog.partial(writeToFile, 'test content')).
addCallback(function(file) { return file.remove(); }).
addCallback(goog.partial(checkFileRemoved, 'test')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testRemoveFile');
}
function testMoveFile() {
if (!fsExists) {
return;
}
var deferredSubdir = loadDirectory(
'subdir', goog.fs.DirectoryEntry.Behavior.CREATE);
var deferredWrittenFile =
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(goog.partial(writeToFile, 'test content'));
goog.async.DeferredList.gatherResults([deferredSubdir, deferredWrittenFile]).
addCallback(splitArgs(function(dir, file) { return file.moveTo(dir); })).
addCallback(goog.partial(checkFileContent, 'test content')).
addCallback(goog.partial(checkFileRemoved, 'test')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testMoveFile');
}
function testCopyFile() {
if (!fsExists) {
return;
}
var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
var deferredSubdir = loadDirectory(
'subdir', goog.fs.DirectoryEntry.Behavior.CREATE);
var deferredWrittenFile = deferredFile.branch().
addCallback(goog.partial(writeToFile, 'test content'));
goog.async.DeferredList.gatherResults([deferredSubdir, deferredWrittenFile]).
addCallback(splitArgs(function(dir, file) { return file.copyTo(dir); })).
addCallback(goog.partial(checkFileContent, 'test content')).
awaitDeferred(deferredFile).
addCallback(goog.partial(checkFileContent, 'test content')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testMoveFile');
}
function testAbortWrite() {
// TODO(nicksantos): This test is broken in newer versions of chrome.
// We don't know why yet.
if (true) return;
if (!fsExists) {
return;
}
var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
deferredFile.branch().
addCallback(goog.partial(startWrite, 'test content')).
addCallback(function(writer) { writer.abort(); }).
addCallback(
goog.partial(waitForEvent, goog.fs.FileSaver.EventType.ABORT)).
awaitDeferred(deferredFile).
addCallback(goog.partial(checkFileContent, '')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testWriteFile');
}
function testSeek() {
if (!fsExists) {
return;
}
var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
deferredFile.branch().
addCallback(goog.partial(writeToFile, 'test content')).
addCallback(function(file) { return file.createWriter(); }).
addCallback(
goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
addCallback(function(writer) {
writer.seek(5);
writer.write(goog.fs.getBlob('stuff and things'));
}).
addCallback(
goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING)).
addCallback(
goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
awaitDeferred(deferredFile).
addCallback(goog.partial(checkFileContent, 'test stuff and things')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testWriteFile');
}
function testTruncate() {
if (!fsExists) {
return;
}
var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
deferredFile.branch().
addCallback(goog.partial(writeToFile, 'test content')).
addCallback(function(file) { return file.createWriter(); }).
addCallback(
goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
addCallback(function(writer) { writer.truncate(4); }).
addCallback(
goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING)).
addCallback(
goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
awaitDeferred(deferredFile).
addCallback(goog.partial(checkFileContent, 'test')).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testWriteFile');
}
function testGetLastModified() {
if (!fsExists) {
return;
}
var now = goog.now();
loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
addCallback(function(entry) {
return entry.getLastModified();
}).addCallback(function(date) {
assertRoughlyEquals('Expected the last modified date to be within ' +
'a few milliseconds of the test start time.',
now, date.getTime(), 2000);
}).addCallback(continueTesting);
asyncTestCase.waitForAsync('testGetLastModified');
}
function testCreatePath() {
if (!fsExists) {
return;
}
loadTestDir().
addCallback(function(testDir) {
return testDir.createPath('foo');
}).
addCallback(function(fooDir) {
assertEquals('/goog-fs-test-dir/foo', fooDir.getFullPath());
return fooDir.createPath('bar/baz/bat');
}).
addCallback(function(batDir) {
assertEquals('/goog-fs-test-dir/foo/bar/baz/bat', batDir.getFullPath());
}).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testCreatePath');
}
function testCreateAbsolutePath() {
if (!fsExists) {
return;
}
loadTestDir().
addCallback(function(testDir) {
return testDir.createPath('/' + TEST_DIR + '/fee/fi/fo/fum');
}).
addCallback(function(absDir) {
assertEquals('/goog-fs-test-dir/fee/fi/fo/fum', absDir.getFullPath());
}).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testCreateAbsolutePath');
}
function testCreateRelativePath() {
if (!fsExists) {
return;
}
loadTestDir().
addCallback(function(dir) {
return dir.createPath('../' + TEST_DIR + '/dir');
}).
addCallback(function(relDir) {
assertEquals('/goog-fs-test-dir/dir', relDir.getFullPath());
return relDir.createPath('.');
}).
addCallback(function(sameDir) {
assertEquals('/goog-fs-test-dir/dir', sameDir.getFullPath());
return sameDir.createPath('./././.');
}).
addCallback(function(reallySameDir) {
assertEquals('/goog-fs-test-dir/dir', reallySameDir.getFullPath());
return reallySameDir.createPath('./new/../..//dir/./new////.');
}).
addCallback(function(newDir) {
assertEquals('/goog-fs-test-dir/dir/new', newDir.getFullPath());
}).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testCreateRelativePath');
}
function testCreateBadPath() {
if (!fsExists) {
return;
}
loadTestDir().
awaitDeferred(loadTestDir()).
addCallback(function(dir) {
// There is only one layer of parent directory from the test dir.
return dir.createPath('../../../../' + TEST_DIR + '/baz/bat');
}).
addCallback(function(batDir) {
assertEquals('The parent directory of the root directory should ' +
'point back to the root directory.',
'/goog-fs-test-dir/baz/bat', batDir.getFullPath());
}).
awaitDeferred(loadTestDir()).
addCallback(function(dir) {
// An empty path should return the same as the input directory.
return dir.createPath('');
}).
addCallback(function(testDir) {
assertEquals('/goog-fs-test-dir', testDir.getFullPath());
}).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testCreateBadPath');
}
function testGetAbsolutePaths() {
if (!fsExists) {
return;
}
loadFile('foo', goog.fs.DirectoryEntry.Behavior.CREATE).
awaitDeferred(loadTestDir()).
addCallback(function(testDir) {
return testDir.getDirectory('/');
}).
addCallback(function(root) {
assertEquals('/', root.getFullPath());
return root.getDirectory('/' + TEST_DIR);
}).
addCallback(function(testDir) {
assertEquals('/goog-fs-test-dir', testDir.getFullPath());
return testDir.getDirectory('//' + TEST_DIR + '////');
}).
addCallback(function(testDir) {
assertEquals('/goog-fs-test-dir', testDir.getFullPath());
return testDir.getDirectory('////');
}).
addCallback(function(testDir) {
assertEquals('/', testDir.getFullPath());
}).
addBoth(continueTesting);
asyncTestCase.waitForAsync('testGetAbsolutePaths');
}
function continueTesting(result) {
asyncTestCase.continueTesting();
if (result instanceof Error) {
throw result;
}
}
function testListEmptyDirectory() {
if (!fsExists) {
return;
}
loadTestDir().
addCallback(function(dir) { return dir.listDirectory(); }).
addCallback(function(entries) { assertArrayEquals([], entries); }).
addCallback(continueTesting);
asyncTestCase.waitForAsync('testListEmptyDirectory');
}
function testListDirectory() {
if (!fsExists) {
return;
}
goog.async.Deferred.succeed().
// Create the test directory and test entries.
awaitDeferred(
loadDirectory('testDir', goog.fs.DirectoryEntry.Behavior.CREATE)).
awaitDeferred(
loadFile('testFile', goog.fs.DirectoryEntry.Behavior.CREATE)).
awaitDeferred(loadTestDir()).
// Verify the contents of the directory listing.
addCallback(function(testDir) { return testDir.listDirectory(); }).
addCallback(function(entries) {
assertEquals(2, entries.length);
var dir = goog.array.find(entries, function(entry) {
return entry.getName() == 'testDir';
});
assertNotNull(dir);
assertTrue(dir.isDirectory());
var file = goog.array.find(entries, function(entry) {
return entry.getName() == 'testFile';
});
assertNotNull(file);
assertTrue(file.isFile());
}).
addCallback(continueTesting);
asyncTestCase.waitForAsync('testListDirectory');
}
function testListBigDirectory() {
// TODO(nicksantos): This test is broken in newer versions of chrome.
// We don't know why yet.
if (true) return;
if (!fsExists) {
return;
}
function getFileName(i) {
return 'file' + goog.string.padNumber(i, String(count).length);
}
// NOTE: This was intended to verify that the results from repeated
// DirectoryReader.readEntries() callbacks are appropriately concatenated.
// In current versions of Chrome (March 2011), all results are returned in the
// first callback regardless of directory size. The count can be increased in
// the future to test batched result lists once they are implemented.
var count = 100;
var expectedNames = [];
var def = goog.async.Deferred.succeed();
for (var i = 0; i < count; i++) {
var name = getFileName(i);
expectedNames.push(name);
def.awaitDeferred(
loadFile(name, goog.fs.DirectoryEntry.Behavior.CREATE));
}
def.awaitDeferred(loadTestDir()).
addCallback(function(testDir) { return testDir.listDirectory(); }).
addCallback(function(entries) {
assertEquals(count, entries.length);
assertSameElements(expectedNames,
goog.array.map(entries, function(entry) {
return entry.getName();
}));
assertTrue(goog.array.every(entries, function(entry) {
return entry.isFile();
}));
}).
addCallback(continueTesting);
asyncTestCase.waitForAsync('testListBigDirectory');
}
function testSliceBlob() {
// A mock blob object whose slice returns the parameters it was called with.
var blob = {
'size': 10,
'slice': function(start, end) {
return [start, end];
}
};
// Simulate Firefox 13 that implements the new slice.
var tmpStubs = new goog.testing.PropertyReplacer();
tmpStubs.set(goog.userAgent, 'GECKO', true);
tmpStubs.set(goog.userAgent, 'WEBKIT', false);
tmpStubs.set(goog.userAgent, 'IE', false);
tmpStubs.set(goog.userAgent, 'VERSION', '13.0');
tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
// Expect slice to be called with no change to parameters
assertArrayEquals([2, 10], goog.fs.sliceBlob(blob, 2));
assertArrayEquals([-2, 10], goog.fs.sliceBlob(blob, -2));
assertArrayEquals([3, 6], goog.fs.sliceBlob(blob, 3, 6));
assertArrayEquals([3, -6], goog.fs.sliceBlob(blob, 3, -6));
// Simulate IE 10 that implements the new slice.
var tmpStubs = new goog.testing.PropertyReplacer();
tmpStubs.set(goog.userAgent, 'GECKO', false);
tmpStubs.set(goog.userAgent, 'WEBKIT', false);
tmpStubs.set(goog.userAgent, 'IE', true);
tmpStubs.set(goog.userAgent, 'VERSION', '10.0');
tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
// Expect slice to be called with no change to parameters
assertArrayEquals([2, 10], goog.fs.sliceBlob(blob, 2));
assertArrayEquals([-2, 10], goog.fs.sliceBlob(blob, -2));
assertArrayEquals([3, 6], goog.fs.sliceBlob(blob, 3, 6));
assertArrayEquals([3, -6], goog.fs.sliceBlob(blob, 3, -6));
// Simulate Firefox 4 that implements the old slice.
tmpStubs.set(goog.userAgent, 'GECKO', true);
tmpStubs.set(goog.userAgent, 'WEBKIT', false);
tmpStubs.set(goog.userAgent, 'IE', false);
tmpStubs.set(goog.userAgent, 'VERSION', '2.0');
tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
// Expect slice to be called with transformed parameters.
assertArrayEquals([2, 8], goog.fs.sliceBlob(blob, 2));
assertArrayEquals([8, 2], goog.fs.sliceBlob(blob, -2));
assertArrayEquals([3, 3], goog.fs.sliceBlob(blob, 3, 6));
assertArrayEquals([3, 1], goog.fs.sliceBlob(blob, 3, -6));
// Simulate Firefox 5 that implements mozSlice (new spec).
delete blob.slice;
blob.mozSlice = function(start, end) {
return ['moz', start, end];
};
tmpStubs.set(goog.userAgent, 'GECKO', true);
tmpStubs.set(goog.userAgent, 'WEBKIT', false);
tmpStubs.set(goog.userAgent, 'IE', false);
tmpStubs.set(goog.userAgent, 'VERSION', '5.0');
tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
// Expect mozSlice to be called with no change to parameters.
assertArrayEquals(['moz', 2, 10], goog.fs.sliceBlob(blob, 2));
assertArrayEquals(['moz', -2, 10], goog.fs.sliceBlob(blob, -2));
assertArrayEquals(['moz', 3, 6], goog.fs.sliceBlob(blob, 3, 6));
assertArrayEquals(['moz', 3, -6], goog.fs.sliceBlob(blob, 3, -6));
// Simulate Chrome 20 that implements webkitSlice (new spec).
delete blob.mozSlice;
blob.webkitSlice = function(start, end) {
return ['webkit', start, end];
};
tmpStubs.set(goog.userAgent, 'GECKO', false);
tmpStubs.set(goog.userAgent, 'WEBKIT', true);
tmpStubs.set(goog.userAgent, 'IE', false);
tmpStubs.set(goog.userAgent, 'VERSION', '536.10');
tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
// Expect webkitSlice to be called with no change to parameters.
assertArrayEquals(['webkit', 2, 10], goog.fs.sliceBlob(blob, 2));
assertArrayEquals(['webkit', -2, 10], goog.fs.sliceBlob(blob, -2));
assertArrayEquals(['webkit', 3, 6], goog.fs.sliceBlob(blob, 3, 6));
assertArrayEquals(['webkit', 3, -6], goog.fs.sliceBlob(blob, 3, -6));
tmpStubs.reset();
}
function testBrowserSupportsObjectUrls() {
stubs.remove(goog.global, 'URL');
stubs.remove(goog.global, 'webkitURL');
stubs.remove(goog.global, 'createObjectURL');
assertFalse(goog.fs.browserSupportsObjectUrls());
try {
goog.fs.createObjectUrl();
fail();
} catch (e) {
assertEquals('This browser doesn\'t seem to support blob URLs', e.message);
}
var objectUrl = {};
function createObjectURL() { return objectUrl; }
stubs.set(goog.global, 'createObjectURL', createObjectURL);
assertTrue(goog.fs.browserSupportsObjectUrls());
assertEquals(objectUrl, goog.fs.createObjectUrl());
stubs.reset();
}
function testGetBlobThrowsError() {
stubs.remove(goog.global, 'BlobBuilder');
stubs.remove(goog.global, 'WebKitBlobBuilder');
stubs.remove(goog.global, 'Blob');
try {
goog.fs.getBlob();
fail();
} catch (e) {
assertEquals('This browser doesn\'t seem to support creating Blobs',
e.message);
}
stubs.reset();
}
function testGetBlobWithProperties() {
// Skip test if browser doesn't support Blob API.
if (typeof(goog.global.Blob) != 'function') {
return;
}
var blob = goog.fs.getBlobWithProperties(['test'], 'text/test', 'native');
assertEquals('text/test', blob.type);
}
function testGetBlobWithPropertiesThrowsError() {
stubs.remove(goog.global, 'BlobBuilder');
stubs.remove(goog.global, 'WebKitBlobBuilder');
stubs.remove(goog.global, 'Blob');
try {
goog.fs.getBlobWithProperties();
fail();
} catch (e) {
assertEquals('This browser doesn\'t seem to support creating Blobs',
e.message);
}
stubs.reset();
}
function testGetBlobWithPropertiesUsingBlobBuilder() {
function BlobBuilder() {
this.parts = [];
this.append = function(value, endings) {
this.parts.push({value: value, endings: endings});
};
this.getBlob = function(type) {
return {type: type, builder: this};
};
}
stubs.set(goog.global, 'BlobBuilder', BlobBuilder);
var blob = goog.fs.getBlobWithProperties(['test'], 'text/test', 'native');
assertEquals('text/test', blob.type);
assertEquals('test', blob.builder.parts[0].value);
assertEquals('native', blob.builder.parts[0].endings);
stubs.reset();
}
function loadTestDir() {
return deferredFs.branch().addCallback(function(fs) {
return fs.getRoot().getDirectory(
TEST_DIR, goog.fs.DirectoryEntry.Behavior.CREATE);
});
}
function loadFile(filename, behavior) {
return loadTestDir().addCallback(function(dir) {
return dir.getFile(filename, behavior);
});
}
function loadDirectory(filename, behavior) {
return loadTestDir().addCallback(function(dir) {
return dir.getDirectory(filename, behavior);
});
}
function startWrite(content, file) {
return file.createWriter().
addCallback(
goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
addCallback(function(writer) {
writer.write(goog.fs.getBlob(content));
return writer;
}).
addCallback(
goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING));
}
function waitForEvent(type, target) {
var d = new goog.async.Deferred();
goog.events.listenOnce(target, type, d.callback, false, d);
return d;
}
function writeToFile(content, file) {
return startWrite(content, file).
addCallback(
goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
addCallback(function() { return file; });
}
function checkFileContent(content, file) {
return checkFileContentAs(content, 'Text', undefined, file);
}
function checkFileContentWithEncoding(content, encoding, file) {
checkFileContentAs(content, 'Text', encoding, file);
}
function checkFileContentAs(content, filetype, encoding, file) {
return file.file().
addCallback(function(blob) {
return goog.fs.FileReader['readAs' + filetype](blob, encoding);
}).
addCallback(goog.partial(checkEquals, content));
}
function checkEquals(a, b) {
if (a instanceof ArrayBuffer && b instanceof ArrayBuffer) {
assertEquals(a.byteLength, b.byteLength);
var viewA = new DataView(a);
var viewB = new DataView(b);
for (var i = 0; i < a.byteLength; i++) {
assertEquals(viewA.getUint8(i), viewB.getUint8(i));
}
} else {
assertEquals(a, b);
}
}
function checkFileRemoved(filename) {
return loadFile(filename).
addCallback(goog.partial(fail, 'expected file to be removed')).
addErrback(function(err) {
assertEquals(err.code, goog.fs.Error.ErrorCode.NOT_FOUND);
return true; // Go back to callback path
});
}
function checkReadyState(expectedState, writer) {
assertEquals(expectedState, writer.getReadyState());
}
function splitArgs(fn) {
return function(args) { return fn(args[0], args[1]); };
}
@@ -0,0 +1,69 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A wrapper for the HTML5 File ProgressEvent objects.
*
*/
goog.provide('goog.fs.ProgressEvent');
goog.require('goog.events.Event');
/**
* A wrapper for the progress events emitted by the File APIs.
*
* @param {!ProgressEvent} event The underlying event object.
* @param {!Object} target The file access object emitting the event.
* @extends {goog.events.Event}
* @constructor
* @final
*/
goog.fs.ProgressEvent = function(event, target) {
goog.fs.ProgressEvent.base(this, 'constructor', event.type, target);
/**
* The underlying event object.
* @type {!ProgressEvent}
* @private
*/
this.event_ = event;
};
goog.inherits(goog.fs.ProgressEvent, goog.events.Event);
/**
* @return {boolean} Whether or not the total size of the of the file being
* saved is known.
*/
goog.fs.ProgressEvent.prototype.isLengthComputable = function() {
return this.event_.lengthComputable;
};
/**
* @return {number} The number of bytes saved so far.
*/
goog.fs.ProgressEvent.prototype.getLoaded = function() {
return this.event_.loaded;
};
/**
* @return {number} The total number of bytes in the file being saved.
*/
goog.fs.ProgressEvent.prototype.getTotal = function() {
return this.event_.total;
};