http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fs/filesaver.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fs/filesaver.js b/externs/GCL/externs/goog/fs/filesaver.js new file mode 100644 index 0000000..8d441c4 --- /dev/null +++ b/externs/GCL/externs/goog/fs/filesaver.js @@ -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'); +};
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fs/filesystem.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fs/filesystem.js b/externs/GCL/externs/goog/fs/filesystem.js new file mode 100644 index 0000000..b120b92 --- /dev/null +++ b/externs/GCL/externs/goog/fs/filesystem.js @@ -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() {}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fs/filesystemimpl.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fs/filesystemimpl.js b/externs/GCL/externs/goog/fs/filesystemimpl.js new file mode 100644 index 0000000..b5ebb33 --- /dev/null +++ b/externs/GCL/externs/goog/fs/filesystemimpl.js @@ -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_; +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fs/filewriter.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fs/filewriter.js b/externs/GCL/externs/goog/fs/filewriter.js new file mode 100644 index 0000000..1709846 --- /dev/null +++ b/externs/GCL/externs/goog/fs/filewriter.js @@ -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'); + } +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fs/fs.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fs/fs.js b/externs/GCL/externs/goog/fs/fs.js new file mode 100644 index 0000000..6081f6e --- /dev/null +++ b/externs/GCL/externs/goog/fs/fs.js @@ -0,0 +1,278 @@ +// 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.fs.url'); +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. + * + * TODO(user): Update references to this method to use + * goog.fs.url.createObjectUrl instead. + * + * @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.url.createObjectUrl(blob); +}; + + +/** + * Revokes a URL created by {@link goog.fs.createObjectUrl}. + * Throws an error if the browser does not support Object Urls. + * + * TODO(user): Update references to this method to use + * goog.fs.url.revokeObjectUrl instead. + * + * @param {string} url The URL to revoke. + */ +goog.fs.revokeObjectUrl = function(url) { + goog.fs.url.revokeObjectUrl(url); +}; + + +/** + * Checks whether this browser supports Object Urls. If not, calls to + * createObjectUrl and revokeObjectUrl will result in an error. + * + * TODO(user): Update references to this method to use + * goog.fs.url.browserSupportsObjectUrls instead. + * + * @return {boolean} True if this browser supports Object Urls. + */ +goog.fs.browserSupportsObjectUrls = function() { + return goog.fs.url.browserSupportsObjectUrls(); +}; + + +/** + * 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; +}; + http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fs/progressevent.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fs/progressevent.js b/externs/GCL/externs/goog/fs/progressevent.js new file mode 100644 index 0000000..b0695be --- /dev/null +++ b/externs/GCL/externs/goog/fs/progressevent.js @@ -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; +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fs/url.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fs/url.js b/externs/GCL/externs/goog/fs/url.js new file mode 100644 index 0000000..083c066 --- /dev/null +++ b/externs/GCL/externs/goog/fs/url.js @@ -0,0 +1,105 @@ +// Copyright 2015 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 Wrapper for URL and its createObjectUrl and revokeObjectUrl + * methods that are part of the HTML5 File API. + */ + +goog.provide('goog.fs.url'); + + +/** + * 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.url.createObjectUrl = function(blob) { + return goog.fs.url.getUrlObject_().createObjectURL(blob); +}; + + +/** + * Revokes a URL created by {@link goog.fs.url.createObjectUrl}. + * Throws an error if the browser does not support Object Urls. + * + * @param {string} url The URL to revoke. + */ +goog.fs.url.revokeObjectUrl = function(url) { + goog.fs.url.getUrlObject_().revokeObjectURL(url); +}; + + +/** + * @typedef {{createObjectURL: (function(!Blob): string), + * revokeObjectURL: function(string): void}} + */ +goog.fs.url.UrlObject_; + + +/** + * Get the object that has the createObjectURL and revokeObjectURL functions for + * this browser. + * + * @return {goog.fs.url.UrlObject_} The object for this browser. + * @private + */ +goog.fs.url.getUrlObject_ = function() { + var urlObject = goog.fs.url.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.url.UrlObject_} The object for this browser or null if the + * browser does not support Object Urls. + * @private + */ +goog.fs.url.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.url.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.url.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.url.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.url.browserSupportsObjectUrls = function() { + return goog.fs.url.findUrlObject_() != null; +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/functions/functions.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/functions/functions.js b/externs/GCL/externs/goog/functions/functions.js new file mode 100644 index 0000000..d7ccf40 --- /dev/null +++ b/externs/GCL/externs/goog/functions/functions.js @@ -0,0 +1,332 @@ +// Copyright 2008 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 Utilities for creating functions. Loosely inspired by the + * java classes: http://goo.gl/GM0Hmu and http://goo.gl/6k7nI8. + * + * @author [email protected] (Nick Santos) + */ + + +goog.provide('goog.functions'); + + +/** + * Creates a function that always returns the same value. + * @param {T} retValue The value to return. + * @return {function():T} The new function. + * @template T + */ +goog.functions.constant = function(retValue) { + return function() { + return retValue; + }; +}; + + +/** + * Always returns false. + * @type {function(...): boolean} + */ +goog.functions.FALSE = goog.functions.constant(false); + + +/** + * Always returns true. + * @type {function(...): boolean} + */ +goog.functions.TRUE = goog.functions.constant(true); + + +/** + * Always returns NULL. + * @type {function(...): null} + */ +goog.functions.NULL = goog.functions.constant(null); + + +/** + * A simple function that returns the first argument of whatever is passed + * into it. + * @param {T=} opt_returnValue The single value that will be returned. + * @param {...*} var_args Optional trailing arguments. These are ignored. + * @return {T} The first argument passed in, or undefined if nothing was passed. + * @template T + */ +goog.functions.identity = function(opt_returnValue, var_args) { + return opt_returnValue; +}; + + +/** + * Creates a function that always throws an error with the given message. + * @param {string} message The error message. + * @return {!Function} The error-throwing function. + */ +goog.functions.error = function(message) { + return function() { + throw Error(message); + }; +}; + + +/** + * Creates a function that throws the given object. + * @param {*} err An object to be thrown. + * @return {!Function} The error-throwing function. + */ +goog.functions.fail = function(err) { + return function() { + throw err; + } +}; + + +/** + * Given a function, create a function that keeps opt_numArgs arguments and + * silently discards all additional arguments. + * @param {Function} f The original function. + * @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0. + * @return {!Function} A version of f that only keeps the first opt_numArgs + * arguments. + */ +goog.functions.lock = function(f, opt_numArgs) { + opt_numArgs = opt_numArgs || 0; + return function() { + return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs)); + }; +}; + + +/** + * Creates a function that returns its nth argument. + * @param {number} n The position of the return argument. + * @return {!Function} A new function. + */ +goog.functions.nth = function(n) { + return function() { + return arguments[n]; + }; +}; + + +/** + * Given a function, create a new function that swallows its return value + * and replaces it with a new one. + * @param {Function} f A function. + * @param {T} retValue A new return value. + * @return {function(...?):T} A new function. + * @template T + */ +goog.functions.withReturnValue = function(f, retValue) { + return goog.functions.sequence(f, goog.functions.constant(retValue)); +}; + + +/** + * Creates a function that returns whether its arguement equals the given value. + * + * Example: + * var key = goog.object.findKey(obj, goog.functions.equalTo('needle')); + * + * @param {*} value The value to compare to. + * @param {boolean=} opt_useLooseComparison Whether to use a loose (==) + * comparison rather than a strict (===) one. Defaults to false. + * @return {function(*):boolean} The new function. + */ +goog.functions.equalTo = function(value, opt_useLooseComparison) { + return function(other) { + return opt_useLooseComparison ? (value == other) : (value === other); + }; +}; + + +/** + * Creates the composition of the functions passed in. + * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)). + * @param {function(...?):T} fn The final function. + * @param {...Function} var_args A list of functions. + * @return {function(...?):T} The composition of all inputs. + * @template T + */ +goog.functions.compose = function(fn, var_args) { + var functions = arguments; + var length = functions.length; + return function() { + var result; + if (length) { + result = functions[length - 1].apply(this, arguments); + } + + for (var i = length - 2; i >= 0; i--) { + result = functions[i].call(this, result); + } + return result; + }; +}; + + +/** + * Creates a function that calls the functions passed in in sequence, and + * returns the value of the last function. For example, + * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x). + * @param {...Function} var_args A list of functions. + * @return {!Function} A function that calls all inputs in sequence. + */ +goog.functions.sequence = function(var_args) { + var functions = arguments; + var length = functions.length; + return function() { + var result; + for (var i = 0; i < length; i++) { + result = functions[i].apply(this, arguments); + } + return result; + }; +}; + + +/** + * Creates a function that returns true if each of its components evaluates + * to true. The components are evaluated in order, and the evaluation will be + * short-circuited as soon as a function returns false. + * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x). + * @param {...Function} var_args A list of functions. + * @return {function(...?):boolean} A function that ANDs its component + * functions. + */ +goog.functions.and = function(var_args) { + var functions = arguments; + var length = functions.length; + return function() { + for (var i = 0; i < length; i++) { + if (!functions[i].apply(this, arguments)) { + return false; + } + } + return true; + }; +}; + + +/** + * Creates a function that returns true if any of its components evaluates + * to true. The components are evaluated in order, and the evaluation will be + * short-circuited as soon as a function returns true. + * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x). + * @param {...Function} var_args A list of functions. + * @return {function(...?):boolean} A function that ORs its component + * functions. + */ +goog.functions.or = function(var_args) { + var functions = arguments; + var length = functions.length; + return function() { + for (var i = 0; i < length; i++) { + if (functions[i].apply(this, arguments)) { + return true; + } + } + return false; + }; +}; + + +/** + * Creates a function that returns the Boolean opposite of a provided function. + * For example, (goog.functions.not(f))(x) is equivalent to !f(x). + * @param {!Function} f The original function. + * @return {function(...?):boolean} A function that delegates to f and returns + * opposite. + */ +goog.functions.not = function(f) { + return function() { + return !f.apply(this, arguments); + }; +}; + + +/** + * Generic factory function to construct an object given the constructor + * and the arguments. Intended to be bound to create object factories. + * + * Example: + * + * var factory = goog.partial(goog.functions.create, Class); + * + * @param {function(new:T, ...)} constructor The constructor for the Object. + * @param {...*} var_args The arguments to be passed to the constructor. + * @return {T} A new instance of the class given in {@code constructor}. + * @template T + */ +goog.functions.create = function(constructor, var_args) { + /** + * @constructor + * @final + */ + var temp = function() {}; + temp.prototype = constructor.prototype; + + // obj will have constructor's prototype in its chain and + // 'obj instanceof constructor' will be true. + var obj = new temp(); + + // obj is initialized by constructor. + // arguments is only array-like so lacks shift(), but can be used with + // the Array prototype function. + constructor.apply(obj, Array.prototype.slice.call(arguments, 1)); + return obj; +}; + + +/** + * @define {boolean} Whether the return value cache should be used. + * This should only be used to disable caches when testing. + */ +goog.define('goog.functions.CACHE_RETURN_VALUE', true); + + +/** + * Gives a wrapper function that caches the return value of a parameterless + * function when first called. + * + * When called for the first time, the given function is called and its + * return value is cached (thus this is only appropriate for idempotent + * functions). Subsequent calls will return the cached return value. This + * allows the evaluation of expensive functions to be delayed until first used. + * + * To cache the return values of functions with parameters, see goog.memoize. + * + * @param {!function():T} fn A function to lazily evaluate. + * @return {!function():T} A wrapped version the function. + * @template T + */ +goog.functions.cacheReturnValue = function(fn) { + var called = false; + var value; + + return function() { + if (!goog.functions.CACHE_RETURN_VALUE) { + return fn(); + } + + if (!called) { + value = fn(); + called = true; + } + + return value; + } +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fx/abstractdragdrop.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fx/abstractdragdrop.js b/externs/GCL/externs/goog/fx/abstractdragdrop.js new file mode 100644 index 0000000..afd4af8 --- /dev/null +++ b/externs/GCL/externs/goog/fx/abstractdragdrop.js @@ -0,0 +1,1540 @@ +// Copyright 2006 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 Abstract Base Class for Drag and Drop. + * + * Provides functionality for implementing drag and drop classes. Also provides + * support classes and events. + * + * @author [email protected] (Emil A Eklund) + */ + +goog.provide('goog.fx.AbstractDragDrop'); +goog.provide('goog.fx.AbstractDragDrop.EventType'); +goog.provide('goog.fx.DragDropEvent'); +goog.provide('goog.fx.DragDropItem'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.classlist'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.fx.Dragger'); +goog.require('goog.math.Box'); +goog.require('goog.math.Coordinate'); +goog.require('goog.style'); + + + +/** + * Abstract class that provides reusable functionality for implementing drag + * and drop functionality. + * + * This class also allows clients to define their own subtargeting function + * so that drop areas can have finer granularity then a singe element. This is + * accomplished by using a client provided function to map from element and + * coordinates to a subregion id. + * + * This class can also be made aware of scrollable containers that contain + * drop targets by calling addScrollableContainer. This will cause dnd to + * take changing scroll positions into account while a drag is occuring. + * + * @extends {goog.events.EventTarget} + * @constructor + */ +goog.fx.AbstractDragDrop = function() { + goog.fx.AbstractDragDrop.base(this, 'constructor'); + + /** + * List of items that makes up the drag source or drop target. + * @type {Array<goog.fx.DragDropItem>} + * @protected + * @suppress {underscore|visibility} + */ + this.items_ = []; + + /** + * List of associated drop targets. + * @type {Array<goog.fx.AbstractDragDrop>} + * @private + */ + this.targets_ = []; + + /** + * Scrollable containers to account for during drag + * @type {Array<goog.fx.ScrollableContainer_>} + * @private + */ + this.scrollableContainers_ = []; + +}; +goog.inherits(goog.fx.AbstractDragDrop, goog.events.EventTarget); + + +/** + * Minimum size (in pixels) for a dummy target. If the box for the target is + * less than the specified size it's not created. + * @type {number} + * @private + */ +goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ = 10; + + +/** + * Flag indicating if it's a drag source, set by addTarget. + * @type {boolean} + * @private + */ +goog.fx.AbstractDragDrop.prototype.isSource_ = false; + + +/** + * Flag indicating if it's a drop target, set when added as target to another + * DragDrop object. + * @type {boolean} + * @private + */ +goog.fx.AbstractDragDrop.prototype.isTarget_ = false; + + +/** + * Subtargeting function accepting args: + * (goog.fx.DragDropItem, goog.math.Box, number, number) + * @type {Function} + * @private + */ +goog.fx.AbstractDragDrop.prototype.subtargetFunction_; + + +/** + * Last active subtarget. + * @type {Object} + * @private + */ +goog.fx.AbstractDragDrop.prototype.activeSubtarget_; + + +/** + * Class name to add to source elements being dragged. Set by setDragClass. + * @type {?string} + * @private + */ +goog.fx.AbstractDragDrop.prototype.dragClass_; + + +/** + * Class name to add to source elements. Set by setSourceClass. + * @type {?string} + * @private + */ +goog.fx.AbstractDragDrop.prototype.sourceClass_; + + +/** + * Class name to add to target elements. Set by setTargetClass. + * @type {?string} + * @private + */ +goog.fx.AbstractDragDrop.prototype.targetClass_; + + +/** + * The SCROLL event target used to make drag element follow scrolling. + * @type {EventTarget} + * @private + */ +goog.fx.AbstractDragDrop.prototype.scrollTarget_; + + +/** + * Dummy target, {@see maybeCreateDummyTargetForPosition_}. + * @type {goog.fx.ActiveDropTarget_} + * @private + */ +goog.fx.AbstractDragDrop.prototype.dummyTarget_; + + +/** + * Whether the object has been initialized. + * @type {boolean} + * @private + */ +goog.fx.AbstractDragDrop.prototype.initialized_ = false; + + +/** + * Constants for event names + * @const + */ +goog.fx.AbstractDragDrop.EventType = { + DRAGOVER: 'dragover', + DRAGOUT: 'dragout', + DRAG: 'drag', + DROP: 'drop', + DRAGSTART: 'dragstart', + DRAGEND: 'dragend' +}; + + +/** + * Constant for distance threshold, in pixels, an element has to be moved to + * initiate a drag operation. + * @type {number} + */ +goog.fx.AbstractDragDrop.initDragDistanceThreshold = 5; + + +/** + * Set class to add to source elements being dragged. + * + * @param {string} className Class to be added. Must be a single, valid + * classname. + */ +goog.fx.AbstractDragDrop.prototype.setDragClass = function(className) { + this.dragClass_ = className; +}; + + +/** + * Set class to add to source elements. + * + * @param {string} className Class to be added. Must be a single, valid + * classname. + */ +goog.fx.AbstractDragDrop.prototype.setSourceClass = function(className) { + this.sourceClass_ = className; +}; + + +/** + * Set class to add to target elements. + * + * @param {string} className Class to be added. Must be a single, valid + * classname. + */ +goog.fx.AbstractDragDrop.prototype.setTargetClass = function(className) { + this.targetClass_ = className; +}; + + +/** + * Whether the control has been initialized. + * + * @return {boolean} True if it's been initialized. + */ +goog.fx.AbstractDragDrop.prototype.isInitialized = function() { + return this.initialized_; +}; + + +/** + * Add item to drag object. + * + * @param {Element|string} element Dom Node, or string representation of node + * id, to be used as drag source/drop target. + * @throws Error Thrown if called on instance of abstract class + */ +goog.fx.AbstractDragDrop.prototype.addItem = goog.abstractMethod; + + +/** + * Associate drop target with drag element. + * + * @param {goog.fx.AbstractDragDrop} target Target to add. + */ +goog.fx.AbstractDragDrop.prototype.addTarget = function(target) { + this.targets_.push(target); + target.isTarget_ = true; + this.isSource_ = true; +}; + + +/** + * Sets the SCROLL event target to make drag element follow scrolling. + * + * @param {EventTarget} scrollTarget The element that dispatches SCROLL events. + */ +goog.fx.AbstractDragDrop.prototype.setScrollTarget = function(scrollTarget) { + this.scrollTarget_ = scrollTarget; +}; + + +/** + * Initialize drag and drop functionality for sources/targets already added. + * Sources/targets added after init has been called will initialize themselves + * one by one. + */ +goog.fx.AbstractDragDrop.prototype.init = function() { + if (this.initialized_) { + return; + } + for (var item, i = 0; item = this.items_[i]; i++) { + this.initItem(item); + } + + this.initialized_ = true; +}; + + +/** + * Initializes a single item. + * + * @param {goog.fx.DragDropItem} item Item to initialize. + * @protected + */ +goog.fx.AbstractDragDrop.prototype.initItem = function(item) { + if (this.isSource_) { + goog.events.listen(item.element, goog.events.EventType.MOUSEDOWN, + item.mouseDown_, false, item); + if (this.sourceClass_) { + goog.dom.classlist.add( + goog.asserts.assert(item.element), this.sourceClass_); + } + } + + if (this.isTarget_ && this.targetClass_) { + goog.dom.classlist.add( + goog.asserts.assert(item.element), this.targetClass_); + } +}; + + +/** + * Called when removing an item. Removes event listeners and classes. + * + * @param {goog.fx.DragDropItem} item Item to dispose. + * @protected + */ +goog.fx.AbstractDragDrop.prototype.disposeItem = function(item) { + if (this.isSource_) { + goog.events.unlisten(item.element, goog.events.EventType.MOUSEDOWN, + item.mouseDown_, false, item); + if (this.sourceClass_) { + goog.dom.classlist.remove( + goog.asserts.assert(item.element), this.sourceClass_); + } + } + if (this.isTarget_ && this.targetClass_) { + goog.dom.classlist.remove( + goog.asserts.assert(item.element), this.targetClass_); + } + item.dispose(); +}; + + +/** + * Removes all items. + */ +goog.fx.AbstractDragDrop.prototype.removeItems = function() { + for (var item, i = 0; item = this.items_[i]; i++) { + this.disposeItem(item); + } + this.items_.length = 0; +}; + + +/** + * Starts a drag event for an item if the mouse button stays pressed and the + * cursor moves a few pixels. Allows dragging of items without first having to + * register them with addItem. + * + * @param {goog.events.BrowserEvent} event Mouse down event. + * @param {goog.fx.DragDropItem} item Item that's being dragged. + */ +goog.fx.AbstractDragDrop.prototype.maybeStartDrag = function(event, item) { + item.maybeStartDrag_(event, item.element); +}; + + +/** + * Event handler that's used to start drag. + * + * @param {goog.events.BrowserEvent} event Mouse move event. + * @param {goog.fx.DragDropItem} item Item that's being dragged. + */ +goog.fx.AbstractDragDrop.prototype.startDrag = function(event, item) { + + // Prevent a new drag operation from being started if another one is already + // in progress (could happen if the mouse was released outside of the + // document). + if (this.dragItem_) { + return; + } + + this.dragItem_ = item; + + // Dispatch DRAGSTART event + var dragStartEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DRAGSTART, this, this.dragItem_); + if (this.dispatchEvent(dragStartEvent) == false) { + this.dragItem_ = null; + return; + } + + // Get the source element and create a drag element for it. + var el = item.getCurrentDragElement(); + this.dragEl_ = this.createDragElement(el); + var doc = goog.dom.getOwnerDocument(el); + doc.body.appendChild(this.dragEl_); + + this.dragger_ = this.createDraggerFor(el, this.dragEl_, event); + this.dragger_.setScrollTarget(this.scrollTarget_); + + goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG, + this.moveDrag_, false, this); + + goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.END, + this.endDrag, false, this); + + // IE may issue a 'selectstart' event when dragging over an iframe even when + // default mousemove behavior is suppressed. If the default selectstart + // behavior is not suppressed, elements dragged over will show as selected. + goog.events.listen(doc.body, goog.events.EventType.SELECTSTART, + this.suppressSelect_); + + this.recalculateDragTargets(); + this.recalculateScrollableContainers(); + this.activeTarget_ = null; + this.initScrollableContainerListeners_(); + this.dragger_.startDrag(event); + + event.preventDefault(); +}; + + +/** + * Recalculates the geometry of this source's drag targets. Call this + * if the position or visibility of a drag target has changed during + * a drag, or if targets are added or removed. + * + * TODO(user): this is an expensive operation; more efficient APIs + * may be necessary. + */ +goog.fx.AbstractDragDrop.prototype.recalculateDragTargets = function() { + this.targetList_ = []; + for (var target, i = 0; target = this.targets_[i]; i++) { + for (var itm, j = 0; itm = target.items_[j]; j++) { + this.addDragTarget_(target, itm); + } + } + if (!this.targetBox_) { + this.targetBox_ = new goog.math.Box(0, 0, 0, 0); + } +}; + + +/** + * Recalculates the current scroll positions of scrollable containers and + * allocates targets. Call this if the position of a container changed or if + * targets are added or removed. + */ +goog.fx.AbstractDragDrop.prototype.recalculateScrollableContainers = + function() { + var container, i, j, target; + for (i = 0; container = this.scrollableContainers_[i]; i++) { + container.containedTargets_ = []; + container.savedScrollLeft_ = container.element_.scrollLeft; + container.savedScrollTop_ = container.element_.scrollTop; + var pos = goog.style.getPageOffset(container.element_); + var size = goog.style.getSize(container.element_); + container.box_ = new goog.math.Box(pos.y, pos.x + size.width, + pos.y + size.height, pos.x); + } + + for (i = 0; target = this.targetList_[i]; i++) { + for (j = 0; container = this.scrollableContainers_[j]; j++) { + if (goog.dom.contains(container.element_, target.element_)) { + container.containedTargets_.push(target); + target.scrollableContainer_ = container; + } + } + } +}; + + +/** + * Creates the Dragger for the drag element. + * @param {Element} sourceEl Drag source element. + * @param {Element} el the element created by createDragElement(). + * @param {goog.events.BrowserEvent} event Mouse down event for start of drag. + * @return {!goog.fx.Dragger} The new Dragger. + * @protected + */ +goog.fx.AbstractDragDrop.prototype.createDraggerFor = + function(sourceEl, el, event) { + // Position the drag element. + var pos = this.getDragElementPosition(sourceEl, el, event); + el.style.position = 'absolute'; + el.style.left = pos.x + 'px'; + el.style.top = pos.y + 'px'; + return new goog.fx.Dragger(el); +}; + + +/** + * Event handler that's used to stop drag. Fires a drop event if over a valid + * target. + * + * @param {goog.fx.DragEvent} event Drag event. + */ +goog.fx.AbstractDragDrop.prototype.endDrag = function(event) { + var activeTarget = event.dragCanceled ? null : this.activeTarget_; + if (activeTarget && activeTarget.target_) { + var clientX = event.clientX; + var clientY = event.clientY; + var scroll = this.getScrollPos(); + var x = clientX + scroll.x; + var y = clientY + scroll.y; + + var subtarget; + // If a subtargeting function is enabled get the current subtarget + if (this.subtargetFunction_) { + subtarget = this.subtargetFunction_(activeTarget.item_, + activeTarget.box_, x, y); + } + + var dragEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DRAG, this, this.dragItem_, + activeTarget.target_, activeTarget.item_, activeTarget.element_, + clientX, clientY, x, y); + this.dispatchEvent(dragEvent); + + var dropEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DROP, this, this.dragItem_, + activeTarget.target_, activeTarget.item_, activeTarget.element_, + clientX, clientY, x, y, subtarget); + activeTarget.target_.dispatchEvent(dropEvent); + } + + var dragEndEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DRAGEND, this, this.dragItem_); + this.dispatchEvent(dragEndEvent); + + goog.events.unlisten(this.dragger_, goog.fx.Dragger.EventType.DRAG, + this.moveDrag_, false, this); + goog.events.unlisten(this.dragger_, goog.fx.Dragger.EventType.END, + this.endDrag, false, this); + var doc = goog.dom.getOwnerDocument(this.dragItem_.getCurrentDragElement()); + goog.events.unlisten(doc.body, goog.events.EventType.SELECTSTART, + this.suppressSelect_); + + + this.afterEndDrag(this.activeTarget_ ? this.activeTarget_.item_ : null); +}; + + +/** + * Called after a drag operation has finished. + * + * @param {goog.fx.DragDropItem=} opt_dropTarget Target for successful drop. + * @protected + */ +goog.fx.AbstractDragDrop.prototype.afterEndDrag = function(opt_dropTarget) { + this.disposeDrag(); +}; + + +/** + * Called once a drag operation has finished. Removes event listeners and + * elements. + * + * @protected + */ +goog.fx.AbstractDragDrop.prototype.disposeDrag = function() { + this.disposeScrollableContainerListeners_(); + this.dragger_.dispose(); + + goog.dom.removeNode(this.dragEl_); + delete this.dragItem_; + delete this.dragEl_; + delete this.dragger_; + delete this.targetList_; + delete this.activeTarget_; +}; + + +/** + * Event handler for drag events. Determines the active drop target, if any, and + * fires dragover and dragout events appropriately. + * + * @param {goog.fx.DragEvent} event Drag event. + * @private + */ +goog.fx.AbstractDragDrop.prototype.moveDrag_ = function(event) { + var position = this.getEventPosition(event); + var x = position.x; + var y = position.y; + + // Check if we're still inside the bounds of the active target, if not fire + // a dragout event and proceed to find a new target. + var activeTarget = this.activeTarget_; + + var subtarget; + if (activeTarget) { + // If a subtargeting function is enabled get the current subtarget + if (this.subtargetFunction_ && activeTarget.target_) { + subtarget = this.subtargetFunction_(activeTarget.item_, + activeTarget.box_, x, y); + } + + if (activeTarget.box_.contains(position) && + subtarget == this.activeSubtarget_) { + return; + } + + if (activeTarget.target_) { + var sourceDragOutEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DRAGOUT, this, this.dragItem_, + activeTarget.target_, activeTarget.item_, activeTarget.element_); + this.dispatchEvent(sourceDragOutEvent); + + // The event should be dispatched the by target DragDrop so that the + // target DragDrop can manage these events without having to know what + // sources this is a target for. + var targetDragOutEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DRAGOUT, + this, + this.dragItem_, + activeTarget.target_, + activeTarget.item_, + activeTarget.element_, + undefined, + undefined, + undefined, + undefined, + this.activeSubtarget_); + activeTarget.target_.dispatchEvent(targetDragOutEvent); + } + this.activeSubtarget_ = subtarget; + this.activeTarget_ = null; + } + + // Check if inside target box + if (this.targetBox_.contains(position)) { + // Search for target and fire a dragover event if found + activeTarget = this.activeTarget_ = this.getTargetFromPosition_(position); + if (activeTarget && activeTarget.target_) { + // If a subtargeting function is enabled get the current subtarget + if (this.subtargetFunction_) { + subtarget = this.subtargetFunction_(activeTarget.item_, + activeTarget.box_, x, y); + } + var sourceDragOverEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_, + activeTarget.target_, activeTarget.item_, activeTarget.element_); + sourceDragOverEvent.subtarget = subtarget; + this.dispatchEvent(sourceDragOverEvent); + + // The event should be dispatched by the target DragDrop so that the + // target DragDrop can manage these events without having to know what + // sources this is a target for. + var targetDragOverEvent = new goog.fx.DragDropEvent( + goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_, + activeTarget.target_, activeTarget.item_, activeTarget.element_, + event.clientX, event.clientY, undefined, undefined, subtarget); + activeTarget.target_.dispatchEvent(targetDragOverEvent); + + } else if (!activeTarget) { + // If no target was found create a dummy one so we won't have to iterate + // over all possible targets for every move event. + this.activeTarget_ = this.maybeCreateDummyTargetForPosition_(x, y); + } + } +}; + + +/** + * Event handler for suppressing selectstart events. Selecting should be + * disabled while dragging. + * + * @param {goog.events.Event} event The selectstart event to suppress. + * @return {boolean} Whether to perform default behavior. + * @private + */ +goog.fx.AbstractDragDrop.prototype.suppressSelect_ = function(event) { + return false; +}; + + +/** + * Sets up listeners for the scrollable containers that keep track of their + * scroll positions. + * @private + */ +goog.fx.AbstractDragDrop.prototype.initScrollableContainerListeners_ = + function() { + var container, i; + for (i = 0; container = this.scrollableContainers_[i]; i++) { + goog.events.listen(container.element_, goog.events.EventType.SCROLL, + this.containerScrollHandler_, false, this); + } +}; + + +/** + * Cleans up the scrollable container listeners. + * @private + */ +goog.fx.AbstractDragDrop.prototype.disposeScrollableContainerListeners_ = + function() { + for (var i = 0, container; container = this.scrollableContainers_[i]; i++) { + goog.events.unlisten(container.element_, 'scroll', + this.containerScrollHandler_, false, this); + container.containedTargets_ = []; + } +}; + + +/** + * Makes drag and drop aware of a target container that could scroll mid drag. + * @param {Element} element The scroll container. + */ +goog.fx.AbstractDragDrop.prototype.addScrollableContainer = function(element) { + this.scrollableContainers_.push(new goog.fx.ScrollableContainer_(element)); +}; + + +/** + * Removes all scrollable containers. + */ +goog.fx.AbstractDragDrop.prototype.removeAllScrollableContainers = function() { + this.disposeScrollableContainerListeners_(); + this.scrollableContainers_ = []; +}; + + +/** + * Event handler for containers scrolling. + * @param {goog.events.Event} e The event. + * @private + */ +goog.fx.AbstractDragDrop.prototype.containerScrollHandler_ = function(e) { + for (var i = 0, container; container = this.scrollableContainers_[i]; i++) { + if (e.target == container.element_) { + var deltaTop = container.savedScrollTop_ - container.element_.scrollTop; + var deltaLeft = + container.savedScrollLeft_ - container.element_.scrollLeft; + container.savedScrollTop_ = container.element_.scrollTop; + container.savedScrollLeft_ = container.element_.scrollLeft; + + // When the container scrolls, it's possible that one of the targets will + // move to the region contained by the dummy target. Since we don't know + // which sides (if any) of the dummy target are defined by targets + // contained by this container, we are conservative and just shrink it. + if (this.dummyTarget_ && this.activeTarget_ == this.dummyTarget_) { + if (deltaTop > 0) { + this.dummyTarget_.box_.top += deltaTop; + } else { + this.dummyTarget_.box_.bottom += deltaTop; + } + if (deltaLeft > 0) { + this.dummyTarget_.box_.left += deltaLeft; + } else { + this.dummyTarget_.box_.right += deltaLeft; + } + } + for (var j = 0, target; target = container.containedTargets_[j]; j++) { + var box = target.box_; + box.top += deltaTop; + box.left += deltaLeft; + box.bottom += deltaTop; + box.right += deltaLeft; + + this.calculateTargetBox_(box); + } + } + } + this.dragger_.onScroll_(e); +}; + + +/** + * Set a function that provides subtargets. A subtargeting function + * returns an arbitrary identifier for each subtarget of an element. + * DnD code will generate additional drag over / out events when + * switching from subtarget to subtarget. This is useful for instance + * if you are interested if you are on the top half or the bottom half + * of the element. + * The provided function will be given the DragDropItem, box, x, y + * box is the current window coordinates occupied by element + * x, y is the mouse position in window coordinates + * + * @param {Function} f The new subtarget function. + */ +goog.fx.AbstractDragDrop.prototype.setSubtargetFunction = function(f) { + this.subtargetFunction_ = f; +}; + + +/** + * Creates an element for the item being dragged. + * + * @param {Element} sourceEl Drag source element. + * @return {Element} The new drag element. + */ +goog.fx.AbstractDragDrop.prototype.createDragElement = function(sourceEl) { + var dragEl = this.createDragElementInternal(sourceEl); + goog.asserts.assert(dragEl); + if (this.dragClass_) { + goog.dom.classlist.add(dragEl, this.dragClass_); + } + + return dragEl; +}; + + +/** + * Returns the position for the drag element. + * + * @param {Element} el Drag source element. + * @param {Element} dragEl The dragged element created by createDragElement(). + * @param {goog.events.BrowserEvent} event Mouse down event for start of drag. + * @return {!goog.math.Coordinate} The position for the drag element. + */ +goog.fx.AbstractDragDrop.prototype.getDragElementPosition = + function(el, dragEl, event) { + var pos = goog.style.getPageOffset(el); + + // Subtract margin from drag element position twice, once to adjust the + // position given by the original node and once for the drag node. + var marginBox = goog.style.getMarginBox(el); + pos.x -= (marginBox.left || 0) * 2; + pos.y -= (marginBox.top || 0) * 2; + + return pos; +}; + + +/** + * Returns the dragger object. + * + * @return {goog.fx.Dragger} The dragger object used by this drag and drop + * instance. + */ +goog.fx.AbstractDragDrop.prototype.getDragger = function() { + return this.dragger_; +}; + + +/** + * Creates copy of node being dragged. + * + * @param {Element} sourceEl Element to copy. + * @return {!Element} The clone of {@code sourceEl}. + * @deprecated Use goog.fx.Dragger.cloneNode(). + * @private + */ +goog.fx.AbstractDragDrop.prototype.cloneNode_ = function(sourceEl) { + return goog.fx.Dragger.cloneNode(sourceEl); +}; + + +/** + * Generates an element to follow the cursor during dragging, given a drag + * source element. The default behavior is simply to clone the source element, + * but this may be overridden in subclasses. This method is called by + * {@code createDragElement()} before the drag class is added. + * + * @param {Element} sourceEl Drag source element. + * @return {!Element} The new drag element. + * @protected + * @suppress {deprecated} + */ +goog.fx.AbstractDragDrop.prototype.createDragElementInternal = + function(sourceEl) { + return this.cloneNode_(sourceEl); +}; + + +/** + * Add possible drop target for current drag operation. + * + * @param {goog.fx.AbstractDragDrop} target Drag handler. + * @param {goog.fx.DragDropItem} item Item that's being dragged. + * @private + */ +goog.fx.AbstractDragDrop.prototype.addDragTarget_ = function(target, item) { + + // Get all the draggable elements and add each one. + var draggableElements = item.getDraggableElements(); + var targetList = this.targetList_; + for (var i = 0; i < draggableElements.length; i++) { + var draggableElement = draggableElements[i]; + + // Determine target position and dimension + var box = this.getElementBox(item, draggableElement); + + targetList.push( + new goog.fx.ActiveDropTarget_(box, target, item, draggableElement)); + + this.calculateTargetBox_(box); + } +}; + + +/** + * Calculates the position and dimension of a draggable element. + * + * @param {goog.fx.DragDropItem} item Item that's being dragged. + * @param {Element} element The element to calculate the box. + * + * @return {!goog.math.Box} Box describing the position and dimension + * of element. + * @protected + */ +goog.fx.AbstractDragDrop.prototype.getElementBox = function(item, element) { + var pos = goog.style.getPageOffset(element); + var size = goog.style.getSize(element); + return new goog.math.Box(pos.y, pos.x + size.width, pos.y + size.height, + pos.x); +}; + + +/** + * Calculate the outer bounds (the region all targets are inside). + * + * @param {goog.math.Box} box Box describing the position and dimension + * of a drag target. + * @private + */ +goog.fx.AbstractDragDrop.prototype.calculateTargetBox_ = function(box) { + if (this.targetList_.length == 1) { + this.targetBox_ = new goog.math.Box(box.top, box.right, + box.bottom, box.left); + } else { + var tb = this.targetBox_; + tb.left = Math.min(box.left, tb.left); + tb.right = Math.max(box.right, tb.right); + tb.top = Math.min(box.top, tb.top); + tb.bottom = Math.max(box.bottom, tb.bottom); + } +}; + + +/** + * Creates a dummy target for the given cursor position. The assumption is to + * create as big dummy target box as possible, the only constraints are: + * - The dummy target box cannot overlap any of real target boxes. + * - The dummy target has to contain a point with current mouse coordinates. + * + * NOTE: For performance reasons the box construction algorithm is kept simple + * and it is not optimal (see example below). Currently it is O(n) in regard to + * the number of real drop target boxes, but its result depends on the order + * of those boxes being processed (the order in which they're added to the + * targetList_ collection). + * + * The algorithm. + * a) Assumptions + * - Mouse pointer is in the bounding box of real target boxes. + * - None of the boxes have negative coordinate values. + * - Mouse pointer is not contained by any of "real target" boxes. + * - For targets inside a scrollable container, the box used is the + * intersection of the scrollable container's box and the target's box. + * This is because the part of the target that extends outside the scrollable + * container should not be used in the clipping calculations. + * + * b) Outline + * - Initialize the fake target to the bounding box of real targets. + * - For each real target box - clip the fake target box so it does not contain + * that target box, but does contain the mouse pointer. + * -- Project the real target box, mouse pointer and fake target box onto + * both axes and calculate the clipping coordinates. + * -- Only one coordinate is used to clip the fake target box to keep the + * fake target as big as possible. + * -- If the projection of the real target box contains the mouse pointer, + * clipping for a given axis is not possible. + * -- If both clippings are possible, the clipping more distant from the + * mouse pointer is selected to keep bigger fake target area. + * - Save the created fake target only if it has a big enough area. + * + * + * c) Example + * <pre> + * Input: Algorithm created box: Maximum box: + * +---------------------+ +---------------------+ +---------------------+ + * | B1 | B2 | | B1 B2 | | B1 B2 | + * | | | | +-------------+ | |+-------------------+| + * |---------x-----------| | | | | || || + * | | | | | | | || || + * | | | | | | | || || + * | | | | | | | || || + * | | | | | | | || || + * | | | | +-------------+ | |+-------------------+| + * | B4 | B3 | | B4 B3 | | B4 B3 | + * +---------------------+ +---------------------+ +---------------------+ + * </pre> + * + * @param {number} x Cursor position on the x-axis. + * @param {number} y Cursor position on the y-axis. + * @return {goog.fx.ActiveDropTarget_} Dummy drop target. + * @private + */ +goog.fx.AbstractDragDrop.prototype.maybeCreateDummyTargetForPosition_ = + function(x, y) { + if (!this.dummyTarget_) { + this.dummyTarget_ = new goog.fx.ActiveDropTarget_(this.targetBox_.clone()); + } + var fakeTargetBox = this.dummyTarget_.box_; + + // Initialize the fake target box to the bounding box of DnD targets. + fakeTargetBox.top = this.targetBox_.top; + fakeTargetBox.right = this.targetBox_.right; + fakeTargetBox.bottom = this.targetBox_.bottom; + fakeTargetBox.left = this.targetBox_.left; + + // Clip the fake target based on mouse position and DnD target boxes. + for (var i = 0, target; target = this.targetList_[i]; i++) { + var box = target.box_; + + if (target.scrollableContainer_) { + // If the target has a scrollable container, use the intersection of that + // container's box and the target's box. + var scrollBox = target.scrollableContainer_.box_; + + box = new goog.math.Box( + Math.max(box.top, scrollBox.top), + Math.min(box.right, scrollBox.right), + Math.min(box.bottom, scrollBox.bottom), + Math.max(box.left, scrollBox.left)); + } + + // Calculate clipping coordinates for horizontal and vertical axis. + // The clipping coordinate is calculated by projecting fake target box, + // the mouse pointer and DnD target box onto an axis and checking how + // box projections overlap and if the projected DnD target box contains + // mouse pointer. The clipping coordinate cannot be computed and is set to + // a negative value if the projected DnD target contains the mouse pointer. + + var horizontalClip = null; // Assume mouse is above or below the DnD box. + if (x >= box.right) { // Mouse is to the right of the DnD box. + // Clip the fake box only if the DnD box overlaps it. + horizontalClip = box.right > fakeTargetBox.left ? + box.right : fakeTargetBox.left; + } else if (x < box.left) { // Mouse is to the left of the DnD box. + // Clip the fake box only if the DnD box overlaps it. + horizontalClip = box.left < fakeTargetBox.right ? + box.left : fakeTargetBox.right; + } + var verticalClip = null; + if (y >= box.bottom) { + verticalClip = box.bottom > fakeTargetBox.top ? + box.bottom : fakeTargetBox.top; + } else if (y < box.top) { + verticalClip = box.top < fakeTargetBox.bottom ? + box.top : fakeTargetBox.bottom; + } + + // If both clippings are possible, choose one that gives us larger distance + // to mouse pointer (mark the shorter clipping as impossible, by setting it + // to null). + if (!goog.isNull(horizontalClip) && !goog.isNull(verticalClip)) { + if (Math.abs(horizontalClip - x) > Math.abs(verticalClip - y)) { + verticalClip = null; + } else { + horizontalClip = null; + } + } + + // Clip none or one of fake target box sides (at most one clipping + // coordinate can be active). + if (!goog.isNull(horizontalClip)) { + if (horizontalClip <= x) { + fakeTargetBox.left = horizontalClip; + } else { + fakeTargetBox.right = horizontalClip; + } + } else if (!goog.isNull(verticalClip)) { + if (verticalClip <= y) { + fakeTargetBox.top = verticalClip; + } else { + fakeTargetBox.bottom = verticalClip; + } + } + } + + // Only return the new fake target if it is big enough. + return (fakeTargetBox.right - fakeTargetBox.left) * + (fakeTargetBox.bottom - fakeTargetBox.top) >= + goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ ? + this.dummyTarget_ : null; +}; + + +/** + * Returns the target for a given cursor position. + * + * @param {goog.math.Coordinate} position Cursor position. + * @return {Object} Target for position or null if no target was defined + * for the given position. + * @private + */ +goog.fx.AbstractDragDrop.prototype.getTargetFromPosition_ = function(position) { + for (var target, i = 0; target = this.targetList_[i]; i++) { + if (target.box_.contains(position)) { + if (target.scrollableContainer_) { + // If we have a scrollable container we will need to make sure + // we account for clipping of the scroll area + var box = target.scrollableContainer_.box_; + if (box.contains(position)) { + return target; + } + } else { + return target; + } + } + } + + return null; +}; + + +/** + * Checks whatever a given point is inside a given box. + * + * @param {number} x Cursor position on the x-axis. + * @param {number} y Cursor position on the y-axis. + * @param {goog.math.Box} box Box to check position against. + * @return {boolean} Whether the given point is inside {@code box}. + * @protected + * @deprecated Use goog.math.Box.contains. + */ +goog.fx.AbstractDragDrop.prototype.isInside = function(x, y, box) { + return x >= box.left && + x < box.right && + y >= box.top && + y < box.bottom; +}; + + +/** + * Gets the scroll distance as a coordinate object, using + * the window of the current drag element's dom. + * @return {!goog.math.Coordinate} Object with scroll offsets 'x' and 'y'. + * @protected + */ +goog.fx.AbstractDragDrop.prototype.getScrollPos = function() { + return goog.dom.getDomHelper(this.dragEl_).getDocumentScroll(); +}; + + +/** + * Get the position of a drag event. + * @param {goog.fx.DragEvent} event Drag event. + * @return {!goog.math.Coordinate} Position of the event. + * @protected + */ +goog.fx.AbstractDragDrop.prototype.getEventPosition = function(event) { + var scroll = this.getScrollPos(); + return new goog.math.Coordinate(event.clientX + scroll.x, + event.clientY + scroll.y); +}; + + +/** @override */ +goog.fx.AbstractDragDrop.prototype.disposeInternal = function() { + goog.fx.AbstractDragDrop.base(this, 'disposeInternal'); + this.removeItems(); +}; + + + +/** + * Object representing a drag and drop event. + * + * @param {string} type Event type. + * @param {goog.fx.AbstractDragDrop} source Source drag drop object. + * @param {goog.fx.DragDropItem} sourceItem Source item. + * @param {goog.fx.AbstractDragDrop=} opt_target Target drag drop object. + * @param {goog.fx.DragDropItem=} opt_targetItem Target item. + * @param {Element=} opt_targetElement Target element. + * @param {number=} opt_clientX X-Position relative to the screen. + * @param {number=} opt_clientY Y-Position relative to the screen. + * @param {number=} opt_x X-Position relative to the viewport. + * @param {number=} opt_y Y-Position relative to the viewport. + * @param {Object=} opt_subtarget The currently active subtarget. + * @extends {goog.events.Event} + * @constructor + */ +goog.fx.DragDropEvent = function(type, source, sourceItem, + opt_target, opt_targetItem, opt_targetElement, + opt_clientX, opt_clientY, opt_x, opt_y, + opt_subtarget) { + // TODO(eae): Get rid of all the optional parameters and have the caller set + // the fields directly instead. + goog.fx.DragDropEvent.base(this, 'constructor', type); + + /** + * Reference to the source goog.fx.AbstractDragDrop object. + * @type {goog.fx.AbstractDragDrop} + */ + this.dragSource = source; + + /** + * Reference to the source goog.fx.DragDropItem object. + * @type {goog.fx.DragDropItem} + */ + this.dragSourceItem = sourceItem; + + /** + * Reference to the target goog.fx.AbstractDragDrop object. + * @type {goog.fx.AbstractDragDrop|undefined} + */ + this.dropTarget = opt_target; + + /** + * Reference to the target goog.fx.DragDropItem object. + * @type {goog.fx.DragDropItem|undefined} + */ + this.dropTargetItem = opt_targetItem; + + /** + * The actual element of the drop target that is the target for this event. + * @type {Element|undefined} + */ + this.dropTargetElement = opt_targetElement; + + /** + * X-Position relative to the screen. + * @type {number|undefined} + */ + this.clientX = opt_clientX; + + /** + * Y-Position relative to the screen. + * @type {number|undefined} + */ + this.clientY = opt_clientY; + + /** + * X-Position relative to the viewport. + * @type {number|undefined} + */ + this.viewportX = opt_x; + + /** + * Y-Position relative to the viewport. + * @type {number|undefined} + */ + this.viewportY = opt_y; + + /** + * The subtarget that is currently active if a subtargeting function + * is supplied. + * @type {Object|undefined} + */ + this.subtarget = opt_subtarget; +}; +goog.inherits(goog.fx.DragDropEvent, goog.events.Event); + + + +/** + * Class representing a source or target element for drag and drop operations. + * + * @param {Element|string} element Dom Node, or string representation of node + * id, to be used as drag source/drop target. + * @param {Object=} opt_data Data associated with the source/target. + * @throws Error If no element argument is provided or if the type is invalid + * @extends {goog.events.EventTarget} + * @constructor + */ +goog.fx.DragDropItem = function(element, opt_data) { + goog.fx.DragDropItem.base(this, 'constructor'); + + /** + * Reference to drag source/target element + * @type {Element} + */ + this.element = goog.dom.getElement(element); + + /** + * Data associated with element. + * @type {Object|undefined} + */ + this.data = opt_data; + + /** + * Drag object the item belongs to. + * @type {goog.fx.AbstractDragDrop?} + * @private + */ + this.parent_ = null; + + /** + * Event handler for listeners on events that can initiate a drag. + * @type {!goog.events.EventHandler<!goog.fx.DragDropItem>} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + this.registerDisposable(this.eventHandler_); + + if (!this.element) { + throw Error('Invalid argument'); + } +}; +goog.inherits(goog.fx.DragDropItem, goog.events.EventTarget); + + +/** + * The current element being dragged. This is needed because a DragDropItem can + * have multiple elements that can be dragged. + * @type {Element} + * @private + */ +goog.fx.DragDropItem.prototype.currentDragElement_ = null; + + +/** + * Get the data associated with the source/target. + * @return {Object|null|undefined} Data associated with the source/target. + */ +goog.fx.DragDropItem.prototype.getData = function() { + return this.data; +}; + + +/** + * Gets the element that is actually draggable given that the given target was + * attempted to be dragged. This should be overriden when the element that was + * given actually contains many items that can be dragged. From the target, you + * can determine what element should actually be dragged. + * + * @param {Element} target The target that was attempted to be dragged. + * @return {Element} The element that is draggable given the target. If + * none are draggable, this will return null. + */ +goog.fx.DragDropItem.prototype.getDraggableElement = function(target) { + return target; +}; + + +/** + * Gets the element that is currently being dragged. + * + * @return {Element} The element that is currently being dragged. + */ +goog.fx.DragDropItem.prototype.getCurrentDragElement = function() { + return this.currentDragElement_; +}; + + +/** + * Gets all the elements of this item that are potentially draggable/ + * + * @return {!Array<Element>} The draggable elements. + */ +goog.fx.DragDropItem.prototype.getDraggableElements = function() { + return [this.element]; +}; + + +/** + * Event handler for mouse down. + * + * @param {goog.events.BrowserEvent} event Mouse down event. + * @private + */ +goog.fx.DragDropItem.prototype.mouseDown_ = function(event) { + if (!event.isMouseActionButton()) { + return; + } + + // Get the draggable element for the target. + var element = this.getDraggableElement(/** @type {Element} */ (event.target)); + if (element) { + this.maybeStartDrag_(event, element); + } +}; + + +/** + * Sets the dragdrop to which this item belongs. + * @param {goog.fx.AbstractDragDrop} parent The parent dragdrop. + */ +goog.fx.DragDropItem.prototype.setParent = function(parent) { + this.parent_ = parent; +}; + + +/** + * Adds mouse move, mouse out and mouse up handlers. + * + * @param {goog.events.BrowserEvent} event Mouse down event. + * @param {Element} element Element. + * @private + */ +goog.fx.DragDropItem.prototype.maybeStartDrag_ = function(event, element) { + var eventType = goog.events.EventType; + this.eventHandler_. + listen(element, eventType.MOUSEMOVE, this.mouseMove_, false). + listen(element, eventType.MOUSEOUT, this.mouseMove_, false); + + // Capture the MOUSEUP on the document to ensure that we cancel the start + // drag handlers even if the mouse up occurs on some other element. This can + // happen for instance when the mouse down changes the geometry of the element + // clicked on (e.g. through changes in activation styling) such that the mouse + // up occurs outside the original element. + var doc = goog.dom.getOwnerDocument(element); + this.eventHandler_.listen(doc, eventType.MOUSEUP, this.mouseUp_, true); + + this.currentDragElement_ = element; + + this.startPosition_ = new goog.math.Coordinate( + event.clientX, event.clientY); + + event.preventDefault(); +}; + + +/** + * Event handler for mouse move. Starts drag operation if moved more than the + * threshold value. + * + * @param {goog.events.BrowserEvent} event Mouse move or mouse out event. + * @private + */ +goog.fx.DragDropItem.prototype.mouseMove_ = function(event) { + var distance = Math.abs(event.clientX - this.startPosition_.x) + + Math.abs(event.clientY - this.startPosition_.y); + // Fire dragStart event if the drag distance exceeds the threshold or if the + // mouse leave the dragged element. + // TODO(user): Consider using the goog.fx.Dragger to track the distance + // even after the mouse leaves the dragged element. + var currentDragElement = this.currentDragElement_; + var distanceAboveThreshold = + distance > goog.fx.AbstractDragDrop.initDragDistanceThreshold; + var mouseOutOnDragElement = event.type == goog.events.EventType.MOUSEOUT && + event.target == currentDragElement; + if (distanceAboveThreshold || mouseOutOnDragElement) { + this.eventHandler_.removeAll(); + this.parent_.startDrag(event, this); + } +}; + + +/** + * Event handler for mouse up. Removes mouse move, mouse out and mouse up event + * handlers. + * + * @param {goog.events.BrowserEvent} event Mouse up event. + * @private + */ +goog.fx.DragDropItem.prototype.mouseUp_ = function(event) { + this.eventHandler_.removeAll(); + delete this.startPosition_; + this.currentDragElement_ = null; +}; + + + +/** + * Class representing an active drop target + * + * @param {goog.math.Box} box Box describing the position and dimension of the + * target item. + * @param {goog.fx.AbstractDragDrop=} opt_target Target that contains the item + associated with position. + * @param {goog.fx.DragDropItem=} opt_item Item associated with position. + * @param {Element=} opt_element Element of item associated with position. + * @constructor + * @private + */ +goog.fx.ActiveDropTarget_ = function(box, opt_target, opt_item, opt_element) { + + /** + * Box describing the position and dimension of the target item + * @type {goog.math.Box} + * @private + */ + this.box_ = box; + + /** + * Target that contains the item associated with position + * @type {goog.fx.AbstractDragDrop|undefined} + * @private + */ + this.target_ = opt_target; + + /** + * Item associated with position + * @type {goog.fx.DragDropItem|undefined} + * @private + */ + this.item_ = opt_item; + + /** + * The draggable element of the item associated with position. + * @type {Element|undefined} + * @private + */ + this.element_ = opt_element; +}; + + +/** + * If this target is in a scrollable container this is it. + * @type {goog.fx.ScrollableContainer_} + * @private + */ +goog.fx.ActiveDropTarget_.prototype.scrollableContainer_ = null; + + + +/** + * Class for representing a scrollable container + * @param {Element} element the scrollable element. + * @constructor + * @private + */ +goog.fx.ScrollableContainer_ = function(element) { + + /** + * The targets that lie within this container. + * @type {Array<goog.fx.ActiveDropTarget_>} + * @private + */ + this.containedTargets_ = []; + + /** + * The element that is this container + * @type {Element} + * @private + */ + this.element_ = element; + + /** + * The saved scroll left location for calculating deltas. + * @type {number} + * @private + */ + this.savedScrollLeft_ = 0; + + /** + * The saved scroll top location for calculating deltas. + * @type {number} + * @private + */ + this.savedScrollTop_ = 0; + + /** + * The space occupied by the container. + * @type {goog.math.Box} + * @private + */ + this.box_ = null; +};
