Update wmts-hidpi, add nicer-api-docs
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
// Copyright 2009 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 Action event wrapper implementation.
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.actionEventWrapper');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.EventWrapper');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event wrapper for action handling. Fires when an element is activated either
|
||||
* by clicking it or by focusing it and pressing Enter.
|
||||
*
|
||||
* @constructor
|
||||
* @implements {goog.events.EventWrapper}
|
||||
* @private
|
||||
*/
|
||||
goog.events.ActionEventWrapper_ = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Singleton instance of ActionEventWrapper_.
|
||||
* @type {goog.events.ActionEventWrapper_}
|
||||
*/
|
||||
goog.events.actionEventWrapper = new goog.events.ActionEventWrapper_();
|
||||
|
||||
|
||||
/**
|
||||
* Event types used by the wrapper.
|
||||
*
|
||||
* @type {Array.<goog.events.EventType>}
|
||||
* @private
|
||||
*/
|
||||
goog.events.ActionEventWrapper_.EVENT_TYPES_ = [
|
||||
goog.events.EventType.CLICK,
|
||||
goog.userAgent.GECKO ?
|
||||
goog.events.EventType.KEYPRESS :
|
||||
goog.events.EventType.KEYDOWN
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Adds an event listener using the wrapper on a DOM Node or an object that has
|
||||
* implemented {@link goog.events.EventTarget}. A listener can only be added
|
||||
* once to an object.
|
||||
*
|
||||
* @param {goog.events.ListenableType} target The target to listen to events on.
|
||||
* @param {Function|Object} listener Callback method, or an object with a
|
||||
* handleEvent function.
|
||||
* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
|
||||
* false).
|
||||
* @param {Object=} opt_scope Element in whose scope to call the listener.
|
||||
* @param {goog.events.EventHandler=} opt_eventHandler Event handler to add
|
||||
* listener to.
|
||||
* @override
|
||||
*/
|
||||
goog.events.ActionEventWrapper_.prototype.listen = function(target, listener,
|
||||
opt_capt, opt_scope, opt_eventHandler) {
|
||||
var callback = function(e) {
|
||||
if (e.type == goog.events.EventType.CLICK && e.isMouseActionButton()) {
|
||||
listener.call(opt_scope, e);
|
||||
} else if (e.keyCode == goog.events.KeyCodes.ENTER ||
|
||||
e.keyCode == goog.events.KeyCodes.MAC_ENTER) {
|
||||
// convert keydown to keypress for backward compatibility.
|
||||
e.type = goog.events.EventType.KEYPRESS;
|
||||
listener.call(opt_scope, e);
|
||||
}
|
||||
};
|
||||
callback.listener_ = listener;
|
||||
callback.scope_ = opt_scope;
|
||||
|
||||
if (opt_eventHandler) {
|
||||
opt_eventHandler.listen(target,
|
||||
goog.events.ActionEventWrapper_.EVENT_TYPES_,
|
||||
callback, opt_capt);
|
||||
} else {
|
||||
goog.events.listen(target,
|
||||
goog.events.ActionEventWrapper_.EVENT_TYPES_,
|
||||
callback, opt_capt);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes an event listener added using goog.events.EventWrapper.listen.
|
||||
*
|
||||
* @param {goog.events.ListenableType} target The node to remove listener from.
|
||||
* @param {Function|Object} listener Callback method, or an object with a
|
||||
* handleEvent function.
|
||||
* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
|
||||
* false).
|
||||
* @param {Object=} opt_scope Element in whose scope to call the listener.
|
||||
* @param {goog.events.EventHandler=} opt_eventHandler Event handler to remove
|
||||
* listener from.
|
||||
* @override
|
||||
*/
|
||||
goog.events.ActionEventWrapper_.prototype.unlisten = function(target, listener,
|
||||
opt_capt, opt_scope, opt_eventHandler) {
|
||||
for (var type, j = 0; type = goog.events.ActionEventWrapper_.EVENT_TYPES_[j];
|
||||
j++) {
|
||||
var listeners = goog.events.getListeners(target, type, !!opt_capt);
|
||||
for (var obj, i = 0; obj = listeners[i]; i++) {
|
||||
if (obj.listener.listener_ == listener &&
|
||||
obj.listener.scope_ == opt_scope) {
|
||||
if (opt_eventHandler) {
|
||||
opt_eventHandler.unlisten(target, type, obj.listener, opt_capt,
|
||||
opt_scope);
|
||||
} else {
|
||||
goog.events.unlisten(target, type, obj.listener, opt_capt, opt_scope);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
// Copyright 2007 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 This file contains a class to provide a unified mechanism for
|
||||
* CLICK and enter KEYDOWN events. This provides better accessibility by
|
||||
* providing the given functionality to a keyboard user which is otherwise
|
||||
* would be available only via a mouse click.
|
||||
*
|
||||
* If there is an existing CLICK listener or planning to be added as below -
|
||||
*
|
||||
* <code>this.eventHandler_.listen(el, CLICK, this.onClick_);<code>
|
||||
*
|
||||
* it can be replaced with an ACTION listener as follows:
|
||||
*
|
||||
* <code>this.eventHandler_.listen(
|
||||
* new goog.events.ActionHandler(el),
|
||||
* ACTION,
|
||||
* this.onAction_);<code>
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.ActionEvent');
|
||||
goog.provide('goog.events.ActionHandler');
|
||||
goog.provide('goog.events.ActionHandler.EventType');
|
||||
goog.provide('goog.events.BeforeActionEvent');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper around an element that you want to listen to ACTION events on.
|
||||
* @param {Element|Document} element The element or document to listen on.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.events.ActionHandler = function(element) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* This is the element that we will listen to events on.
|
||||
* @type {Element|Document}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = element;
|
||||
|
||||
goog.events.listen(element, goog.events.ActionHandler.KEY_EVENT_TYPE_,
|
||||
this.handleKeyDown_, false, this);
|
||||
goog.events.listen(element, goog.events.EventType.CLICK,
|
||||
this.handleClick_, false, this);
|
||||
};
|
||||
goog.inherits(goog.events.ActionHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Enum type for the events fired by the action handler
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.ActionHandler.EventType = {
|
||||
ACTION: 'action',
|
||||
BEFOREACTION: 'beforeaction'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Key event type to listen for.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.events.ActionHandler.KEY_EVENT_TYPE_ = goog.userAgent.GECKO ?
|
||||
goog.events.EventType.KEYPRESS :
|
||||
goog.events.EventType.KEYDOWN;
|
||||
|
||||
|
||||
/**
|
||||
* Handles key press events.
|
||||
* @param {!goog.events.BrowserEvent} e The key press event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ActionHandler.prototype.handleKeyDown_ = function(e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.ENTER ||
|
||||
goog.userAgent.WEBKIT && e.keyCode == goog.events.KeyCodes.MAC_ENTER) {
|
||||
this.dispatchEvents_(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mouse events.
|
||||
* @param {!goog.events.BrowserEvent} e The click event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ActionHandler.prototype.handleClick_ = function(e) {
|
||||
this.dispatchEvents_(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches BeforeAction and Action events to the element
|
||||
* @param {!goog.events.BrowserEvent} e The event causing dispatches.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ActionHandler.prototype.dispatchEvents_ = function(e) {
|
||||
var beforeActionEvent = new goog.events.BeforeActionEvent(e);
|
||||
|
||||
// Allow application specific logic here before the ACTION event.
|
||||
// For example, Gmail uses this event to restore keyboard focus
|
||||
if (!this.dispatchEvent(beforeActionEvent)) {
|
||||
// If the listener swallowed the BEFOREACTION event, don't dispatch the
|
||||
// ACTION event.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Wrap up original event and send it off
|
||||
var actionEvent = new goog.events.ActionEvent(e);
|
||||
try {
|
||||
this.dispatchEvent(actionEvent);
|
||||
} finally {
|
||||
// Stop propagating the event
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.ActionHandler.prototype.disposeInternal = function() {
|
||||
goog.events.ActionHandler.superClass_.disposeInternal.call(this);
|
||||
goog.events.unlisten(this.element_, goog.events.ActionHandler.KEY_EVENT_TYPE_,
|
||||
this.handleKeyDown_, false, this);
|
||||
goog.events.unlisten(this.element_, goog.events.EventType.CLICK,
|
||||
this.handleClick_, false, this);
|
||||
delete this.element_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class is used for the goog.events.ActionHandler.EventType.ACTION event.
|
||||
* @param {!goog.events.BrowserEvent} browserEvent Browser event object.
|
||||
* @constructor
|
||||
* @extends {goog.events.BrowserEvent}
|
||||
*/
|
||||
goog.events.ActionEvent = function(browserEvent) {
|
||||
goog.events.BrowserEvent.call(this, browserEvent.getBrowserEvent());
|
||||
this.type = goog.events.ActionHandler.EventType.ACTION;
|
||||
};
|
||||
goog.inherits(goog.events.ActionEvent, goog.events.BrowserEvent);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class is used for the goog.events.ActionHandler.EventType.BEFOREACTION
|
||||
* event. BEFOREACTION gives a chance to the application so the keyboard focus
|
||||
* can be restored back, if required.
|
||||
* @param {!goog.events.BrowserEvent} browserEvent Browser event object.
|
||||
* @constructor
|
||||
* @extends {goog.events.BrowserEvent}
|
||||
*/
|
||||
goog.events.BeforeActionEvent = function(browserEvent) {
|
||||
goog.events.BrowserEvent.call(this, browserEvent.getBrowserEvent());
|
||||
this.type = goog.events.ActionHandler.EventType.BEFOREACTION;
|
||||
};
|
||||
goog.inherits(goog.events.BeforeActionEvent, goog.events.BrowserEvent);
|
||||
@@ -0,0 +1,411 @@
|
||||
// Copyright 2005 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 patched, standardized event object for browser events.
|
||||
*
|
||||
* <pre>
|
||||
* The patched event object contains the following members:
|
||||
* - type {string} Event type, e.g. 'click'
|
||||
* - timestamp {Date} A date object for when the event was fired
|
||||
* - target {Object} The element that actually triggered the event
|
||||
* - currentTarget {Object} The element the listener is attached to
|
||||
* - relatedTarget {Object} For mouseover and mouseout, the previous object
|
||||
* - offsetX {number} X-coordinate relative to target
|
||||
* - offsetY {number} Y-coordinate relative to target
|
||||
* - clientX {number} X-coordinate relative to viewport
|
||||
* - clientY {number} Y-coordinate relative to viewport
|
||||
* - screenX {number} X-coordinate relative to the edge of the screen
|
||||
* - screenY {number} Y-coordinate relative to the edge of the screen
|
||||
* - button {number} Mouse button. Use isButton() to test.
|
||||
* - keyCode {number} Key-code
|
||||
* - ctrlKey {boolean} Was ctrl key depressed
|
||||
* - altKey {boolean} Was alt key depressed
|
||||
* - shiftKey {boolean} Was shift key depressed
|
||||
* - metaKey {boolean} Was meta key depressed
|
||||
* - defaultPrevented {boolean} Whether the default action has been prevented
|
||||
* - state {Object} History state object
|
||||
*
|
||||
* NOTE: The keyCode member contains the raw browser keyCode. For normalized
|
||||
* key and character code use {@link goog.events.KeyHandler}.
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.BrowserEvent');
|
||||
goog.provide('goog.events.BrowserEvent.MouseButton');
|
||||
|
||||
goog.require('goog.events.BrowserFeature');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.reflect');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Accepts a browser event object and creates a patched, cross browser event
|
||||
* object.
|
||||
* The content of this object will not be initialized if no event object is
|
||||
* provided. If this is the case, init() needs to be invoked separately.
|
||||
* @param {Event=} opt_e Browser event object.
|
||||
* @param {EventTarget=} opt_currentTarget Current target for event.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
|
||||
if (opt_e) {
|
||||
this.init(opt_e, opt_currentTarget);
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.events.BrowserEvent, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Normalized button constants for the mouse.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.MouseButton = {
|
||||
LEFT: 0,
|
||||
MIDDLE: 1,
|
||||
RIGHT: 2
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static data for mapping mouse buttons.
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
goog.events.BrowserEvent.IEButtonMap = [
|
||||
1, // LEFT
|
||||
4, // MIDDLE
|
||||
2 // RIGHT
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Target that fired the event.
|
||||
* @override
|
||||
* @type {Node}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.target = null;
|
||||
|
||||
|
||||
/**
|
||||
* Node that had the listener attached.
|
||||
* @override
|
||||
* @type {Node|undefined}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.currentTarget;
|
||||
|
||||
|
||||
/**
|
||||
* For mouseover and mouseout events, the related object for the event.
|
||||
* @type {Node}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.relatedTarget = null;
|
||||
|
||||
|
||||
/**
|
||||
* X-coordinate relative to target.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.offsetX = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Y-coordinate relative to target.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.offsetY = 0;
|
||||
|
||||
|
||||
/**
|
||||
* X-coordinate relative to the window.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.clientX = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Y-coordinate relative to the window.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.clientY = 0;
|
||||
|
||||
|
||||
/**
|
||||
* X-coordinate relative to the monitor.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.screenX = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Y-coordinate relative to the monitor.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.screenY = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Which mouse button was pressed.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.button = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Keycode of key press.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.keyCode = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Keycode of key press.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.charCode = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Whether control was pressed at time of event.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.ctrlKey = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether alt was pressed at time of event.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.altKey = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether shift was pressed at time of event.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.shiftKey = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the meta key was pressed at time of event.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.metaKey = false;
|
||||
|
||||
|
||||
/**
|
||||
* History state object, only set for PopState events where it's a copy of the
|
||||
* state object provided to pushState or replaceState.
|
||||
* @type {Object}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.state;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the default platform modifier key was pressed at time of event.
|
||||
* (This is control for all platforms except Mac, where it's Meta.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.platformModifierKey = false;
|
||||
|
||||
|
||||
/**
|
||||
* The browser event object.
|
||||
* @type {Event}
|
||||
* @private
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.event_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Accepts a browser event object and creates a patched, cross browser event
|
||||
* object.
|
||||
* @param {Event} e Browser event object.
|
||||
* @param {EventTarget=} opt_currentTarget Current target for event.
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
|
||||
var type = this.type = e.type;
|
||||
goog.events.Event.call(this, type);
|
||||
|
||||
// TODO(nicksantos): Change this.target to type EventTarget.
|
||||
this.target = /** @type {Node} */ (e.target) || e.srcElement;
|
||||
|
||||
// TODO(nicksantos): Change this.currentTarget to type EventTarget.
|
||||
this.currentTarget = /** @type {Node} */ (opt_currentTarget);
|
||||
|
||||
var relatedTarget = /** @type {Node} */ (e.relatedTarget);
|
||||
if (relatedTarget) {
|
||||
// There's a bug in FireFox where sometimes, relatedTarget will be a
|
||||
// chrome element, and accessing any property of it will get a permission
|
||||
// denied exception. See:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=497780
|
||||
if (goog.userAgent.GECKO) {
|
||||
if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
|
||||
relatedTarget = null;
|
||||
}
|
||||
}
|
||||
// TODO(arv): Use goog.events.EventType when it has been refactored into its
|
||||
// own file.
|
||||
} else if (type == goog.events.EventType.MOUSEOVER) {
|
||||
relatedTarget = e.fromElement;
|
||||
} else if (type == goog.events.EventType.MOUSEOUT) {
|
||||
relatedTarget = e.toElement;
|
||||
}
|
||||
|
||||
this.relatedTarget = relatedTarget;
|
||||
|
||||
// Webkit emits a lame warning whenever layerX/layerY is accessed.
|
||||
// http://code.google.com/p/chromium/issues/detail?id=101733
|
||||
this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
|
||||
e.offsetX : e.layerX;
|
||||
this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
|
||||
e.offsetY : e.layerY;
|
||||
|
||||
this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
|
||||
this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
|
||||
this.screenX = e.screenX || 0;
|
||||
this.screenY = e.screenY || 0;
|
||||
|
||||
this.button = e.button;
|
||||
|
||||
this.keyCode = e.keyCode || 0;
|
||||
this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
|
||||
this.ctrlKey = e.ctrlKey;
|
||||
this.altKey = e.altKey;
|
||||
this.shiftKey = e.shiftKey;
|
||||
this.metaKey = e.metaKey;
|
||||
this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
|
||||
this.state = e.state;
|
||||
this.event_ = e;
|
||||
if (e.defaultPrevented) {
|
||||
this.preventDefault();
|
||||
}
|
||||
delete this.propagationStopped_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tests to see which button was pressed during the event. This is really only
|
||||
* useful in IE and Gecko browsers. And in IE, it's only useful for
|
||||
* mousedown/mouseup events, because click only fires for the left mouse button.
|
||||
*
|
||||
* Safari 2 only reports the left button being clicked, and uses the value '1'
|
||||
* instead of 0. Opera only reports a mousedown event for the middle button, and
|
||||
* no mouse events for the right button. Opera has default behavior for left and
|
||||
* middle click that can only be overridden via a configuration setting.
|
||||
*
|
||||
* There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent.MouseButton} button The button
|
||||
* to test for.
|
||||
* @return {boolean} True if button was pressed.
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.isButton = function(button) {
|
||||
if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
|
||||
if (this.type == 'click') {
|
||||
return button == goog.events.BrowserEvent.MouseButton.LEFT;
|
||||
} else {
|
||||
return !!(this.event_.button &
|
||||
goog.events.BrowserEvent.IEButtonMap[button]);
|
||||
}
|
||||
} else {
|
||||
return this.event_.button == button;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether this has an "action"-producing mouse button.
|
||||
*
|
||||
* By definition, this includes left-click on windows/linux, and left-click
|
||||
* without the ctrl key on Macs.
|
||||
*
|
||||
* @return {boolean} The result.
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
|
||||
// Webkit does not ctrl+click to be a right-click, so we
|
||||
// normalize it to behave like Gecko and Opera.
|
||||
return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
|
||||
!(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.stopPropagation = function() {
|
||||
goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
|
||||
if (this.event_.stopPropagation) {
|
||||
this.event_.stopPropagation();
|
||||
} else {
|
||||
this.event_.cancelBubble = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.preventDefault = function() {
|
||||
goog.events.BrowserEvent.superClass_.preventDefault.call(this);
|
||||
var be = this.event_;
|
||||
if (!be.preventDefault) {
|
||||
be.returnValue = false;
|
||||
if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
|
||||
/** @preserveTry */
|
||||
try {
|
||||
// Most keys can be prevented using returnValue. Some special keys
|
||||
// require setting the keyCode to -1 as well:
|
||||
//
|
||||
// In IE7:
|
||||
// F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
|
||||
//
|
||||
// In IE8:
|
||||
// Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
|
||||
//
|
||||
// We therefore do this for all function keys as well as when Ctrl key
|
||||
// is pressed.
|
||||
var VK_F1 = 112;
|
||||
var VK_F12 = 123;
|
||||
if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
|
||||
be.keyCode = -1;
|
||||
}
|
||||
} catch (ex) {
|
||||
// IE throws an 'access denied' exception when trying to change
|
||||
// keyCode in some situations (e.g. srcElement is input[type=file],
|
||||
// or srcElement is an anchor tag rewritten by parent's innerHTML).
|
||||
// Do nothing in this case.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
be.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Event} The underlying browser event object.
|
||||
*/
|
||||
goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
|
||||
return this.event_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.BrowserEvent.prototype.disposeInternal = function() {
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2010 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 Browser capability checks for the events package.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.events.BrowserFeature');
|
||||
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Enum of browser capabilities.
|
||||
* @enum {boolean}
|
||||
*/
|
||||
goog.events.BrowserFeature = {
|
||||
/**
|
||||
* Whether the button attribute of the event is W3C compliant. False in
|
||||
* Internet Explorer prior to version 9; document-version dependent.
|
||||
*/
|
||||
HAS_W3C_BUTTON: !goog.userAgent.IE ||
|
||||
goog.userAgent.isDocumentModeOrHigher(9),
|
||||
|
||||
/**
|
||||
* Whether the browser supports full W3C event model.
|
||||
*/
|
||||
HAS_W3C_EVENT_SUPPORT: !goog.userAgent.IE ||
|
||||
goog.userAgent.isDocumentModeOrHigher(9),
|
||||
|
||||
/**
|
||||
* To prevent default in IE7-8 for certain keydown events we need set the
|
||||
* keyCode to -1.
|
||||
*/
|
||||
SET_KEY_CODE_TO_PREVENT_DEFAULT: goog.userAgent.IE &&
|
||||
!goog.userAgent.isVersionOrHigher('9'),
|
||||
|
||||
/**
|
||||
* Whether the {@code navigator.onLine} property is supported.
|
||||
*/
|
||||
HAS_NAVIGATOR_ONLINE_PROPERTY: !goog.userAgent.WEBKIT ||
|
||||
goog.userAgent.isVersionOrHigher('528'),
|
||||
|
||||
/**
|
||||
* Whether HTML5 network online/offline events are supported.
|
||||
*/
|
||||
HAS_HTML5_NETWORK_EVENT_SUPPORT:
|
||||
goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9b') ||
|
||||
goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8') ||
|
||||
goog.userAgent.OPERA && goog.userAgent.isVersionOrHigher('9.5') ||
|
||||
goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('528'),
|
||||
|
||||
/**
|
||||
* Whether HTML5 network events fire on document.body, or otherwise the
|
||||
* window.
|
||||
*/
|
||||
HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
|
||||
goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('8') ||
|
||||
goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
|
||||
|
||||
/**
|
||||
* Whether touch is enabled in the browser.
|
||||
*/
|
||||
TOUCH_ENABLED:
|
||||
('ontouchstart' in goog.global ||
|
||||
!!(goog.global['document'] &&
|
||||
document.documentElement &&
|
||||
'ontouchstart' in document.documentElement) ||
|
||||
// IE10 uses non-standard touch events, so it has a different check.
|
||||
!!(goog.global['navigator'] &&
|
||||
goog.global['navigator']['msMaxTouchPoints']))
|
||||
};
|
||||
154
nicer-api-docs/closure-library/closure/goog/events/event.js
Normal file
154
nicer-api-docs/closure-library/closure/goog/events/event.js
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2005 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 base class for event objects.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.events.Event');
|
||||
goog.provide('goog.events.EventLike');
|
||||
|
||||
// goog.events.Event no longer depends on goog.Disposable. Keep requiring
|
||||
// goog.Disposable here to not break projects which assume this dependency.
|
||||
goog.require('goog.Disposable');
|
||||
|
||||
|
||||
/**
|
||||
* A typedef for event like objects that are dispatchable via the
|
||||
* goog.events.dispatchEvent function. strings are treated as the type for a
|
||||
* goog.events.Event. Objects are treated as an extension of a new
|
||||
* goog.events.Event with the type property of the object being used as the type
|
||||
* of the Event.
|
||||
* @typedef {string|Object|goog.events.Event}
|
||||
*/
|
||||
goog.events.EventLike;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A base class for event objects, so that they can support preventDefault and
|
||||
* stopPropagation.
|
||||
*
|
||||
* @param {string} type Event Type.
|
||||
* @param {Object=} opt_target Reference to the object that is the target of
|
||||
* this event. It has to implement the {@code EventTarget} interface
|
||||
* declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.
|
||||
* @constructor
|
||||
*/
|
||||
goog.events.Event = function(type, opt_target) {
|
||||
/**
|
||||
* Event type.
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = type;
|
||||
|
||||
/**
|
||||
* Target of the event.
|
||||
* @type {Object|undefined}
|
||||
*/
|
||||
this.target = opt_target;
|
||||
|
||||
/**
|
||||
* Object that had the listener attached.
|
||||
* @type {Object|undefined}
|
||||
*/
|
||||
this.currentTarget = this.target;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* For backwards compatibility (goog.events.Event used to inherit
|
||||
* goog.Disposable).
|
||||
* @deprecated Events don't need to be disposed.
|
||||
*/
|
||||
goog.events.Event.prototype.disposeInternal = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* For backwards compatibility (goog.events.Event used to inherit
|
||||
* goog.Disposable).
|
||||
* @deprecated Events don't need to be disposed.
|
||||
*/
|
||||
goog.events.Event.prototype.dispose = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether to cancel the event in internal capture/bubble processing for IE.
|
||||
* @type {boolean}
|
||||
* @suppress {underscore} Technically public, but referencing this outside
|
||||
* this package is strongly discouraged.
|
||||
*/
|
||||
goog.events.Event.prototype.propagationStopped_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the default action has been prevented.
|
||||
* This is a property to match the W3C specification at {@link
|
||||
* http://www.w3.org/TR/DOM-Level-3-Events/#events-event-type-defaultPrevented}.
|
||||
* Must be treated as read-only outside the class.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.Event.prototype.defaultPrevented = false;
|
||||
|
||||
|
||||
/**
|
||||
* Return value for in internal capture/bubble processing for IE.
|
||||
* @type {boolean}
|
||||
* @suppress {underscore} Technically public, but referencing this outside
|
||||
* this package is strongly discouraged.
|
||||
*/
|
||||
goog.events.Event.prototype.returnValue_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Stops event propagation.
|
||||
*/
|
||||
goog.events.Event.prototype.stopPropagation = function() {
|
||||
this.propagationStopped_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Prevents the default action, for example a link redirecting to a url.
|
||||
*/
|
||||
goog.events.Event.prototype.preventDefault = function() {
|
||||
this.defaultPrevented = true;
|
||||
this.returnValue_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stops the propagation of the event. It is equivalent to
|
||||
* {@code e.stopPropagation()}, but can be used as the callback argument of
|
||||
* {@link goog.events.listen} without declaring another function.
|
||||
* @param {!goog.events.Event} e An event.
|
||||
*/
|
||||
goog.events.Event.stopPropagation = function(e) {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Prevents the default action. It is equivalent to
|
||||
* {@code e.preventDefault()}, but can be used as the callback argument of
|
||||
* {@link goog.events.listen} without declaring another function.
|
||||
* @param {!goog.events.Event} e An event.
|
||||
*/
|
||||
goog.events.Event.preventDefault = function(e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
@@ -0,0 +1,291 @@
|
||||
// Copyright 2005 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 Class to create objects which want to handle multiple events
|
||||
* and have their listeners easily cleaned up via a dispose method.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* function Something() {
|
||||
* goog.base(this);
|
||||
*
|
||||
* ... set up object ...
|
||||
*
|
||||
* // Add event listeners
|
||||
* this.listen(this.starEl, goog.events.EventType.CLICK, this.handleStar);
|
||||
* this.listen(this.headerEl, goog.events.EventType.CLICK, this.expand);
|
||||
* this.listen(this.collapseEl, goog.events.EventType.CLICK, this.collapse);
|
||||
* this.listen(this.infoEl, goog.events.EventType.MOUSEOVER, this.showHover);
|
||||
* this.listen(this.infoEl, goog.events.EventType.MOUSEOUT, this.hideHover);
|
||||
* }
|
||||
* goog.inherits(Something, goog.events.EventHandler);
|
||||
*
|
||||
* Something.prototype.disposeInternal = function() {
|
||||
* goog.base(this, 'disposeInternal');
|
||||
* goog.dom.removeNode(this.container);
|
||||
* };
|
||||
*
|
||||
*
|
||||
* // Then elsewhere:
|
||||
*
|
||||
* var activeSomething = null;
|
||||
* function openSomething() {
|
||||
* activeSomething = new Something();
|
||||
* }
|
||||
*
|
||||
* function closeSomething() {
|
||||
* if (activeSomething) {
|
||||
* activeSomething.dispose(); // Remove event listeners
|
||||
* activeSomething = null;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.EventHandler');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Super class for objects that want to easily manage a number of event
|
||||
* listeners. It allows a short cut to listen and also provides a quick way
|
||||
* to remove all events listeners belonging to this object.
|
||||
* @param {Object=} opt_handler Object in whose scope to call the listeners.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.events.EventHandler = function(opt_handler) {
|
||||
goog.Disposable.call(this);
|
||||
this.handler_ = opt_handler;
|
||||
|
||||
/**
|
||||
* Keys for events that are being listened to.
|
||||
* @type {!Object.<!goog.events.Key>}
|
||||
* @private
|
||||
*/
|
||||
this.keys_ = {};
|
||||
};
|
||||
goog.inherits(goog.events.EventHandler, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Utility array used to unify the cases of listening for an array of types
|
||||
* and listening for a single event, without using recursion or allocating
|
||||
* an array each time.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.events.EventHandler.typeArray_ = [];
|
||||
|
||||
|
||||
/**
|
||||
* Listen to an event on a Listenable. If the function is omitted then the
|
||||
* EventHandler's handleEvent method will be used.
|
||||
* @param {goog.events.ListenableType} src Event source.
|
||||
* @param {string|Array.<string>} type Event type to listen for or array of
|
||||
* event types.
|
||||
* @param {Function|Object=} opt_fn Optional callback function to be used as the
|
||||
* listener or an object with handleEvent function.
|
||||
* @param {boolean=} opt_capture Optional whether to use capture phase.
|
||||
* @param {Object=} opt_handler Object in whose scope to call the listener.
|
||||
* @return {goog.events.EventHandler} This object, allowing for chaining of
|
||||
* calls.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.listen = function(src, type, opt_fn,
|
||||
opt_capture,
|
||||
opt_handler) {
|
||||
if (!goog.isArray(type)) {
|
||||
goog.events.EventHandler.typeArray_[0] = /** @type {string} */(type);
|
||||
type = goog.events.EventHandler.typeArray_;
|
||||
}
|
||||
for (var i = 0; i < type.length; i++) {
|
||||
var listenerObj = goog.events.listen(
|
||||
src, type[i], opt_fn || this,
|
||||
opt_capture || false,
|
||||
opt_handler || this.handler_ || this);
|
||||
|
||||
if (goog.DEBUG && !listenerObj) {
|
||||
// Some tests mock goog.events.listen, thus ensuring that
|
||||
// they are never testing the real thing anyway, hence this is safe
|
||||
// (except that #getListenerCount() will return the wrong value).
|
||||
return this;
|
||||
}
|
||||
|
||||
var key = listenerObj.key;
|
||||
this.keys_[key] = listenerObj;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Listen to an event on a Listenable. If the function is omitted, then the
|
||||
* EventHandler's handleEvent method will be used. After the event has fired the
|
||||
* event listener is removed from the target. If an array of event types is
|
||||
* provided, each event type will be listened to once.
|
||||
* @param {goog.events.ListenableType} src Event source.
|
||||
* @param {string|Array.<string>} type Event type to listen for or array of
|
||||
* event types.
|
||||
* @param {Function|Object=} opt_fn Optional callback function to be used as the
|
||||
* listener or an object with handleEvent function.
|
||||
* @param {boolean=} opt_capture Optional whether to use capture phase.
|
||||
* @param {Object=} opt_handler Object in whose scope to call the listener.
|
||||
* @return {goog.events.EventHandler} This object, allowing for chaining of
|
||||
* calls.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.listenOnce = function(src, type, opt_fn,
|
||||
opt_capture,
|
||||
opt_handler) {
|
||||
if (goog.isArray(type)) {
|
||||
for (var i = 0; i < type.length; i++) {
|
||||
this.listenOnce(src, type[i], opt_fn, opt_capture, opt_handler);
|
||||
}
|
||||
} else {
|
||||
var listenerObj = goog.events.listenOnce(
|
||||
src, type, opt_fn || this, opt_capture,
|
||||
opt_handler || this.handler_ || this);
|
||||
var key = listenerObj.key;
|
||||
this.keys_[key] = listenerObj;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an event listener with a specific event wrapper on a DOM Node or an
|
||||
* object that has implemented {@link goog.events.EventTarget}. A listener can
|
||||
* only be added once to an object.
|
||||
*
|
||||
* @param {EventTarget|goog.events.EventTarget} src The node to listen to
|
||||
* events on.
|
||||
* @param {goog.events.EventWrapper} wrapper Event wrapper to use.
|
||||
* @param {Function|Object} listener Callback method, or an object with a
|
||||
* handleEvent function.
|
||||
* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
|
||||
* false).
|
||||
* @param {Object=} opt_handler Element in whose scope to call the listener.
|
||||
* @return {goog.events.EventHandler} This object, allowing for chaining of
|
||||
* calls.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.listenWithWrapper = function(src, wrapper,
|
||||
listener, opt_capt, opt_handler) {
|
||||
wrapper.listen(src, listener, opt_capt, opt_handler || this.handler_ || this,
|
||||
this);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Number of listeners registered by this handler.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.getListenerCount = function() {
|
||||
var count = 0;
|
||||
for (var key in this.keys_) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.keys_, key)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Unlistens on an event.
|
||||
* @param {goog.events.ListenableType} src Event source.
|
||||
* @param {string|Array.<string>} type Event type to listen for.
|
||||
* @param {Function|Object=} opt_fn Optional callback function to be used as the
|
||||
* listener or an object with handleEvent function.
|
||||
* @param {boolean=} opt_capture Optional whether to use capture phase.
|
||||
* @param {Object=} opt_handler Object in whose scope to call the listener.
|
||||
* @return {goog.events.EventHandler} This object, allowing for chaining of
|
||||
* calls.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.unlisten = function(src, type, opt_fn,
|
||||
opt_capture,
|
||||
opt_handler) {
|
||||
if (goog.isArray(type)) {
|
||||
for (var i = 0; i < type.length; i++) {
|
||||
this.unlisten(src, type[i], opt_fn, opt_capture, opt_handler);
|
||||
}
|
||||
} else {
|
||||
var listener = goog.events.getListener(src, type, opt_fn || this,
|
||||
opt_capture, opt_handler || this.handler_ || this);
|
||||
|
||||
if (listener) {
|
||||
goog.events.unlistenByKey(listener);
|
||||
delete this.keys_[listener.key];
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes an event listener which was added with listenWithWrapper().
|
||||
*
|
||||
* @param {EventTarget|goog.events.EventTarget} src The target to stop
|
||||
* listening to events on.
|
||||
* @param {goog.events.EventWrapper} wrapper Event wrapper to use.
|
||||
* @param {Function|Object} listener The listener function to remove.
|
||||
* @param {boolean=} opt_capt In DOM-compliant browsers, this determines
|
||||
* whether the listener is fired during the capture or bubble phase of the
|
||||
* event.
|
||||
* @param {Object=} opt_handler Element in whose scope to call the listener.
|
||||
* @return {goog.events.EventHandler} This object, allowing for chaining of
|
||||
* calls.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.unlistenWithWrapper = function(src, wrapper,
|
||||
listener, opt_capt, opt_handler) {
|
||||
wrapper.unlisten(src, listener, opt_capt,
|
||||
opt_handler || this.handler_ || this, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Unlistens to all events.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.removeAll = function() {
|
||||
goog.object.forEach(this.keys_, goog.events.unlistenByKey);
|
||||
this.keys_ = {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disposes of this EventHandler and removes all listeners that it registered.
|
||||
* @override
|
||||
* @protected
|
||||
*/
|
||||
goog.events.EventHandler.prototype.disposeInternal = function() {
|
||||
goog.events.EventHandler.superClass_.disposeInternal.call(this);
|
||||
this.removeAll();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default event handler
|
||||
* @param {goog.events.Event} e Event object.
|
||||
*/
|
||||
goog.events.EventHandler.prototype.handleEvent = function(e) {
|
||||
throw Error('EventHandler.handleEvent not implemented');
|
||||
};
|
||||
1095
nicer-api-docs/closure-library/closure/goog/events/events.js
Normal file
1095
nicer-api-docs/closure-library/closure/goog/events/events.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,401 @@
|
||||
// Copyright 2005 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 disposable implementation of a custom
|
||||
* listenable/event target. See also: documentation for
|
||||
* {@code goog.events.Listenable}.
|
||||
*
|
||||
* @author arv@google.com (Erik Arvidsson) [Original implementation]
|
||||
* @author pupius@google.com (Daniel Pupius) [Port to use goog.events]
|
||||
* @see ../demos/eventtarget.html
|
||||
* @see goog.events.Listenable
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.EventTarget');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.Listenable');
|
||||
goog.require('goog.events.ListenerMap');
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of {@code goog.events.Listenable} with full W3C
|
||||
* EventTarget-like support (capture/bubble mechanism, stopping event
|
||||
* propagation, preventing default actions).
|
||||
*
|
||||
* You may subclass this class to turn your class into a Listenable.
|
||||
*
|
||||
* Unless propagation is stopped, an event dispatched by an
|
||||
* EventTarget will bubble to the parent returned by
|
||||
* {@code getParentEventTarget}. To set the parent, call
|
||||
* {@code setParentEventTarget}. Subclasses that don't support
|
||||
* changing the parent can override the setter to throw an error.
|
||||
*
|
||||
* Example usage:
|
||||
* <pre>
|
||||
* var source = new goog.events.EventTarget();
|
||||
* function handleEvent(e) {
|
||||
* alert('Type: ' + e.type + '; Target: ' + e.target);
|
||||
* }
|
||||
* source.listen('foo', handleEvent);
|
||||
* // Or: goog.events.listen(source, 'foo', handleEvent);
|
||||
* ...
|
||||
* source.dispatchEvent('foo'); // will call handleEvent
|
||||
* ...
|
||||
* source.unlisten('foo', handleEvent);
|
||||
* // Or: goog.events.unlisten(source, 'foo', handleEvent);
|
||||
* </pre>
|
||||
*
|
||||
* TODO(user): Consider writing a parallel class to this that
|
||||
* does not implement goog.Disposable.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
* @implements {goog.events.Listenable}
|
||||
*/
|
||||
goog.events.EventTarget = function() {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* Maps of event type to an array of listeners.
|
||||
* @private {!goog.events.ListenerMap}
|
||||
*/
|
||||
this.eventTargetListeners_ = new goog.events.ListenerMap(this);
|
||||
|
||||
/**
|
||||
* The object to use for event.target. Useful when mixing in an
|
||||
* EventTarget to another object.
|
||||
* @private {!Object}
|
||||
*/
|
||||
this.actualEventTarget_ = this;
|
||||
};
|
||||
goog.inherits(goog.events.EventTarget, goog.Disposable);
|
||||
goog.events.Listenable.addImplementation(goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* An artificial cap on the number of ancestors you can have. This is mainly
|
||||
* for loop detection.
|
||||
* @const {number}
|
||||
* @private
|
||||
*/
|
||||
goog.events.EventTarget.MAX_ANCESTORS_ = 1000;
|
||||
|
||||
|
||||
/**
|
||||
* Parent event target, used during event bubbling.
|
||||
*
|
||||
* TODO(user): Change this to goog.events.Listenable. This
|
||||
* currently breaks people who expect getParentEventTarget to return
|
||||
* goog.events.EventTarget.
|
||||
*
|
||||
* @type {goog.events.EventTarget}
|
||||
* @private
|
||||
*/
|
||||
goog.events.EventTarget.prototype.parentEventTarget_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parent of this event target to use for bubbling.
|
||||
*
|
||||
* @return {goog.events.EventTarget} The parent EventTarget or null if
|
||||
* there is no parent.
|
||||
* @override
|
||||
*/
|
||||
goog.events.EventTarget.prototype.getParentEventTarget = function() {
|
||||
return this.parentEventTarget_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the parent of this event target to use for capture/bubble
|
||||
* mechanism.
|
||||
* @param {goog.events.EventTarget} parent Parent listenable (null if none).
|
||||
*/
|
||||
goog.events.EventTarget.prototype.setParentEventTarget = function(parent) {
|
||||
this.parentEventTarget_ = parent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an event listener to the event target. The same handler can only be
|
||||
* added once per the type. Even if you add the same handler multiple times
|
||||
* using the same type then it will only be called once when the event is
|
||||
* dispatched.
|
||||
*
|
||||
* Supported for legacy but use goog.events.listen(src, type, handler) instead.
|
||||
*
|
||||
* TODO(user): Deprecate this.
|
||||
*
|
||||
* @param {string} type The type of the event to listen for.
|
||||
* @param {Function|Object} handler The function to handle the event. The
|
||||
* handler can also be an object that implements the handleEvent method
|
||||
* which takes the event object as argument.
|
||||
* @param {boolean=} opt_capture In DOM-compliant browsers, this determines
|
||||
* whether the listener is fired during the capture or bubble phase
|
||||
* of the event.
|
||||
* @param {Object=} opt_handlerScope Object in whose scope to call
|
||||
* the listener.
|
||||
*/
|
||||
goog.events.EventTarget.prototype.addEventListener = function(
|
||||
type, handler, opt_capture, opt_handlerScope) {
|
||||
goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes an event listener from the event target. The handler must be the
|
||||
* same object as the one added. If the handler has not been added then
|
||||
* nothing is done.
|
||||
*
|
||||
* TODO(user): Deprecate this.
|
||||
*
|
||||
* @param {string} type The type of the event to listen for.
|
||||
* @param {Function|Object} handler The function to handle the event. The
|
||||
* handler can also be an object that implements the handleEvent method
|
||||
* which takes the event object as argument.
|
||||
* @param {boolean=} opt_capture In DOM-compliant browsers, this determines
|
||||
* whether the listener is fired during the capture or bubble phase
|
||||
* of the event.
|
||||
* @param {Object=} opt_handlerScope Object in whose scope to call
|
||||
* the listener.
|
||||
*/
|
||||
goog.events.EventTarget.prototype.removeEventListener = function(
|
||||
type, handler, opt_capture, opt_handlerScope) {
|
||||
goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.dispatchEvent = function(e) {
|
||||
this.assertInitialized_();
|
||||
|
||||
var ancestorsTree, ancestor = this.getParentEventTarget();
|
||||
if (ancestor) {
|
||||
ancestorsTree = [];
|
||||
var ancestorCount = 1;
|
||||
for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
|
||||
ancestorsTree.push(ancestor);
|
||||
goog.asserts.assert(
|
||||
(++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),
|
||||
'infinite loop');
|
||||
}
|
||||
}
|
||||
|
||||
return goog.events.EventTarget.dispatchEventInternal_(
|
||||
this.actualEventTarget_, e, ancestorsTree);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes listeners from this object. Classes that extend EventTarget may
|
||||
* need to override this method in order to remove references to DOM Elements
|
||||
* and additional listeners.
|
||||
* @override
|
||||
*/
|
||||
goog.events.EventTarget.prototype.disposeInternal = function() {
|
||||
goog.events.EventTarget.superClass_.disposeInternal.call(this);
|
||||
|
||||
this.removeAllListeners();
|
||||
this.parentEventTarget_ = null;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.listen = function(
|
||||
type, listener, opt_useCapture, opt_listenerScope) {
|
||||
this.assertInitialized_();
|
||||
return this.eventTargetListeners_.add(
|
||||
type, listener, false /* callOnce */, opt_useCapture, opt_listenerScope);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.listenOnce = function(
|
||||
type, listener, opt_useCapture, opt_listenerScope) {
|
||||
return this.eventTargetListeners_.add(
|
||||
type, listener, true /* callOnce */, opt_useCapture, opt_listenerScope);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.unlisten = function(
|
||||
type, listener, opt_useCapture, opt_listenerScope) {
|
||||
return this.eventTargetListeners_.remove(
|
||||
type, listener, opt_useCapture, opt_listenerScope);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.unlistenByKey = function(key) {
|
||||
return this.eventTargetListeners_.removeByKey(key);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {
|
||||
// TODO(user): Previously, removeAllListeners can be called on
|
||||
// uninitialized EventTarget, so we preserve that behavior. We
|
||||
// should remove this when usages that rely on that fact are purged.
|
||||
if (!this.eventTargetListeners_) {
|
||||
return 0;
|
||||
}
|
||||
return this.eventTargetListeners_.removeAll(opt_type);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.fireListeners = function(
|
||||
type, capture, eventObject) {
|
||||
// TODO(user): Original code avoids array creation when there
|
||||
// is no listener, so we do the same. If this optimization turns
|
||||
// out to be not required, we can replace this with
|
||||
// getListeners(type, capture) instead, which is simpler.
|
||||
var listenerArray = this.eventTargetListeners_.listeners[type];
|
||||
if (!listenerArray) {
|
||||
return true;
|
||||
}
|
||||
listenerArray = goog.array.clone(listenerArray);
|
||||
|
||||
var rv = true;
|
||||
for (var i = 0; i < listenerArray.length; ++i) {
|
||||
var listener = listenerArray[i];
|
||||
// We might not have a listener if the listener was removed.
|
||||
if (listener && !listener.removed && listener.capture == capture) {
|
||||
var listenerFn = listener.listener;
|
||||
var listenerHandler = listener.handler || listener.src;
|
||||
|
||||
if (listener.callOnce) {
|
||||
this.unlistenByKey(listener);
|
||||
}
|
||||
rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
|
||||
}
|
||||
}
|
||||
|
||||
return rv && eventObject.returnValue_ != false;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.getListeners = function(type, capture) {
|
||||
return this.eventTargetListeners_.getListeners(type, capture);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.getListener = function(
|
||||
type, listener, capture, opt_listenerScope) {
|
||||
return this.eventTargetListeners_.getListener(
|
||||
type, listener, capture, opt_listenerScope);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.EventTarget.prototype.hasListener = function(
|
||||
opt_type, opt_capture) {
|
||||
return this.eventTargetListeners_.hasListener(opt_type, opt_capture);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the target to be used for {@code event.target} when firing
|
||||
* event. Mainly used for testing. For example, see
|
||||
* {@code goog.testing.events.mixinListenable}.
|
||||
* @param {!Object} target The target.
|
||||
*/
|
||||
goog.events.EventTarget.prototype.setTargetForTesting = function(target) {
|
||||
this.actualEventTarget_ = target;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the event target instance is initialized properly.
|
||||
* @private
|
||||
*/
|
||||
goog.events.EventTarget.prototype.assertInitialized_ = function() {
|
||||
goog.asserts.assert(
|
||||
this.eventTargetListeners_,
|
||||
'Event target is not initialized. Did you call the superclass ' +
|
||||
'(goog.events.EventTarget) constructor?');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches the given event on the ancestorsTree.
|
||||
*
|
||||
* TODO(user): Look for a way to reuse this logic in
|
||||
* goog.events, if possible.
|
||||
*
|
||||
* @param {!Object} target The target to dispatch on.
|
||||
* @param {goog.events.Event|Object|string} e The event object.
|
||||
* @param {Array.<goog.events.Listenable>=} opt_ancestorsTree The ancestors
|
||||
* tree of the target, in reverse order from the closest ancestor
|
||||
* to the root event target. May be null if the target has no ancestor.
|
||||
* @return {boolean} If anyone called preventDefault on the event object (or
|
||||
* if any of the listeners returns false) this will also return false.
|
||||
* @private
|
||||
*/
|
||||
goog.events.EventTarget.dispatchEventInternal_ = function(
|
||||
target, e, opt_ancestorsTree) {
|
||||
var type = e.type || /** @type {string} */ (e);
|
||||
|
||||
// If accepting a string or object, create a custom event object so that
|
||||
// preventDefault and stopPropagation work with the event.
|
||||
if (goog.isString(e)) {
|
||||
e = new goog.events.Event(e, target);
|
||||
} else if (!(e instanceof goog.events.Event)) {
|
||||
var oldEvent = e;
|
||||
e = new goog.events.Event(type, target);
|
||||
goog.object.extend(e, oldEvent);
|
||||
} else {
|
||||
e.target = e.target || target;
|
||||
}
|
||||
|
||||
var rv = true, currentTarget;
|
||||
|
||||
// Executes all capture listeners on the ancestors, if any.
|
||||
if (opt_ancestorsTree) {
|
||||
for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
|
||||
i--) {
|
||||
currentTarget = e.currentTarget = opt_ancestorsTree[i];
|
||||
rv = currentTarget.fireListeners(type, true, e) && rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Executes capture and bubble listeners on the target.
|
||||
if (!e.propagationStopped_) {
|
||||
currentTarget = e.currentTarget = target;
|
||||
rv = currentTarget.fireListeners(type, true, e) && rv;
|
||||
if (!e.propagationStopped_) {
|
||||
rv = currentTarget.fireListeners(type, false, e) && rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Executes all bubble listeners on the ancestors, if any.
|
||||
if (opt_ancestorsTree) {
|
||||
for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
|
||||
currentTarget = e.currentTarget = opt_ancestorsTree[i];
|
||||
rv = currentTarget.fireListeners(type, false, e) && rv;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
165
nicer-api-docs/closure-library/closure/goog/events/eventtype.js
Normal file
165
nicer-api-docs/closure-library/closure/goog/events/eventtype.js
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright 2010 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 Event Types.
|
||||
*
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
* @author mirkov@google.com (Mirko Visontai)
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.events.EventType');
|
||||
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Constants for event names.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.EventType = {
|
||||
// Mouse events
|
||||
CLICK: 'click',
|
||||
DBLCLICK: 'dblclick',
|
||||
MOUSEDOWN: 'mousedown',
|
||||
MOUSEUP: 'mouseup',
|
||||
MOUSEOVER: 'mouseover',
|
||||
MOUSEOUT: 'mouseout',
|
||||
MOUSEMOVE: 'mousemove',
|
||||
SELECTSTART: 'selectstart', // IE, Safari, Chrome
|
||||
|
||||
// Key events
|
||||
KEYPRESS: 'keypress',
|
||||
KEYDOWN: 'keydown',
|
||||
KEYUP: 'keyup',
|
||||
|
||||
// Focus
|
||||
BLUR: 'blur',
|
||||
FOCUS: 'focus',
|
||||
DEACTIVATE: 'deactivate', // IE only
|
||||
// NOTE: The following two events are not stable in cross-browser usage.
|
||||
// WebKit and Opera implement DOMFocusIn/Out.
|
||||
// IE implements focusin/out.
|
||||
// Gecko implements neither see bug at
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
|
||||
// The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
|
||||
// http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
|
||||
// You can use FOCUS in Capture phase until implementations converge.
|
||||
FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
|
||||
FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
|
||||
|
||||
// Forms
|
||||
CHANGE: 'change',
|
||||
SELECT: 'select',
|
||||
SUBMIT: 'submit',
|
||||
INPUT: 'input',
|
||||
PROPERTYCHANGE: 'propertychange', // IE only
|
||||
|
||||
// Drag and drop
|
||||
DRAGSTART: 'dragstart',
|
||||
DRAG: 'drag',
|
||||
DRAGENTER: 'dragenter',
|
||||
DRAGOVER: 'dragover',
|
||||
DRAGLEAVE: 'dragleave',
|
||||
DROP: 'drop',
|
||||
DRAGEND: 'dragend',
|
||||
|
||||
// WebKit touch events.
|
||||
TOUCHSTART: 'touchstart',
|
||||
TOUCHMOVE: 'touchmove',
|
||||
TOUCHEND: 'touchend',
|
||||
TOUCHCANCEL: 'touchcancel',
|
||||
|
||||
// Misc
|
||||
BEFOREUNLOAD: 'beforeunload',
|
||||
CONSOLEMESSAGE: 'consolemessage',
|
||||
CONTEXTMENU: 'contextmenu',
|
||||
DOMCONTENTLOADED: 'DOMContentLoaded',
|
||||
ERROR: 'error',
|
||||
HELP: 'help',
|
||||
LOAD: 'load',
|
||||
LOSECAPTURE: 'losecapture',
|
||||
READYSTATECHANGE: 'readystatechange',
|
||||
RESIZE: 'resize',
|
||||
SCROLL: 'scroll',
|
||||
UNLOAD: 'unload',
|
||||
|
||||
// HTML 5 History events
|
||||
// See http://www.w3.org/TR/html5/history.html#event-definitions
|
||||
HASHCHANGE: 'hashchange',
|
||||
PAGEHIDE: 'pagehide',
|
||||
PAGESHOW: 'pageshow',
|
||||
POPSTATE: 'popstate',
|
||||
|
||||
// Copy and Paste
|
||||
// Support is limited. Make sure it works on your favorite browser
|
||||
// before using.
|
||||
// http://www.quirksmode.org/dom/events/cutcopypaste.html
|
||||
COPY: 'copy',
|
||||
PASTE: 'paste',
|
||||
CUT: 'cut',
|
||||
BEFORECOPY: 'beforecopy',
|
||||
BEFORECUT: 'beforecut',
|
||||
BEFOREPASTE: 'beforepaste',
|
||||
|
||||
// HTML5 online/offline events.
|
||||
// http://www.w3.org/TR/offline-webapps/#related
|
||||
ONLINE: 'online',
|
||||
OFFLINE: 'offline',
|
||||
|
||||
// HTML 5 worker events
|
||||
MESSAGE: 'message',
|
||||
CONNECT: 'connect',
|
||||
|
||||
// CSS transition events. Based on the browser support described at:
|
||||
// https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
|
||||
TRANSITIONEND: goog.userAgent.WEBKIT ? 'webkitTransitionEnd' :
|
||||
(goog.userAgent.OPERA ? 'oTransitionEnd' : 'transitionend'),
|
||||
|
||||
// IE specific events.
|
||||
// See http://msdn.microsoft.com/en-us/library/ie/hh673557(v=vs.85).aspx
|
||||
MSGESTURECHANGE: 'MSGestureChange',
|
||||
MSGESTUREEND: 'MSGestureEnd',
|
||||
MSGESTUREHOLD: 'MSGestureHold',
|
||||
MSGESTURESTART: 'MSGestureStart',
|
||||
MSGESTURETAP: 'MSGestureTap',
|
||||
MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
|
||||
MSINERTIASTART: 'MSInertiaStart',
|
||||
MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
|
||||
MSPOINTERCANCEL: 'MSPointerCancel',
|
||||
MSPOINTERDOWN: 'MSPointerDown',
|
||||
MSPOINTERMOVE: 'MSPointerMove',
|
||||
MSPOINTEROVER: 'MSPointerOver',
|
||||
MSPOINTEROUT: 'MSPointerOut',
|
||||
MSPOINTERUP: 'MSPointerUp',
|
||||
|
||||
// Native IMEs/input tools events.
|
||||
TEXTINPUT: 'textinput',
|
||||
COMPOSITIONSTART: 'compositionstart',
|
||||
COMPOSITIONUPDATE: 'compositionupdate',
|
||||
COMPOSITIONEND: 'compositionend',
|
||||
|
||||
// Webview tag events
|
||||
// See http://developer.chrome.com/dev/apps/webview_tag.html
|
||||
EXIT: 'exit',
|
||||
LOADABORT: 'loadabort',
|
||||
LOADCOMMIT: 'loadcommit',
|
||||
LOADREDIRECT: 'loadredirect',
|
||||
LOADSTART: 'loadstart',
|
||||
LOADSTOP: 'loadstop',
|
||||
RESPONSIVE: 'responsive',
|
||||
SIZECHANGED: 'sizechanged',
|
||||
UNRESPONSIVE: 'unresponsive'
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2009 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 Definition of the goog.events.EventWrapper interface.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.EventWrapper');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Interface for event wrappers.
|
||||
* @interface
|
||||
*/
|
||||
goog.events.EventWrapper = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an event listener using the wrapper on a DOM Node or an object that has
|
||||
* implemented {@link goog.events.EventTarget}. A listener can only be added
|
||||
* once to an object.
|
||||
*
|
||||
* @param {goog.events.ListenableType} src The node to listen to events on.
|
||||
* @param {Function|Object} listener Callback method, or an object with a
|
||||
* handleEvent function.
|
||||
* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
|
||||
* false).
|
||||
* @param {Object=} opt_scope Element in whose scope to call the listener.
|
||||
* @param {goog.events.EventHandler=} opt_eventHandler Event handler to add
|
||||
* listener to.
|
||||
*/
|
||||
goog.events.EventWrapper.prototype.listen = function(src, listener, opt_capt,
|
||||
opt_scope, opt_eventHandler) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes an event listener added using goog.events.EventWrapper.listen.
|
||||
*
|
||||
* @param {goog.events.ListenableType} src The node to remove listener from.
|
||||
* @param {Function|Object} listener Callback method, or an object with a
|
||||
* handleEvent function.
|
||||
* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
|
||||
* false).
|
||||
* @param {Object=} opt_scope Element in whose scope to call the listener.
|
||||
* @param {goog.events.EventHandler=} opt_eventHandler Event handler to remove
|
||||
* listener from.
|
||||
*/
|
||||
goog.events.EventWrapper.prototype.unlisten = function(src, listener, opt_capt,
|
||||
opt_scope, opt_eventHandler) {
|
||||
};
|
||||
@@ -0,0 +1,221 @@
|
||||
// Copyright 2010 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 Provides a files drag and drop event detector. It works on
|
||||
* HTML5 browsers.
|
||||
*
|
||||
* @see ../demos/filedrophandler.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.FileDropHandler');
|
||||
goog.provide('goog.events.FileDropHandler.EventType');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.log');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A files drag and drop event detector. Gets an {@code element} as parameter
|
||||
* and fires {@code goog.events.FileDropHandler.EventType.DROP} event when files
|
||||
* are dropped in the {@code element}.
|
||||
*
|
||||
* @param {Element|Document} element The element or document to listen on.
|
||||
* @param {boolean=} opt_preventDropOutside Whether to prevent a drop on the
|
||||
* area outside the {@code element}. Default false.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.events.FileDropHandler = function(element, opt_preventDropOutside) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* Handler for drag/drop events.
|
||||
* @type {!goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
var doc = element;
|
||||
if (opt_preventDropOutside) {
|
||||
doc = goog.dom.getOwnerDocument(element);
|
||||
}
|
||||
|
||||
// Add dragenter listener to the owner document of the element.
|
||||
this.eventHandler_.listen(doc,
|
||||
goog.events.EventType.DRAGENTER,
|
||||
this.onDocDragEnter_);
|
||||
|
||||
// Add dragover listener to the owner document of the element only if the
|
||||
// document is not the element itself.
|
||||
if (doc != element) {
|
||||
this.eventHandler_.listen(doc,
|
||||
goog.events.EventType.DRAGOVER,
|
||||
this.onDocDragOver_);
|
||||
}
|
||||
|
||||
// Add dragover and drop listeners to the element.
|
||||
this.eventHandler_.listen(element,
|
||||
goog.events.EventType.DRAGOVER,
|
||||
this.onElemDragOver_);
|
||||
this.eventHandler_.listen(element,
|
||||
goog.events.EventType.DROP,
|
||||
this.onElemDrop_);
|
||||
};
|
||||
goog.inherits(goog.events.FileDropHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Whether the drag event contains files. It is initialized only in the
|
||||
* dragenter event. It is used in all the drag events to prevent default actions
|
||||
* only if the drag contains files. Preventing default actions is necessary to
|
||||
* go from dragenter to dragover and from dragover to drop. However we do not
|
||||
* always want to prevent default actions, e.g. when the user drags text or
|
||||
* links on a text area we should not prevent the browser default action that
|
||||
* inserts the text in the text area. It is also necessary to stop propagation
|
||||
* when handling drag events on the element to prevent them from propagating
|
||||
* to the document.
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.FileDropHandler.prototype.dndContainsFiles_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* A logger, used to help us debug the algorithm.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.events.FileDropHandler.prototype.logger_ =
|
||||
goog.log.getLogger('goog.events.FileDropHandler');
|
||||
|
||||
|
||||
/**
|
||||
* The types of events fired by this class.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.FileDropHandler.EventType = {
|
||||
DROP: goog.events.EventType.DROP
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.FileDropHandler.prototype.disposeInternal = function() {
|
||||
goog.events.FileDropHandler.superClass_.disposeInternal.call(this);
|
||||
this.eventHandler_.dispose();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches the DROP event.
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.FileDropHandler.prototype.dispatch_ = function(e) {
|
||||
goog.log.fine(this.logger_, 'Firing DROP event...');
|
||||
var event = new goog.events.BrowserEvent(e.getBrowserEvent());
|
||||
event.type = goog.events.FileDropHandler.EventType.DROP;
|
||||
this.dispatchEvent(event);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles dragenter on the document.
|
||||
* @param {goog.events.BrowserEvent} e The dragenter event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.FileDropHandler.prototype.onDocDragEnter_ = function(e) {
|
||||
goog.log.log(this.logger_, goog.log.Level.FINER,
|
||||
'"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
|
||||
var dt = e.getBrowserEvent().dataTransfer;
|
||||
// Check whether the drag event contains files.
|
||||
this.dndContainsFiles_ = !!(dt &&
|
||||
((dt.types &&
|
||||
(goog.array.contains(dt.types, 'Files') ||
|
||||
goog.array.contains(dt.types, 'public.file-url'))) ||
|
||||
(dt.files && dt.files.length > 0)));
|
||||
// If it does
|
||||
if (this.dndContainsFiles_) {
|
||||
// Prevent default actions.
|
||||
e.preventDefault();
|
||||
}
|
||||
goog.log.log(this.logger_, goog.log.Level.FINER,
|
||||
'dndContainsFiles_: ' + this.dndContainsFiles_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles dragging something over the document.
|
||||
* @param {goog.events.BrowserEvent} e The dragover event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.FileDropHandler.prototype.onDocDragOver_ = function(e) {
|
||||
goog.log.log(this.logger_, goog.log.Level.FINEST,
|
||||
'"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
|
||||
if (this.dndContainsFiles_) {
|
||||
// Prevent default actions.
|
||||
e.preventDefault();
|
||||
// Disable the drop on the document outside the drop zone.
|
||||
var dt = e.getBrowserEvent().dataTransfer;
|
||||
dt.dropEffect = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles dragging something over the element (drop zone).
|
||||
* @param {goog.events.BrowserEvent} e The dragover event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.FileDropHandler.prototype.onElemDragOver_ = function(e) {
|
||||
goog.log.log(this.logger_, goog.log.Level.FINEST,
|
||||
'"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
|
||||
if (this.dndContainsFiles_) {
|
||||
// Prevent default actions and stop the event from propagating further to
|
||||
// the document. Both lines are needed! (See comment above).
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Allow the drop on the drop zone.
|
||||
var dt = e.getBrowserEvent().dataTransfer;
|
||||
dt.effectAllowed = 'all';
|
||||
dt.dropEffect = 'copy';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles dropping something onto the element (drop zone).
|
||||
* @param {goog.events.BrowserEvent} e The drop event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.FileDropHandler.prototype.onElemDrop_ = function(e) {
|
||||
goog.log.log(this.logger_, goog.log.Level.FINER,
|
||||
'"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
|
||||
// If the drag and drop event contains files.
|
||||
if (this.dndContainsFiles_) {
|
||||
// Prevent default actions and stop the event from propagating further to
|
||||
// the document. Both lines are needed! (See comment above).
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Dispatch DROP event.
|
||||
this.dispatch_(e);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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 This event handler allows you to catch focusin and focusout
|
||||
* events on descendants. Unlike the "focus" and "blur" events which do not
|
||||
* propagate consistently, and therefore must be added to the element that is
|
||||
* focused, this allows you to attach one listener to an ancester and you will
|
||||
* be notified when the focus state changes of ony of its descendants.
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
* @see ../demos/focushandler.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.FocusHandler');
|
||||
goog.provide('goog.events.FocusHandler.EventType');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This event handler allows you to catch focus events when descendants gain or
|
||||
* loses focus.
|
||||
* @param {Element|Document} element The node to listen on.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.events.FocusHandler = function(element) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* This is the element that we will listen to the real focus events on.
|
||||
* @type {Element|Document}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = element;
|
||||
|
||||
// In IE we use focusin/focusout and in other browsers we use a capturing
|
||||
// listner for focus/blur
|
||||
var typeIn = goog.userAgent.IE ? 'focusin' : 'focus';
|
||||
var typeOut = goog.userAgent.IE ? 'focusout' : 'blur';
|
||||
|
||||
/**
|
||||
* Store the listen key so it easier to unlisten in dispose.
|
||||
* @private
|
||||
* @type {goog.events.Key}
|
||||
*/
|
||||
this.listenKeyIn_ =
|
||||
goog.events.listen(this.element_, typeIn, this, !goog.userAgent.IE);
|
||||
|
||||
/**
|
||||
* Store the listen key so it easier to unlisten in dispose.
|
||||
* @private
|
||||
* @type {goog.events.Key}
|
||||
*/
|
||||
this.listenKeyOut_ =
|
||||
goog.events.listen(this.element_, typeOut, this, !goog.userAgent.IE);
|
||||
};
|
||||
goog.inherits(goog.events.FocusHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Enum type for the events fired by the focus handler
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.FocusHandler.EventType = {
|
||||
FOCUSIN: 'focusin',
|
||||
FOCUSOUT: 'focusout'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This handles the underlying events and dispatches a new event.
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
*/
|
||||
goog.events.FocusHandler.prototype.handleEvent = function(e) {
|
||||
var be = e.getBrowserEvent();
|
||||
var event = new goog.events.BrowserEvent(be);
|
||||
event.type = e.type == 'focusin' || e.type == 'focus' ?
|
||||
goog.events.FocusHandler.EventType.FOCUSIN :
|
||||
goog.events.FocusHandler.EventType.FOCUSOUT;
|
||||
this.dispatchEvent(event);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.FocusHandler.prototype.disposeInternal = function() {
|
||||
goog.events.FocusHandler.superClass_.disposeInternal.call(this);
|
||||
goog.events.unlistenByKey(this.listenKeyIn_);
|
||||
goog.events.unlistenByKey(this.listenKeyOut_);
|
||||
delete this.element_;
|
||||
};
|
||||
363
nicer-api-docs/closure-library/closure/goog/events/imehandler.js
Normal file
363
nicer-api-docs/closure-library/closure/goog/events/imehandler.js
Normal file
@@ -0,0 +1,363 @@
|
||||
// Copyright 2010 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 Input Method Editors (IMEs) are OS-level widgets that make
|
||||
* it easier to type non-ascii characters on ascii keyboards (in particular,
|
||||
* characters that require more than one keystroke).
|
||||
*
|
||||
* When the user wants to type such a character, a modal menu pops up and
|
||||
* suggests possible "next" characters in the IME character sequence. After
|
||||
* typing N characters, the user hits "enter" to commit the IME to the field.
|
||||
* N differs from language to language.
|
||||
*
|
||||
* This class offers high-level events for how the user is interacting with the
|
||||
* IME in editable regions.
|
||||
*
|
||||
* Known Issues:
|
||||
*
|
||||
* Firefox always fires an extra pair of compositionstart/compositionend events.
|
||||
* We do not normalize for this.
|
||||
*
|
||||
* Opera does not fire any IME events.
|
||||
*
|
||||
* Spurious UPDATE events are common on all browsers.
|
||||
*
|
||||
* We currently do a bad job detecting when the IME closes on IE, and
|
||||
* make a "best effort" guess on when we know it's closed.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.ImeHandler');
|
||||
goog.provide('goog.events.ImeHandler.Event');
|
||||
goog.provide('goog.events.ImeHandler.EventType');
|
||||
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches high-level events for IMEs.
|
||||
* @param {Element} el The element to listen on.
|
||||
* @extends {goog.events.EventTarget}
|
||||
* @constructor
|
||||
*/
|
||||
goog.events.ImeHandler = function(el) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The element to listen on.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.el_ = el;
|
||||
|
||||
/**
|
||||
* Tracks the keyup event only, because it has a different life-cycle from
|
||||
* other events.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.keyUpHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* Tracks all the browser events.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.handler_ = new goog.events.EventHandler(this);
|
||||
|
||||
if (goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
|
||||
this.handler_.
|
||||
listen(el, 'compositionstart', this.handleCompositionStart_).
|
||||
listen(el, 'compositionend', this.handleCompositionEnd_).
|
||||
listen(el, 'compositionupdate', this.handleTextModifyingInput_);
|
||||
}
|
||||
|
||||
this.handler_.
|
||||
listen(el, 'textInput', this.handleTextInput_).
|
||||
listen(el, 'text', this.handleTextModifyingInput_).
|
||||
listen(el, goog.events.EventType.KEYDOWN, this.handleKeyDown_);
|
||||
};
|
||||
goog.inherits(goog.events.ImeHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Event types fired by ImeHandler. These events do not make any guarantees
|
||||
* about whether they were fired before or after the event in question.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.ImeHandler.EventType = {
|
||||
// After the IME opens.
|
||||
START: 'startIme',
|
||||
|
||||
// An update to the state of the IME. An 'update' does not necessarily mean
|
||||
// that the text contents of the field were modified in any way.
|
||||
UPDATE: 'updateIme',
|
||||
|
||||
// After the IME closes.
|
||||
END: 'endIme'
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An event fired by ImeHandler.
|
||||
* @param {goog.events.ImeHandler.EventType} type The type.
|
||||
* @param {goog.events.BrowserEvent} reason The trigger for this event.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.events.ImeHandler.Event = function(type, reason) {
|
||||
goog.base(this, type);
|
||||
|
||||
/**
|
||||
* The event that triggered this.
|
||||
* @type {goog.events.BrowserEvent}
|
||||
*/
|
||||
this.reason = reason;
|
||||
};
|
||||
goog.inherits(goog.events.ImeHandler.Event, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use the composition events.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.ImeHandler.USES_COMPOSITION_EVENTS =
|
||||
goog.userAgent.GECKO ||
|
||||
(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher(532));
|
||||
|
||||
|
||||
/**
|
||||
* Stores whether IME mode is active.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.imeMode_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The keyCode value of the last keyDown event. This value is used for
|
||||
* identiying whether or not a textInput event is sent by an IME.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.lastKeyCode_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether an IME is active.
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.isImeMode = function() {
|
||||
return this.imeMode_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the compositionstart event.
|
||||
* @param {goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleCompositionStart_ =
|
||||
function(e) {
|
||||
this.handleImeActivate_(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the compositionend event.
|
||||
* @param {goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleCompositionEnd_ = function(e) {
|
||||
this.handleImeDeactivate_(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the compositionupdate and text events.
|
||||
* @param {goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleTextModifyingInput_ =
|
||||
function(e) {
|
||||
if (this.isImeMode()) {
|
||||
this.processImeComposition_(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles IME activation.
|
||||
* @param {goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleImeActivate_ = function(e) {
|
||||
if (this.imeMode_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Listens for keyup events to handle unexpected IME keydown events on older
|
||||
// versions of webkit.
|
||||
//
|
||||
// In those versions, we currently use textInput events deactivate IME
|
||||
// (see handleTextInput_() for the reason). However,
|
||||
// Safari fires a keydown event (as a result of pressing keys to commit IME
|
||||
// text) with keyCode == WIN_IME after textInput event. This activates IME
|
||||
// mode again unnecessarily. To prevent this problem, listens keyup events
|
||||
// which can use to determine whether IME text has been committed.
|
||||
if (goog.userAgent.WEBKIT &&
|
||||
!goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
|
||||
this.keyUpHandler_.listen(this.el_,
|
||||
goog.events.EventType.KEYUP, this.handleKeyUpSafari4_);
|
||||
}
|
||||
|
||||
this.imeMode_ = true;
|
||||
this.dispatchEvent(
|
||||
new goog.events.ImeHandler.Event(
|
||||
goog.events.ImeHandler.EventType.START, e));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the IME compose changes.
|
||||
* @param {goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.processImeComposition_ = function(e) {
|
||||
this.dispatchEvent(
|
||||
new goog.events.ImeHandler.Event(
|
||||
goog.events.ImeHandler.EventType.UPDATE, e));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles IME deactivation.
|
||||
* @param {goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleImeDeactivate_ = function(e) {
|
||||
this.imeMode_ = false;
|
||||
this.keyUpHandler_.removeAll();
|
||||
this.dispatchEvent(
|
||||
new goog.events.ImeHandler.Event(
|
||||
goog.events.ImeHandler.EventType.END, e));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles a key down event.
|
||||
* @param {!goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleKeyDown_ = function(e) {
|
||||
// Firefox and Chrome have a separate event for IME composition ('text'
|
||||
// and 'compositionupdate', respectively), other browsers do not.
|
||||
if (!goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
|
||||
var imeMode = this.isImeMode();
|
||||
// If we're in IE and we detect an IME input on keyDown then activate
|
||||
// the IME, otherwise if the imeMode was previously active, deactivate.
|
||||
if (!imeMode && e.keyCode == goog.events.KeyCodes.WIN_IME) {
|
||||
this.handleImeActivate_(e);
|
||||
} else if (imeMode && e.keyCode != goog.events.KeyCodes.WIN_IME) {
|
||||
if (goog.events.ImeHandler.isImeDeactivateKeyEvent_(e)) {
|
||||
this.handleImeDeactivate_(e);
|
||||
}
|
||||
} else if (imeMode) {
|
||||
this.processImeComposition_(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Safari on Mac doesn't send IME events in the right order so that we must
|
||||
// ignore some modifier key events to insert IME text correctly.
|
||||
if (goog.events.ImeHandler.isImeDeactivateKeyEvent_(e)) {
|
||||
this.lastKeyCode_ = e.keyCode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles a textInput event.
|
||||
* @param {!goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleTextInput_ = function(e) {
|
||||
// Some WebKit-based browsers including Safari 4 don't send composition
|
||||
// events. So, we turn down IME mode when it's still there.
|
||||
if (!goog.events.ImeHandler.USES_COMPOSITION_EVENTS &&
|
||||
goog.userAgent.WEBKIT &&
|
||||
this.lastKeyCode_ == goog.events.KeyCodes.WIN_IME &&
|
||||
this.isImeMode()) {
|
||||
this.handleImeDeactivate_(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the key up event for any IME activity. This handler is just used to
|
||||
* prevent activating IME unnecessary in Safari at this time.
|
||||
* @param {!goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.prototype.handleKeyUpSafari4_ = function(e) {
|
||||
if (this.isImeMode()) {
|
||||
switch (e.keyCode) {
|
||||
// These keyup events indicates that IME text has been committed or
|
||||
// cancelled. We should turn off IME mode when these keyup events
|
||||
// received.
|
||||
case goog.events.KeyCodes.ENTER:
|
||||
case goog.events.KeyCodes.TAB:
|
||||
case goog.events.KeyCodes.ESC:
|
||||
this.handleImeDeactivate_(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given event should be treated as an IME
|
||||
* deactivation trigger.
|
||||
* @param {!goog.events.Event} e The event.
|
||||
* @return {boolean} Whether the given event is an IME deactivate trigger.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ImeHandler.isImeDeactivateKeyEvent_ = function(e) {
|
||||
// Which key events involve IME deactivation depends on the user's
|
||||
// environment (i.e. browsers, platforms, and IMEs). Usually Shift key
|
||||
// and Ctrl key does not involve IME deactivation, so we currently assume
|
||||
// that these keys are not IME deactivation trigger.
|
||||
switch (e.keyCode) {
|
||||
case goog.events.KeyCodes.SHIFT:
|
||||
case goog.events.KeyCodes.CTRL:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.ImeHandler.prototype.disposeInternal = function() {
|
||||
this.handler_.dispose();
|
||||
this.keyUpHandler_.dispose();
|
||||
this.el_ = null;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,213 @@
|
||||
// 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 An object that encapsulates text changed events for textareas
|
||||
* and input element of type text and password. The event occurs after the value
|
||||
* has been changed. The event does not occur if value was changed
|
||||
* programmatically.<br>
|
||||
* <br>
|
||||
* Note: this does not guarantee the correctness of {@code keyCode} or
|
||||
* {@code charCode}, or attempt to unify them across browsers. See
|
||||
* {@code goog.events.KeyHandler} for that functionality.<br>
|
||||
* <br>
|
||||
* Known issues:
|
||||
* <ul>
|
||||
* <li>Does not trigger for drop events on Opera due to browser bug.
|
||||
* <li>IE doesn't have native support for input event. WebKit before version 531
|
||||
* doesn't have support for textareas. For those browsers an emulation mode
|
||||
* based on key, clipboard and drop events is used. Thus this event won't
|
||||
* trigger in emulation mode if text was modified by context menu commands
|
||||
* such as 'Undo' and 'Delete'.
|
||||
* </ul>
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
* @see ../demos/inputhandler.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.InputHandler');
|
||||
goog.provide('goog.events.InputHandler.EventType');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This event handler will dispatch events when the user types into a text
|
||||
* input, password input or a textarea
|
||||
* @param {Element} element The element that you want to listen for input
|
||||
* events on.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.events.InputHandler = function(element) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The element that you want to listen for input events on.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = element;
|
||||
|
||||
// Determine whether input event should be emulated.
|
||||
// IE8 doesn't support input events. We could use property change events but
|
||||
// they are broken in many ways:
|
||||
// - Fire even if value was changed programmatically.
|
||||
// - Aren't always delivered. For example, if you change value or even width
|
||||
// of input programmatically, next value change made by user won't fire an
|
||||
// event.
|
||||
// IE9 supports input events when characters are inserted, but not deleted.
|
||||
// WebKit before version 531 did not support input events for textareas.
|
||||
var emulateInputEvents = goog.userAgent.IE ||
|
||||
(goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('531') &&
|
||||
element.tagName == 'TEXTAREA');
|
||||
|
||||
/**
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
// Even if input event emulation is enabled, still listen for input events
|
||||
// since they may be partially supported by the browser (such as IE9).
|
||||
// If the input event does fire, we will be able to dispatch synchronously.
|
||||
// (InputHandler events being asynchronous for IE is a common issue for
|
||||
// cases like auto-grow textareas where they result in a quick flash of
|
||||
// scrollbars between the textarea content growing and it being resized to
|
||||
// fit.)
|
||||
this.eventHandler_.listen(
|
||||
this.element_,
|
||||
emulateInputEvents ?
|
||||
['keydown', 'paste', 'cut', 'drop', 'input'] :
|
||||
'input',
|
||||
this);
|
||||
};
|
||||
goog.inherits(goog.events.InputHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Enum type for the events fired by the input handler
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.InputHandler.EventType = {
|
||||
INPUT: 'input'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Id of a timer used to postpone firing input event in emulation mode.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
goog.events.InputHandler.prototype.timer_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* This handles the underlying events and dispatches a new event as needed.
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
*/
|
||||
goog.events.InputHandler.prototype.handleEvent = function(e) {
|
||||
if (e.type == 'input') {
|
||||
// This event happens after all the other events we listen to, so cancel
|
||||
// an asynchronous event dispatch if we have it queued up. Otherwise, we
|
||||
// will end up firing an extra event.
|
||||
this.cancelTimerIfSet_();
|
||||
|
||||
// Unlike other browsers, Opera fires an extra input event when an element
|
||||
// is blurred after the user has input into it. Since Opera doesn't fire
|
||||
// input event on drop, it's enough to check whether element still has focus
|
||||
// to suppress bogus notification.
|
||||
if (!goog.userAgent.OPERA || this.element_ ==
|
||||
goog.dom.getOwnerDocument(this.element_).activeElement) {
|
||||
this.dispatchEvent(this.createInputEvent_(e));
|
||||
}
|
||||
} else {
|
||||
// Filter out key events that don't modify text.
|
||||
if (e.type == 'keydown' &&
|
||||
!goog.events.KeyCodes.isTextModifyingKeyEvent(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It is still possible that pressed key won't modify the value of an
|
||||
// element. Storing old value will help us to detect modification but is
|
||||
// also a little bit dangerous. If value is changed programmatically in
|
||||
// another key down handler, we will detect it as user-initiated change.
|
||||
var valueBeforeKey = e.type == 'keydown' ? this.element_.value : null;
|
||||
|
||||
// In IE on XP, IME the element's value has already changed when we get
|
||||
// keydown events when the user is using an IME. In this case, we can't
|
||||
// check the current value normally, so we assume that it's a modifying key
|
||||
// event. This means that ENTER when used to commit will fire a spurious
|
||||
// input event, but it's better to have a false positive than let some input
|
||||
// slip through the cracks.
|
||||
if (goog.userAgent.IE && e.keyCode == goog.events.KeyCodes.WIN_IME) {
|
||||
valueBeforeKey = null;
|
||||
}
|
||||
|
||||
// Create an input event now, because when we fire it on timer, the
|
||||
// underlying event will already be disposed.
|
||||
var inputEvent = this.createInputEvent_(e);
|
||||
|
||||
// Since key down, paste, cut and drop events are fired before actual value
|
||||
// of the element has changed, we need to postpone dispatching input event
|
||||
// until value is updated.
|
||||
this.cancelTimerIfSet_();
|
||||
this.timer_ = goog.Timer.callOnce(function() {
|
||||
this.timer_ = null;
|
||||
if (this.element_.value != valueBeforeKey) {
|
||||
this.dispatchEvent(inputEvent);
|
||||
}
|
||||
}, 0, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cancels timer if it is set, does nothing otherwise.
|
||||
* @private
|
||||
*/
|
||||
goog.events.InputHandler.prototype.cancelTimerIfSet_ = function() {
|
||||
if (this.timer_ != null) {
|
||||
goog.Timer.clear(this.timer_);
|
||||
this.timer_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates an input event from the browser event.
|
||||
* @param {goog.events.BrowserEvent} be A browser event.
|
||||
* @return {goog.events.BrowserEvent} An input event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.InputHandler.prototype.createInputEvent_ = function(be) {
|
||||
var e = new goog.events.BrowserEvent(be.getBrowserEvent());
|
||||
e.type = goog.events.InputHandler.EventType.INPUT;
|
||||
return e;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.InputHandler.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
this.eventHandler_.dispose();
|
||||
this.cancelTimerIfSet_();
|
||||
delete this.element_;
|
||||
};
|
||||
377
nicer-api-docs/closure-library/closure/goog/events/keycodes.js
Normal file
377
nicer-api-docs/closure-library/closure/goog/events/keycodes.js
Normal file
@@ -0,0 +1,377 @@
|
||||
// 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 Constant declarations for common key codes.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @see ../demos/keyhandler.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.KeyCodes');
|
||||
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Key codes for common characters.
|
||||
*
|
||||
* This list is not localized and therefore some of the key codes are not
|
||||
* correct for non US keyboard layouts. See comments below.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.events.KeyCodes = {
|
||||
WIN_KEY_FF_LINUX: 0,
|
||||
MAC_ENTER: 3,
|
||||
BACKSPACE: 8,
|
||||
TAB: 9,
|
||||
NUM_CENTER: 12, // NUMLOCK on FF/Safari Mac
|
||||
ENTER: 13,
|
||||
SHIFT: 16,
|
||||
CTRL: 17,
|
||||
ALT: 18,
|
||||
PAUSE: 19,
|
||||
CAPS_LOCK: 20,
|
||||
ESC: 27,
|
||||
SPACE: 32,
|
||||
PAGE_UP: 33, // also NUM_NORTH_EAST
|
||||
PAGE_DOWN: 34, // also NUM_SOUTH_EAST
|
||||
END: 35, // also NUM_SOUTH_WEST
|
||||
HOME: 36, // also NUM_NORTH_WEST
|
||||
LEFT: 37, // also NUM_WEST
|
||||
UP: 38, // also NUM_NORTH
|
||||
RIGHT: 39, // also NUM_EAST
|
||||
DOWN: 40, // also NUM_SOUTH
|
||||
PRINT_SCREEN: 44,
|
||||
INSERT: 45, // also NUM_INSERT
|
||||
DELETE: 46, // also NUM_DELETE
|
||||
ZERO: 48,
|
||||
ONE: 49,
|
||||
TWO: 50,
|
||||
THREE: 51,
|
||||
FOUR: 52,
|
||||
FIVE: 53,
|
||||
SIX: 54,
|
||||
SEVEN: 55,
|
||||
EIGHT: 56,
|
||||
NINE: 57,
|
||||
FF_SEMICOLON: 59, // Firefox (Gecko) fires this for semicolon instead of 186
|
||||
FF_EQUALS: 61, // Firefox (Gecko) fires this for equals instead of 187
|
||||
QUESTION_MARK: 63, // needs localization
|
||||
A: 65,
|
||||
B: 66,
|
||||
C: 67,
|
||||
D: 68,
|
||||
E: 69,
|
||||
F: 70,
|
||||
G: 71,
|
||||
H: 72,
|
||||
I: 73,
|
||||
J: 74,
|
||||
K: 75,
|
||||
L: 76,
|
||||
M: 77,
|
||||
N: 78,
|
||||
O: 79,
|
||||
P: 80,
|
||||
Q: 81,
|
||||
R: 82,
|
||||
S: 83,
|
||||
T: 84,
|
||||
U: 85,
|
||||
V: 86,
|
||||
W: 87,
|
||||
X: 88,
|
||||
Y: 89,
|
||||
Z: 90,
|
||||
META: 91, // WIN_KEY_LEFT
|
||||
WIN_KEY_RIGHT: 92,
|
||||
CONTEXT_MENU: 93,
|
||||
NUM_ZERO: 96,
|
||||
NUM_ONE: 97,
|
||||
NUM_TWO: 98,
|
||||
NUM_THREE: 99,
|
||||
NUM_FOUR: 100,
|
||||
NUM_FIVE: 101,
|
||||
NUM_SIX: 102,
|
||||
NUM_SEVEN: 103,
|
||||
NUM_EIGHT: 104,
|
||||
NUM_NINE: 105,
|
||||
NUM_MULTIPLY: 106,
|
||||
NUM_PLUS: 107,
|
||||
NUM_MINUS: 109,
|
||||
NUM_PERIOD: 110,
|
||||
NUM_DIVISION: 111,
|
||||
F1: 112,
|
||||
F2: 113,
|
||||
F3: 114,
|
||||
F4: 115,
|
||||
F5: 116,
|
||||
F6: 117,
|
||||
F7: 118,
|
||||
F8: 119,
|
||||
F9: 120,
|
||||
F10: 121,
|
||||
F11: 122,
|
||||
F12: 123,
|
||||
NUMLOCK: 144,
|
||||
SCROLL_LOCK: 145,
|
||||
|
||||
// OS-specific media keys like volume controls and browser controls.
|
||||
FIRST_MEDIA_KEY: 166,
|
||||
LAST_MEDIA_KEY: 183,
|
||||
|
||||
SEMICOLON: 186, // needs localization
|
||||
DASH: 189, // needs localization
|
||||
EQUALS: 187, // needs localization
|
||||
COMMA: 188, // needs localization
|
||||
PERIOD: 190, // needs localization
|
||||
SLASH: 191, // needs localization
|
||||
APOSTROPHE: 192, // needs localization
|
||||
TILDE: 192, // needs localization
|
||||
SINGLE_QUOTE: 222, // needs localization
|
||||
OPEN_SQUARE_BRACKET: 219, // needs localization
|
||||
BACKSLASH: 220, // needs localization
|
||||
CLOSE_SQUARE_BRACKET: 221, // needs localization
|
||||
WIN_KEY: 224,
|
||||
MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
|
||||
WIN_IME: 229,
|
||||
|
||||
// We've seen users whose machines fire this keycode at regular one
|
||||
// second intervals. The common thread among these users is that
|
||||
// they're all using Dell Inspiron laptops, so we suspect that this
|
||||
// indicates a hardware/bios problem.
|
||||
// http://en.community.dell.com/support-forums/laptop/f/3518/p/19285957/19523128.aspx
|
||||
PHANTOM: 255
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the event contains a text modifying key.
|
||||
* @param {goog.events.BrowserEvent} e A key event.
|
||||
* @return {boolean} Whether it's a text modifying key.
|
||||
*/
|
||||
goog.events.KeyCodes.isTextModifyingKeyEvent = function(e) {
|
||||
if (e.altKey && !e.ctrlKey ||
|
||||
e.metaKey ||
|
||||
// Function keys don't generate text
|
||||
e.keyCode >= goog.events.KeyCodes.F1 &&
|
||||
e.keyCode <= goog.events.KeyCodes.F12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The following keys are quite harmless, even in combination with
|
||||
// CTRL, ALT or SHIFT.
|
||||
switch (e.keyCode) {
|
||||
case goog.events.KeyCodes.ALT:
|
||||
case goog.events.KeyCodes.CAPS_LOCK:
|
||||
case goog.events.KeyCodes.CONTEXT_MENU:
|
||||
case goog.events.KeyCodes.CTRL:
|
||||
case goog.events.KeyCodes.DOWN:
|
||||
case goog.events.KeyCodes.END:
|
||||
case goog.events.KeyCodes.ESC:
|
||||
case goog.events.KeyCodes.HOME:
|
||||
case goog.events.KeyCodes.INSERT:
|
||||
case goog.events.KeyCodes.LEFT:
|
||||
case goog.events.KeyCodes.MAC_FF_META:
|
||||
case goog.events.KeyCodes.META:
|
||||
case goog.events.KeyCodes.NUMLOCK:
|
||||
case goog.events.KeyCodes.NUM_CENTER:
|
||||
case goog.events.KeyCodes.PAGE_DOWN:
|
||||
case goog.events.KeyCodes.PAGE_UP:
|
||||
case goog.events.KeyCodes.PAUSE:
|
||||
case goog.events.KeyCodes.PHANTOM:
|
||||
case goog.events.KeyCodes.PRINT_SCREEN:
|
||||
case goog.events.KeyCodes.RIGHT:
|
||||
case goog.events.KeyCodes.SCROLL_LOCK:
|
||||
case goog.events.KeyCodes.SHIFT:
|
||||
case goog.events.KeyCodes.UP:
|
||||
case goog.events.KeyCodes.WIN_KEY:
|
||||
case goog.events.KeyCodes.WIN_KEY_RIGHT:
|
||||
return false;
|
||||
case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
|
||||
return !goog.userAgent.GECKO;
|
||||
default:
|
||||
return e.keyCode < goog.events.KeyCodes.FIRST_MEDIA_KEY ||
|
||||
e.keyCode > goog.events.KeyCodes.LAST_MEDIA_KEY;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the key fires a keypress event in the current browser.
|
||||
*
|
||||
* Accoridng to MSDN [1] IE only fires keypress events for the following keys:
|
||||
* - Letters: A - Z (uppercase and lowercase)
|
||||
* - Numerals: 0 - 9
|
||||
* - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~
|
||||
* - System: ESC, SPACEBAR, ENTER
|
||||
*
|
||||
* That's not entirely correct though, for instance there's no distinction
|
||||
* between upper and lower case letters.
|
||||
*
|
||||
* [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx)
|
||||
*
|
||||
* Safari is similar to IE, but does not fire keypress for ESC.
|
||||
*
|
||||
* Additionally, IE6 does not fire keydown or keypress events for letters when
|
||||
* the control or alt keys are held down and the shift key is not. IE7 does
|
||||
* fire keydown in these cases, though, but not keypress.
|
||||
*
|
||||
* @param {number} keyCode A key code.
|
||||
* @param {number=} opt_heldKeyCode Key code of a currently-held key.
|
||||
* @param {boolean=} opt_shiftKey Whether the shift key is held down.
|
||||
* @param {boolean=} opt_ctrlKey Whether the control key is held down.
|
||||
* @param {boolean=} opt_altKey Whether the alt key is held down.
|
||||
* @return {boolean} Whether it's a key that fires a keypress event.
|
||||
*/
|
||||
goog.events.KeyCodes.firesKeyPressEvent = function(keyCode, opt_heldKeyCode,
|
||||
opt_shiftKey, opt_ctrlKey, opt_altKey) {
|
||||
if (!goog.userAgent.IE &&
|
||||
!(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (goog.userAgent.MAC && opt_altKey) {
|
||||
return goog.events.KeyCodes.isCharacterKey(keyCode);
|
||||
}
|
||||
|
||||
// Alt but not AltGr which is represented as Alt+Ctrl.
|
||||
if (opt_altKey && !opt_ctrlKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Saves Ctrl or Alt + key for IE and WebKit 525+, which won't fire keypress.
|
||||
// Non-IE browsers and WebKit prior to 525 won't get this far so no need to
|
||||
// check the user agent.
|
||||
if (!opt_shiftKey &&
|
||||
(opt_heldKeyCode == goog.events.KeyCodes.CTRL ||
|
||||
opt_heldKeyCode == goog.events.KeyCodes.ALT ||
|
||||
goog.userAgent.MAC &&
|
||||
opt_heldKeyCode == goog.events.KeyCodes.META)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some keys with Ctrl/Shift do not issue keypress in WEBKIT.
|
||||
if (goog.userAgent.WEBKIT && opt_ctrlKey && opt_shiftKey) {
|
||||
switch (keyCode) {
|
||||
case goog.events.KeyCodes.BACKSLASH:
|
||||
case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
|
||||
case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
|
||||
case goog.events.KeyCodes.TILDE:
|
||||
case goog.events.KeyCodes.SEMICOLON:
|
||||
case goog.events.KeyCodes.DASH:
|
||||
case goog.events.KeyCodes.EQUALS:
|
||||
case goog.events.KeyCodes.COMMA:
|
||||
case goog.events.KeyCodes.PERIOD:
|
||||
case goog.events.KeyCodes.SLASH:
|
||||
case goog.events.KeyCodes.APOSTROPHE:
|
||||
case goog.events.KeyCodes.SINGLE_QUOTE:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
|
||||
// continues to fire keydown events as the event repeats.
|
||||
if (goog.userAgent.IE && opt_ctrlKey && opt_heldKeyCode == keyCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case goog.events.KeyCodes.ENTER:
|
||||
// IE9 does not fire KEYPRESS on ENTER.
|
||||
return !(goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9));
|
||||
case goog.events.KeyCodes.ESC:
|
||||
return !goog.userAgent.WEBKIT;
|
||||
}
|
||||
|
||||
return goog.events.KeyCodes.isCharacterKey(keyCode);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the key produces a character.
|
||||
* This does not cover characters on non-US keyboards (Russian, Hebrew, etc.).
|
||||
*
|
||||
* @param {number} keyCode A key code.
|
||||
* @return {boolean} Whether it's a character key.
|
||||
*/
|
||||
goog.events.KeyCodes.isCharacterKey = function(keyCode) {
|
||||
if (keyCode >= goog.events.KeyCodes.ZERO &&
|
||||
keyCode <= goog.events.KeyCodes.NINE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode >= goog.events.KeyCodes.NUM_ZERO &&
|
||||
keyCode <= goog.events.KeyCodes.NUM_MULTIPLY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode >= goog.events.KeyCodes.A &&
|
||||
keyCode <= goog.events.KeyCodes.Z) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Safari sends zero key code for non-latin characters.
|
||||
if (goog.userAgent.WEBKIT && keyCode == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case goog.events.KeyCodes.SPACE:
|
||||
case goog.events.KeyCodes.QUESTION_MARK:
|
||||
case goog.events.KeyCodes.NUM_PLUS:
|
||||
case goog.events.KeyCodes.NUM_MINUS:
|
||||
case goog.events.KeyCodes.NUM_PERIOD:
|
||||
case goog.events.KeyCodes.NUM_DIVISION:
|
||||
case goog.events.KeyCodes.SEMICOLON:
|
||||
case goog.events.KeyCodes.FF_SEMICOLON:
|
||||
case goog.events.KeyCodes.DASH:
|
||||
case goog.events.KeyCodes.EQUALS:
|
||||
case goog.events.KeyCodes.FF_EQUALS:
|
||||
case goog.events.KeyCodes.COMMA:
|
||||
case goog.events.KeyCodes.PERIOD:
|
||||
case goog.events.KeyCodes.SLASH:
|
||||
case goog.events.KeyCodes.APOSTROPHE:
|
||||
case goog.events.KeyCodes.SINGLE_QUOTE:
|
||||
case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
|
||||
case goog.events.KeyCodes.BACKSLASH:
|
||||
case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Normalizes key codes from their Gecko-specific value to the general one.
|
||||
* @param {number} keyCode The native key code.
|
||||
* @return {number} The normalized key code.
|
||||
*/
|
||||
goog.events.KeyCodes.normalizeGeckoKeyCode = function(keyCode) {
|
||||
switch (keyCode) {
|
||||
case goog.events.KeyCodes.FF_EQUALS:
|
||||
return goog.events.KeyCodes.EQUALS;
|
||||
case goog.events.KeyCodes.FF_SEMICOLON:
|
||||
return goog.events.KeyCodes.SEMICOLON;
|
||||
case goog.events.KeyCodes.MAC_FF_META:
|
||||
return goog.events.KeyCodes.META;
|
||||
case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
|
||||
return goog.events.KeyCodes.WIN_KEY;
|
||||
default:
|
||||
return keyCode;
|
||||
}
|
||||
};
|
||||
556
nicer-api-docs/closure-library/closure/goog/events/keyhandler.js
Normal file
556
nicer-api-docs/closure-library/closure/goog/events/keyhandler.js
Normal file
@@ -0,0 +1,556 @@
|
||||
// Copyright 2007 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 This file contains a class for working with keyboard events
|
||||
* that repeat consistently across browsers and platforms. It also unifies the
|
||||
* key code so that it is the same in all browsers and platforms.
|
||||
*
|
||||
* Different web browsers have very different keyboard event handling. Most
|
||||
* importantly is that only certain browsers repeat keydown events:
|
||||
* IE, Opera, FF/Win32, and Safari 3 repeat keydown events.
|
||||
* FF/Mac and Safari 2 do not.
|
||||
*
|
||||
* For the purposes of this code, "Safari 3" means WebKit 525+, when WebKit
|
||||
* decided that they should try to match IE's key handling behavior.
|
||||
* Safari 3.0.4, which shipped with Leopard (WebKit 523), has the
|
||||
* Safari 2 behavior.
|
||||
*
|
||||
* Firefox, Safari, Opera prevent on keypress
|
||||
*
|
||||
* IE prevents on keydown
|
||||
*
|
||||
* Firefox does not fire keypress for shift, ctrl, alt
|
||||
* Firefox does fire keydown for shift, ctrl, alt, meta
|
||||
* Firefox does not repeat keydown for shift, ctrl, alt, meta
|
||||
*
|
||||
* Firefox does not fire keypress for up and down in an input
|
||||
*
|
||||
* Opera fires keypress for shift, ctrl, alt, meta
|
||||
* Opera does not repeat keypress for shift, ctrl, alt, meta
|
||||
*
|
||||
* Safari 2 and 3 do not fire keypress for shift, ctrl, alt
|
||||
* Safari 2 does not fire keydown for shift, ctrl, alt
|
||||
* Safari 3 *does* fire keydown for shift, ctrl, alt
|
||||
*
|
||||
* IE provides the keycode for keyup/down events and the charcode (in the
|
||||
* keycode field) for keypress.
|
||||
*
|
||||
* Mozilla provides the keycode for keyup/down and the charcode for keypress
|
||||
* unless it's a non text modifying key in which case the keycode is provided.
|
||||
*
|
||||
* Safari 3 provides the keycode and charcode for all events.
|
||||
*
|
||||
* Opera provides the keycode for keyup/down event and either the charcode or
|
||||
* the keycode (in the keycode field) for keypress events.
|
||||
*
|
||||
* Firefox x11 doesn't fire keydown events if a another key is already held down
|
||||
* until the first key is released. This can cause a key event to be fired with
|
||||
* a keyCode for the first key and a charCode for the second key.
|
||||
*
|
||||
* Safari in keypress
|
||||
*
|
||||
* charCode keyCode which
|
||||
* ENTER: 13 13 13
|
||||
* F1: 63236 63236 63236
|
||||
* F8: 63243 63243 63243
|
||||
* ...
|
||||
* p: 112 112 112
|
||||
* P: 80 80 80
|
||||
*
|
||||
* Firefox, keypress:
|
||||
*
|
||||
* charCode keyCode which
|
||||
* ENTER: 0 13 13
|
||||
* F1: 0 112 0
|
||||
* F8: 0 119 0
|
||||
* ...
|
||||
* p: 112 0 112
|
||||
* P: 80 0 80
|
||||
*
|
||||
* Opera, Mac+Win32, keypress:
|
||||
*
|
||||
* charCode keyCode which
|
||||
* ENTER: undefined 13 13
|
||||
* F1: undefined 112 0
|
||||
* F8: undefined 119 0
|
||||
* ...
|
||||
* p: undefined 112 112
|
||||
* P: undefined 80 80
|
||||
*
|
||||
* IE7, keydown
|
||||
*
|
||||
* charCode keyCode which
|
||||
* ENTER: undefined 13 undefined
|
||||
* F1: undefined 112 undefined
|
||||
* F8: undefined 119 undefined
|
||||
* ...
|
||||
* p: undefined 80 undefined
|
||||
* P: undefined 80 undefined
|
||||
*
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @see ../demos/keyhandler.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.KeyEvent');
|
||||
goog.provide('goog.events.KeyHandler');
|
||||
goog.provide('goog.events.KeyHandler.EventType');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper around an element that you want to listen to keyboard events on.
|
||||
* @param {Element|Document=} opt_element The element or document to listen on.
|
||||
* @param {boolean=} opt_capture Whether to listen for browser events in
|
||||
* capture phase (defaults to false).
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.events.KeyHandler = function(opt_element, opt_capture) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
if (opt_element) {
|
||||
this.attach(opt_element, opt_capture);
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.events.KeyHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* This is the element that we will listen to the real keyboard events on.
|
||||
* @type {Element|Document|null}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.element_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The key for the key press listener.
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.keyPressKey_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The key for the key down listener.
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.keyDownKey_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The key for the key up listener.
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.keyUpKey_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Used to detect keyboard repeat events.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.lastKey_ = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Keycode recorded for key down events. As most browsers don't report the
|
||||
* keycode in the key press event we need to record it in the key down phase.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.keyCode_ = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Alt key recorded for key down events. FF on Mac does not report the alt key
|
||||
* flag in the key press event, we need to record it in the key down phase.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.altKey_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Enum type for the events fired by the key handler
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.KeyHandler.EventType = {
|
||||
KEY: 'key'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An enumeration of key codes that Safari 2 does incorrectly
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.safariKey_ = {
|
||||
'3': goog.events.KeyCodes.ENTER, // 13
|
||||
'12': goog.events.KeyCodes.NUMLOCK, // 144
|
||||
'63232': goog.events.KeyCodes.UP, // 38
|
||||
'63233': goog.events.KeyCodes.DOWN, // 40
|
||||
'63234': goog.events.KeyCodes.LEFT, // 37
|
||||
'63235': goog.events.KeyCodes.RIGHT, // 39
|
||||
'63236': goog.events.KeyCodes.F1, // 112
|
||||
'63237': goog.events.KeyCodes.F2, // 113
|
||||
'63238': goog.events.KeyCodes.F3, // 114
|
||||
'63239': goog.events.KeyCodes.F4, // 115
|
||||
'63240': goog.events.KeyCodes.F5, // 116
|
||||
'63241': goog.events.KeyCodes.F6, // 117
|
||||
'63242': goog.events.KeyCodes.F7, // 118
|
||||
'63243': goog.events.KeyCodes.F8, // 119
|
||||
'63244': goog.events.KeyCodes.F9, // 120
|
||||
'63245': goog.events.KeyCodes.F10, // 121
|
||||
'63246': goog.events.KeyCodes.F11, // 122
|
||||
'63247': goog.events.KeyCodes.F12, // 123
|
||||
'63248': goog.events.KeyCodes.PRINT_SCREEN, // 44
|
||||
'63272': goog.events.KeyCodes.DELETE, // 46
|
||||
'63273': goog.events.KeyCodes.HOME, // 36
|
||||
'63275': goog.events.KeyCodes.END, // 35
|
||||
'63276': goog.events.KeyCodes.PAGE_UP, // 33
|
||||
'63277': goog.events.KeyCodes.PAGE_DOWN, // 34
|
||||
'63289': goog.events.KeyCodes.NUMLOCK, // 144
|
||||
'63302': goog.events.KeyCodes.INSERT // 45
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An enumeration of key identifiers currently part of the W3C draft for DOM3
|
||||
* and their mappings to keyCodes.
|
||||
* http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set
|
||||
* This is currently supported in Safari and should be platform independent.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.keyIdentifier_ = {
|
||||
'Up': goog.events.KeyCodes.UP, // 38
|
||||
'Down': goog.events.KeyCodes.DOWN, // 40
|
||||
'Left': goog.events.KeyCodes.LEFT, // 37
|
||||
'Right': goog.events.KeyCodes.RIGHT, // 39
|
||||
'Enter': goog.events.KeyCodes.ENTER, // 13
|
||||
'F1': goog.events.KeyCodes.F1, // 112
|
||||
'F2': goog.events.KeyCodes.F2, // 113
|
||||
'F3': goog.events.KeyCodes.F3, // 114
|
||||
'F4': goog.events.KeyCodes.F4, // 115
|
||||
'F5': goog.events.KeyCodes.F5, // 116
|
||||
'F6': goog.events.KeyCodes.F6, // 117
|
||||
'F7': goog.events.KeyCodes.F7, // 118
|
||||
'F8': goog.events.KeyCodes.F8, // 119
|
||||
'F9': goog.events.KeyCodes.F9, // 120
|
||||
'F10': goog.events.KeyCodes.F10, // 121
|
||||
'F11': goog.events.KeyCodes.F11, // 122
|
||||
'F12': goog.events.KeyCodes.F12, // 123
|
||||
'U+007F': goog.events.KeyCodes.DELETE, // 46
|
||||
'Home': goog.events.KeyCodes.HOME, // 36
|
||||
'End': goog.events.KeyCodes.END, // 35
|
||||
'PageUp': goog.events.KeyCodes.PAGE_UP, // 33
|
||||
'PageDown': goog.events.KeyCodes.PAGE_DOWN, // 34
|
||||
'Insert': goog.events.KeyCodes.INSERT // 45
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If true, the KeyEvent fires on keydown. Otherwise, it fires on keypress.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.USES_KEYDOWN_ = goog.userAgent.IE ||
|
||||
goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525');
|
||||
|
||||
|
||||
/**
|
||||
* If true, the alt key flag is saved during the key down and reused when
|
||||
* handling the key press. FF on Mac does not set the alt flag in the key press
|
||||
* event.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_ = goog.userAgent.MAC &&
|
||||
goog.userAgent.GECKO;
|
||||
|
||||
|
||||
/**
|
||||
* Records the keycode for browsers that only returns the keycode for key up/
|
||||
* down events. For browser/key combinations that doesn't trigger a key pressed
|
||||
* event it also fires the patched key event.
|
||||
* @param {goog.events.BrowserEvent} e The key down event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.handleKeyDown_ = function(e) {
|
||||
// Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window
|
||||
// before we've caught a key-up event. If the last-key was one of these we
|
||||
// reset the state.
|
||||
|
||||
if (goog.userAgent.WEBKIT) {
|
||||
if (this.lastKey_ == goog.events.KeyCodes.CTRL && !e.ctrlKey ||
|
||||
this.lastKey_ == goog.events.KeyCodes.ALT && !e.altKey ||
|
||||
goog.userAgent.MAC &&
|
||||
this.lastKey_ == goog.events.KeyCodes.META && !e.metaKey) {
|
||||
this.lastKey_ = -1;
|
||||
this.keyCode_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.lastKey_ == -1) {
|
||||
if (e.ctrlKey && e.keyCode != goog.events.KeyCodes.CTRL) {
|
||||
this.lastKey_ = goog.events.KeyCodes.CTRL;
|
||||
} else if (e.altKey && e.keyCode != goog.events.KeyCodes.ALT) {
|
||||
this.lastKey_ = goog.events.KeyCodes.ALT;
|
||||
} else if (e.metaKey && e.keyCode != goog.events.KeyCodes.META) {
|
||||
this.lastKey_ = goog.events.KeyCodes.META;
|
||||
}
|
||||
}
|
||||
|
||||
if (goog.events.KeyHandler.USES_KEYDOWN_ &&
|
||||
!goog.events.KeyCodes.firesKeyPressEvent(e.keyCode,
|
||||
this.lastKey_, e.shiftKey, e.ctrlKey, e.altKey)) {
|
||||
this.handleEvent(e);
|
||||
} else {
|
||||
this.keyCode_ = goog.userAgent.GECKO ?
|
||||
goog.events.KeyCodes.normalizeGeckoKeyCode(e.keyCode) :
|
||||
e.keyCode;
|
||||
if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {
|
||||
this.altKey_ = e.altKey;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets the stored previous values. Needed to be called for webkit which will
|
||||
* not generate a key up for meta key operations. This should only be called
|
||||
* when having finished with repeat key possiblities.
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.resetState = function() {
|
||||
this.lastKey_ = -1;
|
||||
this.keyCode_ = -1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the stored previous key value, resetting the key repeat status. Uses
|
||||
* -1 because the Safari 3 Windows beta reports 0 for certain keys (like Home
|
||||
* and End.)
|
||||
* @param {goog.events.BrowserEvent} e The keyup event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.handleKeyup_ = function(e) {
|
||||
this.resetState();
|
||||
this.altKey_ = e.altKey;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the events on the element.
|
||||
* @param {goog.events.BrowserEvent} e The keyboard event sent from the
|
||||
* browser.
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.handleEvent = function(e) {
|
||||
var be = e.getBrowserEvent();
|
||||
var keyCode, charCode;
|
||||
var altKey = be.altKey;
|
||||
|
||||
// IE reports the character code in the keyCode field for keypress events.
|
||||
// There are two exceptions however, Enter and Escape.
|
||||
if (goog.userAgent.IE && e.type == goog.events.EventType.KEYPRESS) {
|
||||
keyCode = this.keyCode_;
|
||||
charCode = keyCode != goog.events.KeyCodes.ENTER &&
|
||||
keyCode != goog.events.KeyCodes.ESC ?
|
||||
be.keyCode : 0;
|
||||
|
||||
// Safari reports the character code in the keyCode field for keypress
|
||||
// events but also has a charCode field.
|
||||
} else if (goog.userAgent.WEBKIT &&
|
||||
e.type == goog.events.EventType.KEYPRESS) {
|
||||
keyCode = this.keyCode_;
|
||||
charCode = be.charCode >= 0 && be.charCode < 63232 &&
|
||||
goog.events.KeyCodes.isCharacterKey(keyCode) ?
|
||||
be.charCode : 0;
|
||||
|
||||
// Opera reports the keycode or the character code in the keyCode field.
|
||||
} else if (goog.userAgent.OPERA) {
|
||||
keyCode = this.keyCode_;
|
||||
charCode = goog.events.KeyCodes.isCharacterKey(keyCode) ?
|
||||
be.keyCode : 0;
|
||||
|
||||
// Mozilla reports the character code in the charCode field.
|
||||
} else {
|
||||
keyCode = be.keyCode || this.keyCode_;
|
||||
charCode = be.charCode || 0;
|
||||
if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {
|
||||
altKey = this.altKey_;
|
||||
}
|
||||
// On the Mac, shift-/ triggers a question mark char code and no key code
|
||||
// (normalized to WIN_KEY), so we synthesize the latter.
|
||||
if (goog.userAgent.MAC &&
|
||||
charCode == goog.events.KeyCodes.QUESTION_MARK &&
|
||||
keyCode == goog.events.KeyCodes.WIN_KEY) {
|
||||
keyCode = goog.events.KeyCodes.SLASH;
|
||||
}
|
||||
}
|
||||
|
||||
var key = keyCode;
|
||||
var keyIdentifier = be.keyIdentifier;
|
||||
|
||||
// Correct the key value for certain browser-specific quirks.
|
||||
if (keyCode) {
|
||||
if (keyCode >= 63232 && keyCode in goog.events.KeyHandler.safariKey_) {
|
||||
// NOTE(nicksantos): Safari 3 has fixed this problem,
|
||||
// this is only needed for Safari 2.
|
||||
key = goog.events.KeyHandler.safariKey_[keyCode];
|
||||
} else {
|
||||
|
||||
// Safari returns 25 for Shift+Tab instead of 9.
|
||||
if (keyCode == 25 && e.shiftKey) {
|
||||
key = 9;
|
||||
}
|
||||
}
|
||||
} else if (keyIdentifier &&
|
||||
keyIdentifier in goog.events.KeyHandler.keyIdentifier_) {
|
||||
// This is needed for Safari Windows because it currently doesn't give a
|
||||
// keyCode/which for non printable keys.
|
||||
key = goog.events.KeyHandler.keyIdentifier_[keyIdentifier];
|
||||
}
|
||||
|
||||
// If we get the same keycode as a keydown/keypress without having seen a
|
||||
// keyup event, then this event was caused by key repeat.
|
||||
var repeat = key == this.lastKey_;
|
||||
this.lastKey_ = key;
|
||||
|
||||
var event = new goog.events.KeyEvent(key, charCode, repeat, be);
|
||||
event.altKey = altKey;
|
||||
this.dispatchEvent(event);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element listened on for the real keyboard events.
|
||||
* @return {Element|Document|null} The element listened on for the real
|
||||
* keyboard events.
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.getElement = function() {
|
||||
return this.element_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds the proper key event listeners to the element.
|
||||
* @param {Element|Document} element The element to listen on.
|
||||
* @param {boolean=} opt_capture Whether to listen for browser events in
|
||||
* capture phase (defaults to false).
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.attach = function(element, opt_capture) {
|
||||
if (this.keyUpKey_) {
|
||||
this.detach();
|
||||
}
|
||||
|
||||
this.element_ = element;
|
||||
|
||||
this.keyPressKey_ = goog.events.listen(this.element_,
|
||||
goog.events.EventType.KEYPRESS,
|
||||
this,
|
||||
opt_capture);
|
||||
|
||||
// Most browsers (Safari 2 being the notable exception) doesn't include the
|
||||
// keyCode in keypress events (IE has the char code in the keyCode field and
|
||||
// Mozilla only included the keyCode if there's no charCode). Thus we have to
|
||||
// listen for keydown to capture the keycode.
|
||||
this.keyDownKey_ = goog.events.listen(this.element_,
|
||||
goog.events.EventType.KEYDOWN,
|
||||
this.handleKeyDown_,
|
||||
opt_capture,
|
||||
this);
|
||||
|
||||
|
||||
this.keyUpKey_ = goog.events.listen(this.element_,
|
||||
goog.events.EventType.KEYUP,
|
||||
this.handleKeyup_,
|
||||
opt_capture,
|
||||
this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the listeners that may exist.
|
||||
*/
|
||||
goog.events.KeyHandler.prototype.detach = function() {
|
||||
if (this.keyPressKey_) {
|
||||
goog.events.unlistenByKey(this.keyPressKey_);
|
||||
goog.events.unlistenByKey(this.keyDownKey_);
|
||||
goog.events.unlistenByKey(this.keyUpKey_);
|
||||
this.keyPressKey_ = null;
|
||||
this.keyDownKey_ = null;
|
||||
this.keyUpKey_ = null;
|
||||
}
|
||||
this.element_ = null;
|
||||
this.lastKey_ = -1;
|
||||
this.keyCode_ = -1;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.KeyHandler.prototype.disposeInternal = function() {
|
||||
goog.events.KeyHandler.superClass_.disposeInternal.call(this);
|
||||
this.detach();
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class is used for the goog.events.KeyHandler.EventType.KEY event and
|
||||
* it overrides the key code with the fixed key code.
|
||||
* @param {number} keyCode The adjusted key code.
|
||||
* @param {number} charCode The unicode character code.
|
||||
* @param {boolean} repeat Whether this event was generated by keyboard repeat.
|
||||
* @param {Event} browserEvent Browser event object.
|
||||
* @constructor
|
||||
* @extends {goog.events.BrowserEvent}
|
||||
*/
|
||||
goog.events.KeyEvent = function(keyCode, charCode, repeat, browserEvent) {
|
||||
goog.events.BrowserEvent.call(this, browserEvent);
|
||||
this.type = goog.events.KeyHandler.EventType.KEY;
|
||||
|
||||
/**
|
||||
* Keycode of key press.
|
||||
* @type {number}
|
||||
*/
|
||||
this.keyCode = keyCode;
|
||||
|
||||
/**
|
||||
* Unicode character code.
|
||||
* @type {number}
|
||||
*/
|
||||
this.charCode = charCode;
|
||||
|
||||
/**
|
||||
* True if this event was generated by keyboard auto-repeat (i.e., the user is
|
||||
* holding the key down.)
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.repeat = repeat;
|
||||
};
|
||||
goog.inherits(goog.events.KeyEvent, goog.events.BrowserEvent);
|
||||
132
nicer-api-docs/closure-library/closure/goog/events/keynames.js
Normal file
132
nicer-api-docs/closure-library/closure/goog/events/keynames.js
Normal file
@@ -0,0 +1,132 @@
|
||||
// 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 Constant declarations for common key codes.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.KeyNames');
|
||||
|
||||
|
||||
/**
|
||||
* Key names for common characters.
|
||||
*
|
||||
* This list is not localized and therefore some of the key codes are not
|
||||
* correct for non-US keyboard layouts.
|
||||
*
|
||||
* @see goog.events.KeyCodes
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.KeyNames = {
|
||||
8: 'backspace',
|
||||
9: 'tab',
|
||||
13: 'enter',
|
||||
16: 'shift',
|
||||
17: 'ctrl',
|
||||
18: 'alt',
|
||||
19: 'pause',
|
||||
20: 'caps-lock',
|
||||
27: 'esc',
|
||||
32: 'space',
|
||||
33: 'pg-up',
|
||||
34: 'pg-down',
|
||||
35: 'end',
|
||||
36: 'home',
|
||||
37: 'left',
|
||||
38: 'up',
|
||||
39: 'right',
|
||||
40: 'down',
|
||||
45: 'insert',
|
||||
46: 'delete',
|
||||
48: '0',
|
||||
49: '1',
|
||||
50: '2',
|
||||
51: '3',
|
||||
52: '4',
|
||||
53: '5',
|
||||
54: '6',
|
||||
55: '7',
|
||||
56: '8',
|
||||
57: '9',
|
||||
59: 'semicolon',
|
||||
61: 'equals',
|
||||
65: 'a',
|
||||
66: 'b',
|
||||
67: 'c',
|
||||
68: 'd',
|
||||
69: 'e',
|
||||
70: 'f',
|
||||
71: 'g',
|
||||
72: 'h',
|
||||
73: 'i',
|
||||
74: 'j',
|
||||
75: 'k',
|
||||
76: 'l',
|
||||
77: 'm',
|
||||
78: 'n',
|
||||
79: 'o',
|
||||
80: 'p',
|
||||
81: 'q',
|
||||
82: 'r',
|
||||
83: 's',
|
||||
84: 't',
|
||||
85: 'u',
|
||||
86: 'v',
|
||||
87: 'w',
|
||||
88: 'x',
|
||||
89: 'y',
|
||||
90: 'z',
|
||||
93: 'context',
|
||||
96: 'num-0',
|
||||
97: 'num-1',
|
||||
98: 'num-2',
|
||||
99: 'num-3',
|
||||
100: 'num-4',
|
||||
101: 'num-5',
|
||||
102: 'num-6',
|
||||
103: 'num-7',
|
||||
104: 'num-8',
|
||||
105: 'num-9',
|
||||
106: 'num-multiply',
|
||||
107: 'num-plus',
|
||||
109: 'num-minus',
|
||||
110: 'num-period',
|
||||
111: 'num-division',
|
||||
112: 'f1',
|
||||
113: 'f2',
|
||||
114: 'f3',
|
||||
115: 'f4',
|
||||
116: 'f5',
|
||||
117: 'f6',
|
||||
118: 'f7',
|
||||
119: 'f8',
|
||||
120: 'f9',
|
||||
121: 'f10',
|
||||
122: 'f11',
|
||||
123: 'f12',
|
||||
186: 'semicolon',
|
||||
187: 'equals',
|
||||
189: 'dash',
|
||||
188: ',',
|
||||
190: '.',
|
||||
191: '/',
|
||||
192: '~',
|
||||
219: 'open-square-bracket',
|
||||
220: '\\',
|
||||
221: 'close-square-bracket',
|
||||
222: 'single-quote',
|
||||
224: 'win'
|
||||
};
|
||||
323
nicer-api-docs/closure-library/closure/goog/events/listenable.js
Normal file
323
nicer-api-docs/closure-library/closure/goog/events/listenable.js
Normal file
@@ -0,0 +1,323 @@
|
||||
// Copyright 2012 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 An interface for a listenable JavaScript object.
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.Listenable');
|
||||
goog.provide('goog.events.ListenableKey');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A listenable interface. A listenable is an object with the ability
|
||||
* to dispatch/broadcast events to "event listeners" registered via
|
||||
* listen/listenOnce.
|
||||
*
|
||||
* The interface allows for an event propagation mechanism similar
|
||||
* to one offered by native browser event targets, such as
|
||||
* capture/bubble mechanism, stopping propagation, and preventing
|
||||
* default actions. Capture/bubble mechanism depends on the ancestor
|
||||
* tree constructed via {@code #getParentEventTarget}; this tree
|
||||
* must be directed acyclic graph. The meaning of default action(s)
|
||||
* in preventDefault is specific to a particular use case.
|
||||
*
|
||||
* Implementations that do not support capture/bubble or can not have
|
||||
* a parent listenable can simply not implement any ability to set the
|
||||
* parent listenable (and have {@code #getParentEventTarget} return
|
||||
* null).
|
||||
*
|
||||
* Implementation of this class can be used with or independently from
|
||||
* goog.events.
|
||||
*
|
||||
* Implementation must call {@code #addImplementation(implClass)}.
|
||||
*
|
||||
* @interface
|
||||
* @see goog.events
|
||||
* @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
|
||||
*/
|
||||
goog.events.Listenable = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* An expando property to indicate that an object implements
|
||||
* goog.events.Listenable.
|
||||
*
|
||||
* See addImplementation/isImplementedBy.
|
||||
*
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
goog.events.Listenable.IMPLEMENTED_BY_PROP =
|
||||
'closure_listenable_' + ((Math.random() * 1e6) | 0);
|
||||
|
||||
|
||||
/**
|
||||
* Marks a given class (constructor) as an implementation of
|
||||
* Listenable, do that we can query that fact at runtime. The class
|
||||
* must have already implemented the interface.
|
||||
* @param {!Function} cls The class constructor. The corresponding
|
||||
* class must have already implemented the interface.
|
||||
*/
|
||||
goog.events.Listenable.addImplementation = function(cls) {
|
||||
cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Object} obj The object to check.
|
||||
* @return {boolean} Whether a given instance implements
|
||||
* Listenable. The class/superclass of the instance must call
|
||||
* addImplementation.
|
||||
*/
|
||||
goog.events.Listenable.isImplementedBy = function(obj) {
|
||||
return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an event listener. A listener can only be added once to an
|
||||
* object and if it is added again the key for the listener is
|
||||
* returned. Note that if the existing listener is a one-off listener
|
||||
* (registered via listenOnce), it will no longer be a one-off
|
||||
* listener after a call to listen().
|
||||
*
|
||||
* @param {string} type Event type or array of event types.
|
||||
* @param {!Function} listener Callback method, or an object
|
||||
* with a handleEvent function.
|
||||
* @param {boolean=} opt_useCapture Whether to fire in capture phase
|
||||
* (defaults to false).
|
||||
* @param {Object=} opt_listenerScope Object in whose scope to call the
|
||||
* listener.
|
||||
* @return {goog.events.ListenableKey} Unique key for the listener.
|
||||
*/
|
||||
goog.events.Listenable.prototype.listen;
|
||||
|
||||
|
||||
/**
|
||||
* Adds an event listener that is removed automatically after the
|
||||
* listener fired once.
|
||||
*
|
||||
* If an existing listener already exists, listenOnce will do
|
||||
* nothing. In particular, if the listener was previously registered
|
||||
* via listen(), listenOnce() will not turn the listener into a
|
||||
* one-off listener. Similarly, if there is already an existing
|
||||
* one-off listener, listenOnce does not modify the listeners (it is
|
||||
* still a once listener).
|
||||
*
|
||||
* @param {string} type Event type or array of event types.
|
||||
* @param {!Function} listener Callback method, or an object
|
||||
* with a handleEvent function.
|
||||
* @param {boolean=} opt_useCapture Whether to fire in capture phase
|
||||
* (defaults to false).
|
||||
* @param {Object=} opt_listenerScope Object in whose scope to call the
|
||||
* listener.
|
||||
* @return {goog.events.ListenableKey} Unique key for the listener.
|
||||
*/
|
||||
goog.events.Listenable.prototype.listenOnce;
|
||||
|
||||
|
||||
/**
|
||||
* Removes an event listener which was added with listen() or listenOnce().
|
||||
*
|
||||
* @param {string} type Event type or array of event types.
|
||||
* @param {!Function} listener Callback method, or an object
|
||||
* with a handleEvent function.
|
||||
* @param {boolean=} opt_useCapture Whether to fire in capture phase
|
||||
* (defaults to false).
|
||||
* @param {Object=} opt_listenerScope Object in whose scope to call
|
||||
* the listener.
|
||||
* @return {boolean} Whether any listener was removed.
|
||||
*/
|
||||
goog.events.Listenable.prototype.unlisten;
|
||||
|
||||
|
||||
/**
|
||||
* Removes an event listener which was added with listen() by the key
|
||||
* returned by listen().
|
||||
*
|
||||
* @param {goog.events.ListenableKey} key The key returned by
|
||||
* listen() or listenOnce().
|
||||
* @return {boolean} Whether any listener was removed.
|
||||
*/
|
||||
goog.events.Listenable.prototype.unlistenByKey;
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches an event (or event like object) and calls all listeners
|
||||
* listening for events of this type. The type of the event is decided by the
|
||||
* type property on the event object.
|
||||
*
|
||||
* If any of the listeners returns false OR calls preventDefault then this
|
||||
* function will return false. If one of the capture listeners calls
|
||||
* stopPropagation, then the bubble listeners won't fire.
|
||||
*
|
||||
* @param {goog.events.EventLike} e Event object.
|
||||
* @return {boolean} If anyone called preventDefault on the event object (or
|
||||
* if any of the listeners returns false) this will also return false.
|
||||
*/
|
||||
goog.events.Listenable.prototype.dispatchEvent;
|
||||
|
||||
|
||||
/**
|
||||
* Removes all listeners from this listenable. If type is specified,
|
||||
* it will only remove listeners of the particular type. otherwise all
|
||||
* registered listeners will be removed.
|
||||
*
|
||||
* @param {string=} opt_type Type of event to remove, default is to
|
||||
* remove all types.
|
||||
* @return {number} Number of listeners removed.
|
||||
*/
|
||||
goog.events.Listenable.prototype.removeAllListeners;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parent of this event target to use for capture/bubble
|
||||
* mechanism.
|
||||
*
|
||||
* NOTE(user): The name reflects the original implementation of
|
||||
* custom event target ({@code goog.events.EventTarget}). We decided
|
||||
* that changing the name is not worth it.
|
||||
*
|
||||
* @return {goog.events.Listenable} The parent EventTarget or null if
|
||||
* there is no parent.
|
||||
*/
|
||||
goog.events.Listenable.prototype.getParentEventTarget;
|
||||
|
||||
|
||||
/**
|
||||
* Fires all registered listeners in this listenable for the given
|
||||
* type and capture mode, passing them the given eventObject. This
|
||||
* does not perform actual capture/bubble. Only implementors of the
|
||||
* interface should be using this.
|
||||
*
|
||||
* @param {string} type The type of the listeners to fire.
|
||||
* @param {boolean} capture The capture mode of the listeners to fire.
|
||||
* @param {goog.events.Event} eventObject The event object to fire.
|
||||
* @return {boolean} Whether all listeners succeeded without
|
||||
* attempting to prevent default behavior. If any listener returns
|
||||
* false or called goog.events.Event#preventDefault, this returns
|
||||
* false.
|
||||
*/
|
||||
goog.events.Listenable.prototype.fireListeners;
|
||||
|
||||
|
||||
/**
|
||||
* Gets all listeners in this listenable for the given type and
|
||||
* capture mode.
|
||||
*
|
||||
* @param {string} type The type of the listeners to fire.
|
||||
* @param {boolean} capture The capture mode of the listeners to fire.
|
||||
* @return {!Array.<goog.events.ListenableKey>} An array of registered
|
||||
* listeners.
|
||||
*/
|
||||
goog.events.Listenable.prototype.getListeners;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the goog.events.ListenableKey for the event or null if no such
|
||||
* listener is in use.
|
||||
*
|
||||
* @param {string} type The name of the event without the 'on' prefix.
|
||||
* @param {!Function} listener The listener function to get.
|
||||
* @param {boolean} capture Whether the listener is a capturing listener.
|
||||
* @param {Object=} opt_listenerScope Object in whose scope to call the
|
||||
* listener.
|
||||
* @return {goog.events.ListenableKey} the found listener or null if not found.
|
||||
*/
|
||||
goog.events.Listenable.prototype.getListener;
|
||||
|
||||
|
||||
/**
|
||||
* Whether there is any active listeners matching the specified
|
||||
* signature. If either the type or capture parameters are
|
||||
* unspecified, the function will match on the remaining criteria.
|
||||
*
|
||||
* @param {string=} opt_type Event type.
|
||||
* @param {boolean=} opt_capture Whether to check for capture or bubble
|
||||
* listeners.
|
||||
* @return {boolean} Whether there is any active listeners matching
|
||||
* the requested type and/or capture phase.
|
||||
*/
|
||||
goog.events.Listenable.prototype.hasListener;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An interface that describes a single registered listener.
|
||||
* @interface
|
||||
*/
|
||||
goog.events.ListenableKey = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Counter used to create a unique key
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.events.ListenableKey.counter_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Reserves a key to be used for ListenableKey#key field.
|
||||
* @return {number} A number to be used to fill ListenableKey#key
|
||||
* field.
|
||||
*/
|
||||
goog.events.ListenableKey.reserveKey = function() {
|
||||
return ++goog.events.ListenableKey.counter_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The source event target.
|
||||
* @type {!(Object|goog.events.Listenable|goog.events.EventTarget)}
|
||||
*/
|
||||
goog.events.ListenableKey.prototype.src;
|
||||
|
||||
|
||||
/**
|
||||
* The event type the listener is listening to.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.events.ListenableKey.prototype.type;
|
||||
|
||||
|
||||
/**
|
||||
* The listener function.
|
||||
* TODO(user): Narrow the type if possible.
|
||||
* @type {Function|Object}
|
||||
*/
|
||||
goog.events.ListenableKey.prototype.listener;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the listener works on capture phase.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.events.ListenableKey.prototype.capture;
|
||||
|
||||
|
||||
/**
|
||||
* The 'this' object for the listener function's scope.
|
||||
* @type {Object}
|
||||
*/
|
||||
goog.events.ListenableKey.prototype.handler;
|
||||
|
||||
|
||||
/**
|
||||
* A globally unique number to identify the key.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.ListenableKey.prototype.key;
|
||||
131
nicer-api-docs/closure-library/closure/goog/events/listener.js
Normal file
131
nicer-api-docs/closure-library/closure/goog/events/listener.js
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2005 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 Listener object.
|
||||
* @see ../demos/events.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.Listener');
|
||||
|
||||
goog.require('goog.events.ListenableKey');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple class that stores information about a listener
|
||||
* @param {!Function} listener Callback function.
|
||||
* @param {Function} proxy Wrapper for the listener that patches the event.
|
||||
* @param {EventTarget|goog.events.Listenable} src Source object for
|
||||
* the event.
|
||||
* @param {string} type Event type.
|
||||
* @param {boolean} capture Whether in capture or bubble phase.
|
||||
* @param {Object=} opt_handler Object in whose context to execute the callback.
|
||||
* @implements {goog.events.ListenableKey}
|
||||
* @constructor
|
||||
*/
|
||||
goog.events.Listener = function(
|
||||
listener, proxy, src, type, capture, opt_handler) {
|
||||
if (goog.events.Listener.ENABLE_MONITORING) {
|
||||
this.creationStack = new Error().stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function.
|
||||
* @type {Function}
|
||||
*/
|
||||
this.listener = listener;
|
||||
|
||||
/**
|
||||
* A wrapper over the original listener. This is used solely to
|
||||
* handle native browser events (it is used to simulate the capture
|
||||
* phase and to patch the event object).
|
||||
* @type {Function}
|
||||
*/
|
||||
this.proxy = proxy;
|
||||
|
||||
/**
|
||||
* Object or node that callback is listening to
|
||||
* @type {EventTarget|goog.events.Listenable}
|
||||
*/
|
||||
this.src = src;
|
||||
|
||||
/**
|
||||
* The event type.
|
||||
* @const {string}
|
||||
*/
|
||||
this.type = type;
|
||||
|
||||
/**
|
||||
* Whether the listener is being called in the capture or bubble phase
|
||||
* @const {boolean}
|
||||
*/
|
||||
this.capture = !!capture;
|
||||
|
||||
/**
|
||||
* Optional object whose context to execute the listener in
|
||||
* @type {Object|undefined}
|
||||
*/
|
||||
this.handler = opt_handler;
|
||||
|
||||
/**
|
||||
* The key of the listener.
|
||||
* @const {number}
|
||||
* @override
|
||||
*/
|
||||
this.key = goog.events.ListenableKey.reserveKey();
|
||||
|
||||
/**
|
||||
* Whether to remove the listener after it has been called.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.callOnce = false;
|
||||
|
||||
/**
|
||||
* Whether the listener has been removed.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.removed = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @define {boolean} Whether to enable the monitoring of the
|
||||
* goog.events.Listener instances. Switching on the monitoring is only
|
||||
* recommended for debugging because it has a significant impact on
|
||||
* performance and memory usage. If switched off, the monitoring code
|
||||
* compiles down to 0 bytes.
|
||||
*/
|
||||
goog.define('goog.events.Listener.ENABLE_MONITORING', false);
|
||||
|
||||
|
||||
/**
|
||||
* If monitoring the goog.events.Listener instances is enabled, stores the
|
||||
* creation stack trace of the Disposable instance.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.events.Listener.prototype.creationStack;
|
||||
|
||||
|
||||
/**
|
||||
* Marks this listener as removed. This also remove references held by
|
||||
* this listener object (such as listener and event source).
|
||||
*/
|
||||
goog.events.Listener.prototype.markAsRemoved = function() {
|
||||
this.removed = true;
|
||||
this.listener = null;
|
||||
this.proxy = null;
|
||||
this.src = null;
|
||||
this.handler = null;
|
||||
};
|
||||
@@ -0,0 +1,299 @@
|
||||
// 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 A map of listeners that provides utility functions to
|
||||
* deal with listeners on an event target. Used by
|
||||
* {@code goog.events.EventTarget}.
|
||||
*
|
||||
* WARNING: Do not use this class from outside goog.events package.
|
||||
*
|
||||
* @visibility {//closure/goog/events:__pkg__}
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.ListenerMap');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.events.Listener');
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new listener map.
|
||||
* @param {EventTarget|goog.events.Listenable} src The src object.
|
||||
* @constructor
|
||||
*/
|
||||
goog.events.ListenerMap = function(src) {
|
||||
/** @type {EventTarget|goog.events.Listenable} */
|
||||
this.src = src;
|
||||
|
||||
/**
|
||||
* Maps of event type to an array of listeners.
|
||||
* @type {Object.<string, !Array.<!goog.events.Listener>>}
|
||||
*/
|
||||
this.listeners = {};
|
||||
|
||||
/**
|
||||
* The count of types in this map that have registered listeners.
|
||||
* @private {number}
|
||||
*/
|
||||
this.typeCount_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The count of event types in this map that actually
|
||||
* have registered listeners.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.getTypeCount = function() {
|
||||
return this.typeCount_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Total number of registered listeners.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.getListenerCount = function() {
|
||||
var count = 0;
|
||||
for (var type in this.listeners) {
|
||||
count += this.listeners[type].length;
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an event listener. A listener can only be added once to an
|
||||
* object and if it is added again the key for the listener is
|
||||
* returned.
|
||||
*
|
||||
* Note that a one-off listener will not change an existing listener,
|
||||
* if any. On the other hand a normal listener will change existing
|
||||
* one-off listener to become a normal listener.
|
||||
*
|
||||
* @param {string} type The listener event type.
|
||||
* @param {!Function} listener This listener callback method.
|
||||
* @param {boolean} callOnce Whether the listener is a one-off
|
||||
* listener.
|
||||
* @param {boolean=} opt_useCapture The capture mode of the listener.
|
||||
* @param {Object=} opt_listenerScope Object in whose scope to call the
|
||||
* listener.
|
||||
* @return {goog.events.ListenableKey} Unique key for the listener.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.add = function(
|
||||
type, listener, callOnce, opt_useCapture, opt_listenerScope) {
|
||||
var listenerArray = this.listeners[type];
|
||||
if (!listenerArray) {
|
||||
listenerArray = this.listeners[type] = [];
|
||||
this.typeCount_++;
|
||||
}
|
||||
|
||||
var listenerObj;
|
||||
var index = goog.events.ListenerMap.findListenerIndex_(
|
||||
listenerArray, listener, opt_useCapture, opt_listenerScope);
|
||||
if (index > -1) {
|
||||
listenerObj = listenerArray[index];
|
||||
if (!callOnce) {
|
||||
// Ensure that, if there is an existing callOnce listener, it is no
|
||||
// longer a callOnce listener.
|
||||
listenerObj.callOnce = false;
|
||||
}
|
||||
} else {
|
||||
listenerObj = new goog.events.Listener(
|
||||
listener, null, this.src, type, !!opt_useCapture, opt_listenerScope);
|
||||
listenerObj.callOnce = callOnce;
|
||||
listenerArray.push(listenerObj);
|
||||
}
|
||||
return listenerObj;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a matching listener.
|
||||
* @param {string} type The listener event type.
|
||||
* @param {!Function} listener This listener callback method.
|
||||
* @param {boolean=} opt_useCapture The capture mode of the listener.
|
||||
* @param {Object=} opt_listenerScope Object in whose scope to call the
|
||||
* listener.
|
||||
* @return {boolean} Whether any listener was removed.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.remove = function(
|
||||
type, listener, opt_useCapture, opt_listenerScope) {
|
||||
if (!(type in this.listeners)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var listenerArray = this.listeners[type];
|
||||
var index = goog.events.ListenerMap.findListenerIndex_(
|
||||
listenerArray, listener, opt_useCapture, opt_listenerScope);
|
||||
if (index > -1) {
|
||||
var listenerObj = listenerArray[index];
|
||||
listenerObj.markAsRemoved();
|
||||
goog.array.removeAt(listenerArray, index);
|
||||
if (listenerArray.length == 0) {
|
||||
delete this.listeners[type];
|
||||
this.typeCount_--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the given listener object.
|
||||
* @param {goog.events.ListenableKey} listener The listener to remove.
|
||||
* @return {boolean} Whether the listener is removed.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.removeByKey = function(listener) {
|
||||
var type = listener.type;
|
||||
if (!(type in this.listeners)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var removed = goog.array.remove(this.listeners[type], listener);
|
||||
if (removed) {
|
||||
listener.markAsRemoved();
|
||||
if (this.listeners[type].length == 0) {
|
||||
delete this.listeners[type];
|
||||
this.typeCount_--;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes all listeners from this map. If opt_type is provided, only
|
||||
* listeners that match the given type are removed.
|
||||
* @param {string=} opt_type Type of event to remove.
|
||||
* @return {number} Number of listeners removed.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.removeAll = function(opt_type) {
|
||||
var count = 0;
|
||||
for (var type in this.listeners) {
|
||||
if (!opt_type || type == opt_type) {
|
||||
var listenerArray = this.listeners[type];
|
||||
for (var i = 0; i < listenerArray.length; i++) {
|
||||
++count;
|
||||
listenerArray[i].removed = true;
|
||||
}
|
||||
delete this.listeners[type];
|
||||
this.typeCount_--;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets all listeners that match the given type and capture mode. The
|
||||
* returned array is a copy (but the listener objects are not).
|
||||
* @param {string} type The type of the listeners to retrieve.
|
||||
* @param {boolean} capture The capture mode of the listeners to retrieve.
|
||||
* @return {!Array.<goog.events.ListenableKey>} An array of matching
|
||||
* listeners.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.getListeners = function(type, capture) {
|
||||
var listenerArray = this.listeners[type];
|
||||
var rv = [];
|
||||
if (listenerArray) {
|
||||
for (var i = 0; i < listenerArray.length; ++i) {
|
||||
var listenerObj = listenerArray[i];
|
||||
if (listenerObj.capture == capture) {
|
||||
rv.push(listenerObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the goog.events.ListenableKey for the event or null if no such
|
||||
* listener is in use.
|
||||
*
|
||||
* @param {string} type The type of the listener to retrieve.
|
||||
* @param {!Function} listener The listener function to get.
|
||||
* @param {boolean} capture Whether the listener is a capturing listener.
|
||||
* @param {Object=} opt_listenerScope Object in whose scope to call the
|
||||
* listener.
|
||||
* @return {goog.events.ListenableKey} the found listener or null if not found.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.getListener = function(
|
||||
type, listener, capture, opt_listenerScope) {
|
||||
var listenerArray = this.listeners[type];
|
||||
var i = -1;
|
||||
if (listenerArray) {
|
||||
i = goog.events.ListenerMap.findListenerIndex_(
|
||||
listenerArray, listener, capture, opt_listenerScope);
|
||||
}
|
||||
return i > -1 ? listenerArray[i] : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether there is a matching listener. If either the type or capture
|
||||
* parameters are unspecified, the function will match on the
|
||||
* remaining criteria.
|
||||
*
|
||||
* @param {string=} opt_type The type of the listener.
|
||||
* @param {boolean=} opt_capture The capture mode of the listener.
|
||||
* @return {boolean} Whether there is an active listener matching
|
||||
* the requested type and/or capture phase.
|
||||
*/
|
||||
goog.events.ListenerMap.prototype.hasListener = function(
|
||||
opt_type, opt_capture) {
|
||||
var hasType = goog.isDef(opt_type);
|
||||
var hasCapture = goog.isDef(opt_capture);
|
||||
|
||||
return goog.object.some(
|
||||
this.listeners, function(listenerArray, type) {
|
||||
for (var i = 0; i < listenerArray.length; ++i) {
|
||||
if ((!hasType || listenerArray[i].type == opt_type) &&
|
||||
(!hasCapture || listenerArray[i].capture == opt_capture)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the index of a matching goog.events.Listener in the given
|
||||
* listenerArray.
|
||||
* @param {!Array.<!goog.events.Listener>} listenerArray Array of listener.
|
||||
* @param {!Function} listener The listener function.
|
||||
* @param {boolean=} opt_useCapture The capture flag for the listener.
|
||||
* @param {Object=} opt_listenerScope The listener scope.
|
||||
* @return {number} The index of the matching listener within the
|
||||
* listenerArray.
|
||||
* @private
|
||||
*/
|
||||
goog.events.ListenerMap.findListenerIndex_ = function(
|
||||
listenerArray, listener, opt_useCapture, opt_listenerScope) {
|
||||
for (var i = 0; i < listenerArray.length; ++i) {
|
||||
var listenerObj = listenerArray[i];
|
||||
if (!listenerObj.removed &&
|
||||
listenerObj.listener == listener &&
|
||||
listenerObj.capture == !!opt_useCapture &&
|
||||
listenerObj.handler == opt_listenerScope) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
// 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 Tests for listenermap.js.
|
||||
*
|
||||
* Most of this class functionality is already tested by
|
||||
* goog.events.EventTarget tests. This test file only provides tests
|
||||
* for features that are not direct duplicates of tests in
|
||||
* goog.events.EventTarget.
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.ListenerMapTest');
|
||||
goog.setTestOnly('goog.events.ListenerMapTest');
|
||||
|
||||
goog.require('goog.dispose');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.ListenerMap');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
|
||||
var et, map;
|
||||
var handler1 = function() {};
|
||||
var handler2 = function() {};
|
||||
var handler3 = function() {};
|
||||
var handler4 = function() {};
|
||||
var handler5 = function() {};
|
||||
|
||||
|
||||
function setUp() {
|
||||
et = new goog.events.EventTarget();
|
||||
map = new goog.events.ListenerMap(et);
|
||||
}
|
||||
|
||||
|
||||
function tearDown() {
|
||||
goog.dispose(et);
|
||||
}
|
||||
|
||||
|
||||
function testGetTypeCount() {
|
||||
assertEquals(0, map.getTypeCount());
|
||||
|
||||
map.add('click', handler1, false);
|
||||
assertEquals(1, map.getTypeCount());
|
||||
map.remove('click', handler1);
|
||||
assertEquals(0, map.getTypeCount());
|
||||
|
||||
map.add('click', handler1, false, true);
|
||||
assertEquals(1, map.getTypeCount());
|
||||
map.remove('click', handler1, true);
|
||||
assertEquals(0, map.getTypeCount());
|
||||
|
||||
map.add('click', handler1, false);
|
||||
map.add('click', handler1, false, true);
|
||||
assertEquals(1, map.getTypeCount());
|
||||
map.remove('click', handler1);
|
||||
assertEquals(1, map.getTypeCount());
|
||||
map.remove('click', handler1, true);
|
||||
assertEquals(0, map.getTypeCount());
|
||||
|
||||
map.add('click', handler1, false);
|
||||
map.add('touchstart', handler2, false);
|
||||
assertEquals(2, map.getTypeCount());
|
||||
map.remove('touchstart', handler2);
|
||||
assertEquals(1, map.getTypeCount());
|
||||
map.remove('click', handler1);
|
||||
assertEquals(0, map.getTypeCount());
|
||||
}
|
||||
|
||||
|
||||
function testGetListenerCount() {
|
||||
assertEquals(0, map.getListenerCount());
|
||||
|
||||
map.add('click', handler1, false);
|
||||
assertEquals(1, map.getListenerCount());
|
||||
map.remove('click', handler1);
|
||||
assertEquals(0, map.getListenerCount());
|
||||
|
||||
map.add('click', handler1, false, true);
|
||||
assertEquals(1, map.getListenerCount());
|
||||
map.remove('click', handler1, true);
|
||||
assertEquals(0, map.getListenerCount());
|
||||
|
||||
map.add('click', handler1, false);
|
||||
map.add('click', handler1, false, true);
|
||||
assertEquals(2, map.getListenerCount());
|
||||
map.remove('click', handler1);
|
||||
map.remove('click', handler1, true);
|
||||
assertEquals(0, map.getListenerCount());
|
||||
|
||||
map.add('click', handler1, false);
|
||||
map.add('touchstart', handler2, false);
|
||||
assertEquals(2, map.getListenerCount());
|
||||
map.remove('touchstart', handler2);
|
||||
map.remove('click', handler1);
|
||||
assertEquals(0, map.getListenerCount());
|
||||
}
|
||||
|
||||
|
||||
function testListenerSourceIsSetCorrectly() {
|
||||
map.add('click', handler1, false);
|
||||
var listener = map.getListener('click', handler1);
|
||||
assertEquals(et, listener.src);
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
// 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 This event wrapper will dispatch an event when the user uses
|
||||
* the mouse wheel to scroll an element. You can get the direction by checking
|
||||
* the deltaX and deltaY properties of the event.
|
||||
*
|
||||
* This class aims to smooth out inconsistencies between browser platforms with
|
||||
* regards to mousewheel events, but we do not cover every possible
|
||||
* software/hardware combination out there, some of which occasionally produce
|
||||
* very large deltas in mousewheel events. If your application wants to guard
|
||||
* against extremely large deltas, use the setMaxDeltaX and setMaxDeltaY APIs
|
||||
* to set maximum values that make sense for your application.
|
||||
*
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
* @see ../demos/mousewheelhandler.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.MouseWheelEvent');
|
||||
goog.provide('goog.events.MouseWheelHandler');
|
||||
goog.provide('goog.events.MouseWheelHandler.EventType');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.math');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This event handler allows you to catch mouse wheel events in a consistent
|
||||
* manner.
|
||||
* @param {Element|Document} element The element to listen to the mouse wheel
|
||||
* event on.
|
||||
* @param {boolean=} opt_capture Whether to handle the mouse wheel event in
|
||||
* capture phase.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.events.MouseWheelHandler = function(element, opt_capture) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* This is the element that we will listen to the real mouse wheel events on.
|
||||
* @type {Element|Document}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = element;
|
||||
|
||||
var rtlElement = goog.dom.isElement(this.element_) ?
|
||||
/** @type {Element} */ (this.element_) :
|
||||
(this.element_ ? /** @type {Document} */ (this.element_).body : null);
|
||||
|
||||
/**
|
||||
* True if the element exists and is RTL, false otherwise.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isRtl_ = !!rtlElement && goog.style.isRightToLeft(rtlElement);
|
||||
|
||||
var type = goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel';
|
||||
|
||||
/**
|
||||
* The key returned from the goog.events.listen.
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
this.listenKey_ = goog.events.listen(this.element_, type, this, opt_capture);
|
||||
};
|
||||
goog.inherits(goog.events.MouseWheelHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Enum type for the events fired by the mouse wheel handler.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.MouseWheelHandler.EventType = {
|
||||
MOUSEWHEEL: 'mousewheel'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Optional maximum magnitude for x delta on each mousewheel event.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.events.MouseWheelHandler.prototype.maxDeltaX_;
|
||||
|
||||
|
||||
/**
|
||||
* Optional maximum magnitude for y delta on each mousewheel event.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.events.MouseWheelHandler.prototype.maxDeltaY_;
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} maxDeltaX Maximum magnitude for x delta on each mousewheel
|
||||
* event. Should be non-negative.
|
||||
*/
|
||||
goog.events.MouseWheelHandler.prototype.setMaxDeltaX = function(maxDeltaX) {
|
||||
this.maxDeltaX_ = maxDeltaX;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} maxDeltaY Maximum magnitude for y delta on each mousewheel
|
||||
* event. Should be non-negative.
|
||||
*/
|
||||
goog.events.MouseWheelHandler.prototype.setMaxDeltaY = function(maxDeltaY) {
|
||||
this.maxDeltaY_ = maxDeltaY;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the events on the element.
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
*/
|
||||
goog.events.MouseWheelHandler.prototype.handleEvent = function(e) {
|
||||
var deltaX = 0;
|
||||
var deltaY = 0;
|
||||
var detail = 0;
|
||||
var be = e.getBrowserEvent();
|
||||
if (be.type == 'mousewheel') {
|
||||
var wheelDeltaScaleFactor = 1;
|
||||
if (goog.userAgent.IE ||
|
||||
goog.userAgent.WEBKIT &&
|
||||
(goog.userAgent.WINDOWS || goog.userAgent.isVersionOrHigher('532.0'))) {
|
||||
// In IE we get a multiple of 120; we adjust to a multiple of 3 to
|
||||
// represent number of lines scrolled (like Gecko).
|
||||
// Newer versions of Webkit match IE behavior, and WebKit on
|
||||
// Windows also matches IE behavior.
|
||||
// See bug https://bugs.webkit.org/show_bug.cgi?id=24368
|
||||
wheelDeltaScaleFactor = 40;
|
||||
}
|
||||
|
||||
detail = goog.events.MouseWheelHandler.smartScale_(
|
||||
-be.wheelDelta, wheelDeltaScaleFactor);
|
||||
if (goog.isDef(be.wheelDeltaX)) {
|
||||
// Webkit has two properties to indicate directional scroll, and
|
||||
// can scroll both directions at once.
|
||||
deltaX = goog.events.MouseWheelHandler.smartScale_(
|
||||
-be.wheelDeltaX, wheelDeltaScaleFactor);
|
||||
deltaY = goog.events.MouseWheelHandler.smartScale_(
|
||||
-be.wheelDeltaY, wheelDeltaScaleFactor);
|
||||
} else {
|
||||
deltaY = detail;
|
||||
}
|
||||
|
||||
// Historical note: Opera (pre 9.5) used to negate the detail value.
|
||||
} else { // Gecko
|
||||
// Gecko returns multiple of 3 (representing the number of lines scrolled)
|
||||
detail = be.detail;
|
||||
|
||||
// Gecko sometimes returns really big values if the user changes settings to
|
||||
// scroll a whole page per scroll
|
||||
if (detail > 100) {
|
||||
detail = 3;
|
||||
} else if (detail < -100) {
|
||||
detail = -3;
|
||||
}
|
||||
|
||||
// Firefox 3.1 adds an axis field to the event to indicate direction of
|
||||
// scroll. See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
|
||||
if (goog.isDef(be.axis) && be.axis === be.HORIZONTAL_AXIS) {
|
||||
deltaX = detail;
|
||||
} else {
|
||||
deltaY = detail;
|
||||
}
|
||||
}
|
||||
|
||||
if (goog.isNumber(this.maxDeltaX_)) {
|
||||
deltaX = goog.math.clamp(deltaX, -this.maxDeltaX_, this.maxDeltaX_);
|
||||
}
|
||||
if (goog.isNumber(this.maxDeltaY_)) {
|
||||
deltaY = goog.math.clamp(deltaY, -this.maxDeltaY_, this.maxDeltaY_);
|
||||
}
|
||||
// Don't clamp 'detail', since it could be ambiguous which axis it refers to
|
||||
// and because it's informally deprecated anyways.
|
||||
|
||||
// For horizontal scrolling we need to flip the value for RTL grids.
|
||||
if (this.isRtl_) {
|
||||
deltaX = -deltaX;
|
||||
}
|
||||
var newEvent = new goog.events.MouseWheelEvent(detail, be, deltaX, deltaY);
|
||||
this.dispatchEvent(newEvent);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper for scaling down a mousewheel delta by a scale factor, if appropriate.
|
||||
* @param {number} mouseWheelDelta Delta from a mouse wheel event. Expected to
|
||||
* be an integer.
|
||||
* @param {number} scaleFactor Factor to scale the delta down by. Expected to
|
||||
* be an integer.
|
||||
* @return {number} Scaled-down delta value, or the original delta if the
|
||||
* scaleFactor does not appear to be applicable.
|
||||
* @private
|
||||
*/
|
||||
goog.events.MouseWheelHandler.smartScale_ = function(mouseWheelDelta,
|
||||
scaleFactor) {
|
||||
// The basic problem here is that in Webkit on Mac and Linux, we can get two
|
||||
// very different types of mousewheel events: from continuous devices
|
||||
// (touchpads, Mighty Mouse) or non-continuous devices (normal wheel mice).
|
||||
//
|
||||
// Non-continuous devices in Webkit get their wheel deltas scaled up to
|
||||
// behave like IE. Continuous devices return much smaller unscaled values
|
||||
// (which most of the time will not be cleanly divisible by the IE scale
|
||||
// factor), so we should not try to normalize them down.
|
||||
//
|
||||
// Detailed discussion:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=29601
|
||||
// http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
|
||||
if (goog.userAgent.WEBKIT &&
|
||||
(goog.userAgent.MAC || goog.userAgent.LINUX) &&
|
||||
(mouseWheelDelta % scaleFactor) != 0) {
|
||||
return mouseWheelDelta;
|
||||
} else {
|
||||
return mouseWheelDelta / scaleFactor;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.MouseWheelHandler.prototype.disposeInternal = function() {
|
||||
goog.events.MouseWheelHandler.superClass_.disposeInternal.call(this);
|
||||
goog.events.unlistenByKey(this.listenKey_);
|
||||
this.listenKey_ = null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A base class for mouse wheel events. This is used with the
|
||||
* MouseWheelHandler.
|
||||
*
|
||||
* @param {number} detail The number of rows the user scrolled.
|
||||
* @param {Event} browserEvent Browser event object.
|
||||
* @param {number} deltaX The number of rows the user scrolled in the X
|
||||
* direction.
|
||||
* @param {number} deltaY The number of rows the user scrolled in the Y
|
||||
* direction.
|
||||
* @constructor
|
||||
* @extends {goog.events.BrowserEvent}
|
||||
*/
|
||||
goog.events.MouseWheelEvent = function(detail, browserEvent, deltaX, deltaY) {
|
||||
goog.events.BrowserEvent.call(this, browserEvent);
|
||||
|
||||
this.type = goog.events.MouseWheelHandler.EventType.MOUSEWHEEL;
|
||||
|
||||
/**
|
||||
* The number of lines the user scrolled
|
||||
* @type {number}
|
||||
* NOTE: Informally deprecated. Use deltaX and deltaY instead, they provide
|
||||
* more information.
|
||||
*/
|
||||
this.detail = detail;
|
||||
|
||||
/**
|
||||
* The number of "lines" scrolled in the X direction.
|
||||
*
|
||||
* Note that not all browsers provide enough information to distinguish
|
||||
* horizontal and vertical scroll events, so for these unsupported browsers,
|
||||
* we will always have a deltaX of 0, even if the user scrolled their mouse
|
||||
* wheel or trackpad sideways.
|
||||
*
|
||||
* Currently supported browsers are Webkit and Firefox 3.1 or later.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
this.deltaX = deltaX;
|
||||
|
||||
/**
|
||||
* The number of lines scrolled in the Y direction.
|
||||
* @type {number}
|
||||
*/
|
||||
this.deltaY = deltaY;
|
||||
};
|
||||
goog.inherits(goog.events.MouseWheelEvent, goog.events.BrowserEvent);
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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 This event handler will dispatch events when
|
||||
* {@code navigator.onLine} changes. HTML5 defines two events, online and
|
||||
* offline that is fired on the window. As of today 3 browsers support these
|
||||
* events: Firefox 3 (Gecko 1.9), Opera 9.5, and IE8. If we have any of these
|
||||
* we listen to the 'online' and 'offline' events on the current window
|
||||
* object. Otherwise we poll the navigator.onLine property to detect changes.
|
||||
*
|
||||
* Note that this class only reflects what the browser tells us and this usually
|
||||
* only reflects changes to the File -> Work Offline menu item.
|
||||
*
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
* @see ../demos/onlinehandler.html
|
||||
*/
|
||||
|
||||
// TODO(arv): We should probably implement some kind of polling service and/or
|
||||
// a poll for changes event handler that can be used to fire events when a state
|
||||
// changes.
|
||||
|
||||
goog.provide('goog.events.OnlineHandler');
|
||||
goog.provide('goog.events.OnlineHandler.EventType');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.events.BrowserFeature');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.net.NetworkStatusMonitor');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Basic object for detecting whether the online state changes.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
* @implements {goog.net.NetworkStatusMonitor}
|
||||
*/
|
||||
goog.events.OnlineHandler = function() {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* @private {goog.events.EventHandler}
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
// Some browsers do not support navigator.onLine and therefore we don't
|
||||
// bother setting up events or timers.
|
||||
if (!goog.events.BrowserFeature.HAS_NAVIGATOR_ONLINE_PROPERTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (goog.events.BrowserFeature.HAS_HTML5_NETWORK_EVENT_SUPPORT) {
|
||||
var target =
|
||||
goog.events.BrowserFeature.HTML5_NETWORK_EVENTS_FIRE_ON_BODY ?
|
||||
document.body : window;
|
||||
this.eventHandler_.listen(target,
|
||||
[goog.events.EventType.ONLINE, goog.events.EventType.OFFLINE],
|
||||
this.handleChange_);
|
||||
} else {
|
||||
this.online_ = this.isOnline();
|
||||
this.timer_ = new goog.Timer(goog.events.OnlineHandler.POLL_INTERVAL_);
|
||||
this.eventHandler_.listen(this.timer_, goog.Timer.TICK, this.handleTick_);
|
||||
this.timer_.start();
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.events.OnlineHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Enum for the events dispatched by the OnlineHandler.
|
||||
* @enum {string}
|
||||
* @deprecated Use goog.net.NetworkStatusMonitor.EventType instead.
|
||||
*/
|
||||
goog.events.OnlineHandler.EventType = goog.net.NetworkStatusMonitor.EventType;
|
||||
|
||||
|
||||
/**
|
||||
* The time to wait before checking the {@code navigator.onLine} again.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.events.OnlineHandler.POLL_INTERVAL_ = 250;
|
||||
|
||||
|
||||
/**
|
||||
* Stores the last value of the online state so we can detect if this has
|
||||
* changed.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.events.OnlineHandler.prototype.online_;
|
||||
|
||||
|
||||
/**
|
||||
* The timer object used to poll the online state.
|
||||
* @type {goog.Timer}
|
||||
* @private
|
||||
*/
|
||||
goog.events.OnlineHandler.prototype.timer_;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.OnlineHandler.prototype.isOnline = function() {
|
||||
return goog.events.BrowserFeature.HAS_NAVIGATOR_ONLINE_PROPERTY ?
|
||||
navigator.onLine : true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called every time the timer ticks to see if the state has changed and when
|
||||
* the online state changes the method handleChange_ is called.
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.events.OnlineHandler.prototype.handleTick_ = function(e) {
|
||||
var online = this.isOnline();
|
||||
if (online != this.online_) {
|
||||
this.online_ = online;
|
||||
this.handleChange_(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called when the online state changes. This dispatches the
|
||||
* {@code ONLINE} and {@code OFFLINE} events respectively.
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.events.OnlineHandler.prototype.handleChange_ = function(e) {
|
||||
var type = this.isOnline() ?
|
||||
goog.net.NetworkStatusMonitor.EventType.ONLINE :
|
||||
goog.net.NetworkStatusMonitor.EventType.OFFLINE;
|
||||
this.dispatchEvent(type);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.OnlineHandler.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
this.eventHandler_.dispose();
|
||||
this.eventHandler_ = null;
|
||||
if (this.timer_) {
|
||||
this.timer_.dispose();
|
||||
this.timer_ = null;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,515 @@
|
||||
// Copyright 2009 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 Provides a 'paste' event detector that works consistently
|
||||
* across different browsers.
|
||||
*
|
||||
* IE5, IE6, IE7, Safari3.0 and FF3.0 all fire 'paste' events on textareas.
|
||||
* FF2 doesn't. This class uses 'paste' events when they are available
|
||||
* and uses heuristics to detect the 'paste' event when they are not available.
|
||||
*
|
||||
* Known issue: will not detect paste events in FF2 if you pasted exactly the
|
||||
* same existing text.
|
||||
* Known issue: Opera + Mac doesn't work properly because of the meta key. We
|
||||
* can probably fix that. TODO(user): {@link KeyboardShortcutHandler} does not
|
||||
* work either very well with opera + mac. fix that.
|
||||
*
|
||||
* @supported IE5, IE6, IE7, Safari3.0, Chrome, FF2.0 (linux) and FF3.0 and
|
||||
* Opera (mac and windows).
|
||||
*
|
||||
* @see ../demos/pastehandler.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.events.PasteHandler');
|
||||
goog.provide('goog.events.PasteHandler.EventType');
|
||||
goog.provide('goog.events.PasteHandler.State');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.async.ConditionalDelay');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A paste event detector. Gets an {@code element} as parameter and fires
|
||||
* {@code goog.events.PasteHandler.EventType.PASTE} events when text is
|
||||
* pasted in the {@code element}. Uses heuristics to detect paste events in FF2.
|
||||
* See more details of the heuristic on {@link #handleEvent_}.
|
||||
*
|
||||
* @param {Element} element The textarea element we are listening on.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.events.PasteHandler = function(element) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* The element that you want to listen for paste events on.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = element;
|
||||
|
||||
/**
|
||||
* The last known value of the element. Kept to check if things changed. See
|
||||
* more details on {@link #handleEvent_}.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.oldValue_ = this.element_.value;
|
||||
|
||||
/**
|
||||
* Handler for events.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* The last time an event occurred on the element. Kept to check whether the
|
||||
* last event was generated by two input events or by multiple fast key events
|
||||
* that got swallowed. See more details on {@link #handleEvent_}.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.lastTime_ = goog.now();
|
||||
|
||||
if (goog.userAgent.WEBKIT ||
|
||||
goog.userAgent.IE ||
|
||||
goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9')) {
|
||||
// Most modern browsers support the paste event.
|
||||
this.eventHandler_.listen(element, goog.events.EventType.PASTE,
|
||||
this.dispatch_);
|
||||
} else {
|
||||
// But FF2 and Opera doesn't. we listen for a series of events to try to
|
||||
// find out if a paste occurred. We enumerate and cover all known ways to
|
||||
// paste text on textareas. See more details on {@link #handleEvent_}.
|
||||
var events = [
|
||||
goog.events.EventType.KEYDOWN,
|
||||
goog.events.EventType.BLUR,
|
||||
goog.events.EventType.FOCUS,
|
||||
goog.events.EventType.MOUSEOVER,
|
||||
'input'
|
||||
];
|
||||
this.eventHandler_.listen(element, events, this.handleEvent_);
|
||||
}
|
||||
|
||||
/**
|
||||
* ConditionalDelay used to poll for changes in the text element once users
|
||||
* paste text. Browsers fire paste events BEFORE the text is actually present
|
||||
* in the element.value property.
|
||||
* @type {goog.async.ConditionalDelay}
|
||||
* @private
|
||||
*/
|
||||
this.delay_ = new goog.async.ConditionalDelay(
|
||||
goog.bind(this.checkUpdatedText_, this));
|
||||
|
||||
};
|
||||
goog.inherits(goog.events.PasteHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* The types of events fired by this class.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.PasteHandler.EventType = {
|
||||
/**
|
||||
* Dispatched as soon as the paste event is detected, but before the pasted
|
||||
* text has been added to the text element we're listening to.
|
||||
*/
|
||||
PASTE: 'paste',
|
||||
|
||||
/**
|
||||
* Dispatched after detecting a change to the value of text element
|
||||
* (within 200msec of receiving the PASTE event).
|
||||
*/
|
||||
AFTER_PASTE: 'after_paste'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The mandatory delay we expect between two {@code input} events, used to
|
||||
* differentiated between non key paste events and key events.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER =
|
||||
400;
|
||||
|
||||
|
||||
/**
|
||||
* The period between each time we check whether the pasted text appears in the
|
||||
* text element or not.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_ = 50;
|
||||
|
||||
|
||||
/**
|
||||
* The maximum amount of time we want to poll for changes.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_ = 200;
|
||||
|
||||
|
||||
/**
|
||||
* The states that this class can be found, on the paste detection algorithm.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.events.PasteHandler.State = {
|
||||
INIT: 'init',
|
||||
FOCUSED: 'focused',
|
||||
TYPING: 'typing'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The initial state of the paste detection algorithm.
|
||||
* @type {goog.events.PasteHandler.State}
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.state_ =
|
||||
goog.events.PasteHandler.State.INIT;
|
||||
|
||||
|
||||
/**
|
||||
* The previous event that caused us to be on the current state.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.previousEvent_;
|
||||
|
||||
|
||||
/**
|
||||
* A logger, used to help us debug the algorithm.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.logger_ =
|
||||
goog.log.getLogger('goog.events.PasteHandler');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.events.PasteHandler.prototype.disposeInternal = function() {
|
||||
goog.events.PasteHandler.superClass_.disposeInternal.call(this);
|
||||
this.eventHandler_.dispose();
|
||||
this.eventHandler_ = null;
|
||||
this.delay_.dispose();
|
||||
this.delay_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current state of the paste detection algorithm. Used mostly for
|
||||
* testing.
|
||||
* @return {goog.events.PasteHandler.State} The current state of the class.
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.getState = function() {
|
||||
return this.state_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the event handler.
|
||||
* @return {goog.events.EventHandler} The event handler.
|
||||
* @protected
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.getEventHandler = function() {
|
||||
return this.eventHandler_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the element.value property was updated, and if so, dispatches
|
||||
* the event that let clients know that the text is available.
|
||||
* @return {boolean} Whether the polling should stop or not, based on whether
|
||||
* we found a text change or not.
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.checkUpdatedText_ = function() {
|
||||
if (this.oldValue_ == this.element_.value) {
|
||||
return false;
|
||||
}
|
||||
goog.log.info(this.logger_, 'detected textchange after paste');
|
||||
this.dispatchEvent(goog.events.PasteHandler.EventType.AFTER_PASTE);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches the paste event.
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.dispatch_ = function(e) {
|
||||
var event = new goog.events.BrowserEvent(e.getBrowserEvent());
|
||||
event.type = goog.events.PasteHandler.EventType.PASTE;
|
||||
this.dispatchEvent(event);
|
||||
|
||||
// Starts polling for updates in the element.value property so we can tell
|
||||
// when do dispatch the AFTER_PASTE event. (We do an initial check after an
|
||||
// async delay of 0 msec since some browsers update the text right away and
|
||||
// our poller will always wait one period before checking).
|
||||
goog.Timer.callOnce(function() {
|
||||
if (!this.checkUpdatedText_()) {
|
||||
this.delay_.start(
|
||||
goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_,
|
||||
goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_);
|
||||
}
|
||||
}, 0, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The main event handler which implements a state machine.
|
||||
*
|
||||
* To handle FF2, we enumerate and cover all the known ways a user can paste:
|
||||
*
|
||||
* 1) ctrl+v, shift+insert, cmd+v
|
||||
* 2) right click -> paste
|
||||
* 3) edit menu -> paste
|
||||
* 4) drag and drop
|
||||
* 5) middle click
|
||||
*
|
||||
* (1) is easy and can be detected by listening for key events and finding out
|
||||
* which keys are pressed. (2), (3), (4) and (5) do not generate a key event,
|
||||
* so we need to listen for more than that. (2-5) all generate 'input' events,
|
||||
* but so does key events. So we need to have some sort of 'how did the input
|
||||
* event was generated' history algorithm.
|
||||
*
|
||||
* (2) is an interesting case in Opera on a Mac: since Macs does not have two
|
||||
* buttons, right clicking involves pressing the CTRL key. Even more interesting
|
||||
* is the fact that opera does NOT set the e.ctrlKey bit. Instead, it sets
|
||||
* e.keyCode = 0.
|
||||
* {@link http://www.quirksmode.org/js/keys.html}
|
||||
*
|
||||
* (1) is also an interesting case in Opera on a Mac: Opera is the only browser
|
||||
* covered by this class that can detect the cmd key (FF2 can't apparently). And
|
||||
* it fires e.keyCode = 17, which is the CTRL key code.
|
||||
* {@link http://www.quirksmode.org/js/keys.html}
|
||||
*
|
||||
* NOTE(user, pbarry): There is an interesting thing about (5): on Linux, (5)
|
||||
* pastes the last thing that you highlighted, not the last thing that you
|
||||
* ctrl+c'ed. This code will still generate a {@code PASTE} event though.
|
||||
*
|
||||
* We enumerate all the possible steps a user can take to paste text and we
|
||||
* implemented the transition between the steps in a state machine. The
|
||||
* following is the design of the state machine:
|
||||
*
|
||||
* matching paths:
|
||||
*
|
||||
* (1) happens on INIT -> FOCUSED -> TYPING -> [e.ctrlKey & e.keyCode = 'v']
|
||||
* (2-3) happens on INIT -> FOCUSED -> [input event happened]
|
||||
* (4) happens on INIT -> [mouseover && text changed]
|
||||
*
|
||||
* non matching paths:
|
||||
*
|
||||
* user is typing normally
|
||||
* INIT -> FOCUS -> TYPING -> INPUT -> INIT
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.handleEvent_ = function(e) {
|
||||
// transition between states happen at each browser event, and depend on the
|
||||
// current state, the event that led to this state, and the event input.
|
||||
switch (this.state_) {
|
||||
case goog.events.PasteHandler.State.INIT: {
|
||||
this.handleUnderInit_(e);
|
||||
break;
|
||||
}
|
||||
case goog.events.PasteHandler.State.FOCUSED: {
|
||||
this.handleUnderFocused_(e);
|
||||
break;
|
||||
}
|
||||
case goog.events.PasteHandler.State.TYPING: {
|
||||
this.handleUnderTyping_(e);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
goog.log.error(this.logger_, 'invalid ' + this.state_ + ' state');
|
||||
}
|
||||
}
|
||||
this.lastTime_ = goog.now();
|
||||
this.oldValue_ = this.element_.value;
|
||||
goog.log.info(this.logger_, e.type + ' -> ' + this.state_);
|
||||
this.previousEvent_ = e.type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* {@code goog.events.PasteHandler.EventType.INIT} is the first initial state
|
||||
* the textarea is found. You can only leave this state by setting focus on the
|
||||
* textarea, which is how users will input text. You can also paste things using
|
||||
* drag and drop, which will not generate a {@code goog.events.EventType.FOCUS}
|
||||
* event, but will generate a {@code goog.events.EventType.MOUSEOVER}.
|
||||
*
|
||||
* For browsers that support the 'paste' event, we match it and stay on the same
|
||||
* state.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.handleUnderInit_ = function(e) {
|
||||
switch (e.type) {
|
||||
case goog.events.EventType.BLUR: {
|
||||
this.state_ = goog.events.PasteHandler.State.INIT;
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.FOCUS: {
|
||||
this.state_ = goog.events.PasteHandler.State.FOCUSED;
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.MOUSEOVER: {
|
||||
this.state_ = goog.events.PasteHandler.State.INIT;
|
||||
if (this.element_.value != this.oldValue_) {
|
||||
goog.log.info(this.logger_, 'paste by dragdrop while on init!');
|
||||
this.dispatch_(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
goog.log.error(this.logger_,
|
||||
'unexpected event ' + e.type + 'during init');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* {@code goog.events.PasteHandler.EventType.FOCUSED} is typically the second
|
||||
* state the textarea will be, which is followed by the {@code INIT} state. On
|
||||
* this state, users can paste in three different ways: edit -> paste,
|
||||
* right click -> paste and drag and drop.
|
||||
*
|
||||
* The latter will generate a {@code goog.events.EventType.MOUSEOVER} event,
|
||||
* which we match by making sure the textarea text changed. The first two will
|
||||
* generate an 'input', which we match by making sure it was NOT generated by a
|
||||
* key event (which also generates an 'input' event).
|
||||
*
|
||||
* Unfortunately, in Firefox, if you type fast, some KEYDOWN events are
|
||||
* swallowed but an INPUT event may still happen. That means we need to
|
||||
* differentiate between two consecutive INPUT events being generated either by
|
||||
* swallowed key events OR by a valid edit -> paste -> edit -> paste action. We
|
||||
* do this by checking a minimum time between the two events. This heuristic
|
||||
* seems to work well, but it is obviously a heuristic :).
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.handleUnderFocused_ = function(e) {
|
||||
switch (e.type) {
|
||||
case 'input' : {
|
||||
// there are two different events that happen in practice that involves
|
||||
// consecutive 'input' events. we use a heuristic to differentiate
|
||||
// between the one that generates a valid paste action and the one that
|
||||
// doesn't.
|
||||
// @see testTypingReallyFastDispatchesTwoInputEventsBeforeTheKEYDOWNEvent
|
||||
// and
|
||||
// @see testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents
|
||||
// Notice that an 'input' event may be also triggered by a 'middle click'
|
||||
// paste event, which is described in
|
||||
// @see testMiddleClickWithoutFocusTriggersPasteEvent
|
||||
var minimumMilisecondsBetweenInputEvents = this.lastTime_ +
|
||||
goog.events.PasteHandler.
|
||||
MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER;
|
||||
if (goog.now() > minimumMilisecondsBetweenInputEvents ||
|
||||
this.previousEvent_ == goog.events.EventType.FOCUS) {
|
||||
goog.log.info(this.logger_, 'paste by textchange while focused!');
|
||||
this.dispatch_(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.BLUR: {
|
||||
this.state_ = goog.events.PasteHandler.State.INIT;
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.KEYDOWN: {
|
||||
goog.log.info(this.logger_, 'key down ... looking for ctrl+v');
|
||||
// Opera + MAC does not set e.ctrlKey. Instead, it gives me e.keyCode = 0.
|
||||
// http://www.quirksmode.org/js/keys.html
|
||||
if (goog.userAgent.MAC && goog.userAgent.OPERA && e.keyCode == 0 ||
|
||||
goog.userAgent.MAC && goog.userAgent.OPERA && e.keyCode == 17) {
|
||||
break;
|
||||
}
|
||||
this.state_ = goog.events.PasteHandler.State.TYPING;
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.MOUSEOVER: {
|
||||
if (this.element_.value != this.oldValue_) {
|
||||
goog.log.info(this.logger_, 'paste by dragdrop while focused!');
|
||||
this.dispatch_(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
goog.log.error(this.logger_,
|
||||
'unexpected event ' + e.type + ' during focused');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* {@code goog.events.PasteHandler.EventType.TYPING} is the third state
|
||||
* this class can be. It exists because each KEYPRESS event will ALSO generate
|
||||
* an INPUT event (because the textarea value changes), and we need to
|
||||
* differentiate between an INPUT event generated by a key event and an INPUT
|
||||
* event generated by edit -> paste actions.
|
||||
*
|
||||
* This is the state that we match the ctrl+v pattern.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} e The underlying browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.events.PasteHandler.prototype.handleUnderTyping_ = function(e) {
|
||||
switch (e.type) {
|
||||
case 'input' : {
|
||||
this.state_ = goog.events.PasteHandler.State.FOCUSED;
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.BLUR: {
|
||||
this.state_ = goog.events.PasteHandler.State.INIT;
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.KEYDOWN: {
|
||||
if (e.ctrlKey && e.keyCode == goog.events.KeyCodes.V ||
|
||||
e.shiftKey && e.keyCode == goog.events.KeyCodes.INSERT ||
|
||||
e.metaKey && e.keyCode == goog.events.KeyCodes.V) {
|
||||
goog.log.info(this.logger_, 'paste by ctrl+v while keypressed!');
|
||||
this.dispatch_(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case goog.events.EventType.MOUSEOVER: {
|
||||
if (this.element_.value != this.oldValue_) {
|
||||
goog.log.info(this.logger_, 'paste by dragdrop while keypressed!');
|
||||
this.dispatch_(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
goog.log.error(this.logger_,
|
||||
'unexpected event ' + e.type + ' during keypressed');
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user