Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Google Closure: The Evil Parts

17 views
Skip to first unread message

David Mark

unread,
Nov 18, 2009, 7:45:26 PM11/18/09
to
Event parts.

Lot of them. Spread out over several files. Check in comments vary
from "Good luck little buddy" to "Checking in to the SVN repository
for Closure". Cargo cult for sure (always add comments, no mater how
absurd and wasteful).

// 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.

// Copyright 2005 Google Inc. All Rights Reserved.

Another time capsule of misdeeds and misconceptions. :)

/**
* @fileoverview Class to create objects which want to handle multiple
events
* and have their listeners easily cleaned up via a dispose method.

Class?! So the authors don't speak the language. How do they
communicate ideas?

*
* Example:
* <pre>
* function Something() {
* goog.events.EventHandler.call(this);
*
* ... set up object ...
*
* // Add event listeners
* this.listen(this.starEl, 'click', this.handleStar);
* this.listen(this.headerEl, 'click', this.expand);
* this.listen(this.collapseEl, 'click', this.collapse);
* this.listen(this.infoEl, 'mouseover', this.showHover);
* this.listen(this.infoEl, 'mouseout', this.hideHover);
* }
* goog.inherits(Something, goog.events.EventHandler);
*
* Something.prototype.disposeInternal = function() {
* Something.superClass_.disposeInternal.call(this);
* 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>
*
*/

And this is evidence that they create memory leaks in IE, but don't
really understand why.

goog.provide('goog.events.EventHandler');

goog.require('goog.Disposable');
goog.require('goog.events');
goog.require('goog.events.EventWrapper');
goog.require('goog.object');
goog.require('goog.structs.SimplePool');


/**
* 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. It is
optimized to
* use less objects if only one event is being listened to, but if
that's the
* case, it may not be worth using the EventHandler anyway.
* @param {Object} opt_handler Object in whose scope to call the
listeners.


This has _nothing_ to do with scope.


* @constructor
* @extends {goog.Disposable}
*/
goog.events.EventHandler = function(opt_handler) {
this.handler_ = opt_handler;
};
goog.inherits(goog.events.EventHandler, goog.Disposable);


/**
* Initial count for the keyPool_
* @type {number}
*/
goog.events.EventHandler.KEY_POOL_INITIAL_COUNT = 0;


/**
* Max count for the keyPool_
* @type {number}
*/
goog.events.EventHandler.KEY_POOL_MAX_COUNT = 100;


Sounds arbitrary.


/**
* SimplePool to cache the key object. This was implemented to make
IE6
* performance better and removed an object allocation in the listen
method
* when in steady state.
* @type {goog.structs.SimplePool}
* @private
*/
goog.events.EventHandler.keyPool_ = new goog.structs.SimplePool(
goog.events.EventHandler.KEY_POOL_INITIAL_COUNT,
goog.events.EventHandler.KEY_POOL_MAX_COUNT);


/**
* Keys for events that are being listened to. This is used once there
are more
* than one event to listen to. If there is only one event to listen
to, key_
* is used.
* @type {Object?}
* @private
*/
goog.events.EventHandler.keys_ = null;


/**
* Keys for event that is being listened to if only one event is being
listened
* to. This is a performance optimization to avoid creating an extra
object
* if not necessary.
* @type {string?}
* @private
*/
goog.events.EventHandler.key_ = null;


/**
* Listen to an event on a DOM node or EventTarget.


Tangled.


If the function is ommitted
* then the EventHandler's handleEvent method will be used.
* @param {goog.events.EventTarget|EventTarget} 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)) {

Oops, indicator. Check for the string, not the array.

for (var i = 0; i < type.length; i++) {
this.listen(src, type[i], opt_fn, opt_capture, opt_handler);
}
} else {
var key = (/** @type {number} */
goog.events.listen(src, type, opt_fn || this,
opt_capture || false,
opt_handler || this.handler_ || this));
this.recordListenerKey_(key);
}

return this;
};


/**
* Listen to an event on a DOM node or EventTarget. If the function
is ommitted
* 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.EventTarget|EventTarget} 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.


Capture?!


* @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)) {


Same mistakes over and over.


for (var i = 0; i < type.length; i++) {
this.listenOnce(src, type[i], opt_fn, opt_capture, opt_handler);
}
} else {
var key = (/** @type {number} */
goog.events.listenOnce(src, type, opt_fn || this,
opt_capture || false,
opt_handler || this.handler_ || this));
this.recordListenerKey_(key);
}

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);
return this;
};


/**
* Record the key returned for the listener so that it can be user
later
* to remove the listener.
* @param {number} key Unique key for the listener.
* @private
*/
goog.events.EventHandler.prototype.recordListenerKey_ = function(key)
{
if (this.keys_) {
// already have multiple keys
this.keys_[key] = true;
} else if (this.key_) {
// going from one key to multiple - must now use object as map
this.keys_ = goog.events.EventHandler.keyPool_.getObject();
this.keys_[this.key_] = true;
this.key_ = null;
this.keys_[key] = true;
} else {
// first key - can use single key
this.key_ = key;
}
};


/**
* Unlistens on an event.


Unlistens? Apparently they don't speak English either.


* @param {goog.events.EventTarget|EventTarget} 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) {

Capture?!


if (this.key_ || this.keys_) {
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 || false, opt_handler || this.handler_ || this);

if (listener) {
var key = listener.key;
goog.events.unlistenByKey(key);

if (this.keys_) {
goog.object.remove(this.keys_, key);
} else if (this.key_ == key) {
this.key_ = null;
}
}
}
}

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);
return this;
};


/**
* Unlistens to all events.
*/
goog.events.EventHandler.prototype.removeAll = function() {
if (this.keys_) {
for (var key in this.keys_) {
goog.events.unlistenByKey((/** @type {number} */ key));
// Clean the keys before returning object to the pool.
delete this.keys_[key];
}
goog.events.EventHandler.keyPool_.releaseObject(this.keys_);
this.keys_ = null;

} else if (this.key_) {
goog.events.unlistenByKey(this.key_);
}
};


/**
* Disposes of this EventHandler and remove all listeners that it
registered.
*/
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');
};

Whatever.

// 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.

// Copyright 2005 Google Inc. All Rights Reserved.

/**
* @fileoverview Implementation of EventTarget as defined by W3C DOM
2/3.
*
* @see ../demos/eventtarget.html
*/

/**
* Namespace for events
*/
goog.provide('goog.events.EventTarget');

goog.require('goog.Disposable');
goog.require('goog.events');


/**
* This implements the EventTarget interface as defined by W3C DOM
2/3. The
* main difference from the spec is that the this does not know about
event
* propagation and therefore the flag whether to use bubbling or
capturing is
* not used.
*
* Another difference is that event objects do not really have to
implement
* the Event interface. An object is treated as an event object if it
has a
* type property.
*
* It also allows you to pass a string instead of an event object and
in that
* case an event like object is created with the type set to the
string value.
*
* Unless propagation is stopped, events dispatched by an EventTarget
bubble
* to its parent event target, returned by <code>getParentEventTarget</
code>.
* To set the parent event target, call <code>setParentEventTarget</
code> or
* override <code>getParentEventTarget</code> in a subclass.
Subclasses that
* don't support changing the parent event target should override the
setter
* to throw an error.
*
* Example usage:
* <pre>
* var et = new goog.events.EventTarget;
* function f(e) {
* alert("Type: " + e.type + "\nTarget: " + e.target);
* }
* et.addEventListener("foo", f);
* ...
* et.dispatchEvent({type: "foo"}); // will call f
* // or et.dispatchEvent("foo");
* ...
* et.removeEventListener("foo", f);
*
* // You can also use the EventHandler interface:
* var eh = {
* handleEvent: function(e) {
* ...
* }
* };
* et.addEventListener("bar", eh);
* </pre>
*
* @constructor
* @extends {goog.Disposable}
*/
goog.events.EventTarget = function() {
goog.Disposable.call(this);
};
goog.inherits(goog.events.EventTarget, goog.Disposable);


/**
* Used to tell if an event is a real event in goog.events.listen() so
we don't
* get listen() calling addEventListener() and vice-versa.
* @type {boolean}
* @private
*/
goog.events.EventTarget.prototype.customEvent_ = true;


/**
* Parent event target, used during event bubbling.
* @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.
*/
goog.events.EventTarget.prototype.getParentEventTarget = function() {
return this.parentEventTarget_;
};


/**
* Sets the parent of this event target to use for bubbling.
*
* @param {goog.events.EventTarget?} parent Parent EventTarget (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.
*
* @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.
* @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);
};


/**
* 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 {string|Object|goog.events.Event} e Event object.
* @return {boolean} If anyone called preventDefault on the event
object (or
* if any of the handlers returns false this will also return
false.
*/
goog.events.EventTarget.prototype.dispatchEvent = function(e) {
return goog.events.dispatchEvent(this, e);
};


/**
* Unattach 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, it should be something like this:
* <pre>
* MyClass.prototype.disposeInternal = function() {
* MyClass.superClass_.disposeInternal.call(this);
* // Dispose logic for MyClass
* };
* </pre>
* @protected
*/
goog.events.EventTarget.prototype.disposeInternal = function() {
goog.events.EventTarget.superClass_.disposeInternal.call(this);
goog.events.removeAll(this);
this.parentEventTarget_ = null;
};

// 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.

// Copyright 2005 Google Inc. All Rights Reserved.

/**
* @fileoverview Event Manager.
*
* Provides an abstracted interface to the browsers' event
* systems. This uses an indirect lookup of listener functions to
avoid circular
* references between DOM (in IE) or XPCOM (in Mozilla) objects which
leak
* memory. This makes it easier to write OO Javascript/DOM code.


I doubt it.


*
* It simulates capture & bubble in Internet Explorer.


Again.


*
* The listeners will also automagically have their event objects
patched, so
* your handlers don't need to worry about the browser.
*
* Example usage:
* <pre>
* goog.events.listen(myNode, 'click', function(e) { alert('woo') });
* goog.events.listen(myNode, 'mouseover', mouseHandler, true);
* goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);
* goog.events.removeAll(myNode);
* goog.events.removeAll();
* </pre>
*
* in IE and event object
patching]
*
* @supported IE6+, FF1.5+, WebKit, Opera.
* @see ../demos/events.html
* @see ../demos/event-propagation.html
* @see ../demos/stopevent.html
*/


// This uses 3 lookup tables/trees.
// listenerTree_ is a tree of type -> capture -> src hash code ->
[Listener]
// listeners_ is a map of key -> [Listener]
//
// The key is a field of the Listener. The Listener class also has the
type,
// capture and the src so one can always trace back in the tree
//
// sources_: src hc -> [Listener]


goog.provide('goog.events');
goog.provide('goog.events.EventType');

goog.require('goog.array');
goog.require('goog.debug.errorHandlerWeakDep');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.Event');
goog.require('goog.events.EventWrapper');
goog.require('goog.events.pools');
goog.require('goog.object');
goog.require('goog.userAgent');


Note that last one. I think "goof" would be more appropriate a name
for this "space".


/**
* Container for storing event listeners and their proxies
* @private
* @type {Object.<goog.events.Listener>}
*/
goog.events.listeners_ = {};


/**
* The root of the listener tree
* @private
* @type {Object}
*/
goog.events.listenerTree_ = {};


/**
* Lookup for mapping source hash codes to listeners
* @private
* @type {Object}
*/
goog.events.sources_ = {};


/**
* String used to prepend to IE event types. Not a constant so that
it is not
* inlined.
* @type {string}
* @private
*/
goog.events.onString_ = 'on';


/**
* Map of computed on strings for IE event types. Caching this removes
an extra
* object allocation in goog.events.listen which improves IE6
performance.
* @type {Object}
* @private
*/
goog.events.onStringMap_ = {};

/**
* Separator used to split up the various parts of an event key, to
help avoid
* the possibilities of collisions.
* @type {string}
* @private
*/
goog.events.keySeparator_ = '_';


/**
* Adds an event listener for a specific event on a DOM Node or an
object that
* has implemented {@link goog.events.EventTarget}. A listener can
only be
* added once to an object and if it is added again the key for the
listener
* is returned.
*
* @param {EventTarget|goog.events.EventTarget} src The node to listen
to
* events on.
* @param {string|Array.<string>} type Event type or array of event
types.
* @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 {number?} Unique key for the listener.
*/
goog.events.listen = function(src, type, listener, opt_capt,
opt_handler) {
if (!type) {
throw Error('Invalid event type');

Waste of time and space.


} else if (goog.isArray(type)) {


Notice how the design aims for the path of most resistance every
time. ;)


for (var i = 0; i < type.length; i++) {


Inefficient. Rookie mistake.


goog.events.listen(src, type[i], listener, opt_capt,
opt_handler);
}
return null;
} else {
var capture = !!opt_capt;
var map = goog.events.listenerTree_;

if (!(type in map)) {
map[type] = goog.events.pools.getObject();
}
map = map[type];

if (!(capture in map)) {


!map[capture]


map[capture] = goog.events.pools.getObject();
map.count_++;
}
map = map[capture];

var srcHashCode = goog.getHashCode(src);
var listenerArray, listenerObj;

// The remaining_ property is used to be able to short circuit the
iteration
// of the event listeners.
//
// Increment the remaining event listeners to call even if this
event might
// already have been fired. At this point we do not know if the
event has
// been fired and it is too expensive to find out. By incrementing
it we are
// guaranteed that we will not skip any event listeners.
map.remaining_++;

// Do not use srcHashCode in map here since that will cast the
number to a
// string which will allocate one string object.
if (!map[srcHashCode]) {
listenerArray = map[srcHashCode] = goog.events.pools.getArray();
map.count_++;
} else {
listenerArray = map[srcHashCode];
// Ensure that the listeners do not already contain the current
listener
for (var i = 0; i < listenerArray.length; i++) {
listenerObj = listenerArray[i];
if (listenerObj.listener == listener &&
listenerObj.handler == opt_handler) {

// If this listener has been removed we should not return
its key. It
// is OK that we create new listenerObj below since the
removed one
// will be cleaned up later.
if (listenerObj.removed) {
break;
}

// We already have this listener. Return its key.
return listenerArray[i].key;
}
}
}

var proxy = goog.events.pools.getProxy();
proxy.src = src;
listenerObj = goog.events.pools.getListener();
listenerObj.init(listener, proxy, src, type, capture,
opt_handler);
var key = listenerObj.key;
proxy.key = key;

listenerArray.push(listenerObj);
goog.events.listeners_[key] = listenerObj;

if (!goog.events.sources_[srcHashCode]) {
goog.events.sources_[srcHashCode] = goog.events.pools.getArray
();
}
goog.events.sources_[srcHashCode].push(listenerObj);


// Attach the proxy through the browser's API
if (src.addEventListener) {
if (src == goog.global || !src.customEvent_) {
src.addEventListener(type, proxy, capture);
}
} else {
// The else above used to be else if (src.attachEvent) and then
there was
// another else statement that threw an exception warning the
developer
// they made a mistake. This resulted in an extra object
allocation in IE6
// due to a wrapper object that had to be implemented around the
element
// and so was removed.
src.attachEvent(goog.events.getOnString_(type), proxy);
}

return key;
}
};


All of that madness, just to attach/detach listeners? And who would
want to try their simulated capture and bubbling?


/**
* Adds an event listener for a specific event on a DomNode or an
object that
* has implemented {@link goog.events.EventTarget}. After the event
has fired
* the event listener is removed from the target.
*
* @param {EventTarget|goog.events.EventTarget} src The node to listen
to
* events on.
* @param {string|Array.<string>} type Event type or array of event
types.
* @param {Function|Object} listener Callback method.
* @param {boolean} opt_capt Fire in capture phase?.
* @param {Object} opt_handler Element in whose scope to call the
listener.
* @return {number?} Unique key for the listener.
*/
goog.events.listenOnce = function(src, type, listener, opt_capt,
opt_handler) {
if (goog.isArray(type)) {


Again. It's like they are purposely trying to do everything in the
least reliable way possible. Check for a string, not an array (former
is 100%, latter is not).


for (var i = 0; i < type.length; i++) {
goog.events.listenOnce(src, type[i], listener, opt_capt,
opt_handler);
}
return null;
}

var key = goog.events.listen(src, type, listener, opt_capt,
opt_handler);
var listenerObj = goog.events.listeners_[key];
listenerObj.callOnce = true;
return key;
};


/**
* 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.
*/
goog.events.listenWithWrapper = function(src, wrapper, listener,
opt_capt,
opt_handler) {
wrapper.listen(src, listener, opt_capt, opt_handler);
};


/**
* Removes an event listener which was added with listen().
*
* @param {EventTarget|goog.events.EventTarget} src The target to stop
* listening to events on.
* @param {string|Array.<string>} type The name of the event without
the 'on'
* prefix.
* @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 {boolean?} indicating whether the listener was there to
remove.
*/
goog.events.unlisten = function(src, type, listener, opt_capt,
opt_handler) {
if (goog.isArray(type)) {

Again.

for (var i = 0; i < type.length; i++) {
goog.events.unlisten(src, type[i], listener, opt_capt,
opt_handler);
}
return null;
}

var capture = !!opt_capt;

var listenerArray = goog.events.getListeners_(src, type, capture);
if (!listenerArray) {
return false;
}

for (var i = 0; i < listenerArray.length; i++) {
if (listenerArray[i].listener == listener &&
listenerArray[i].capture == capture &&
listenerArray[i].handler == opt_handler) {
return goog.events.unlistenByKey(listenerArray[i].key);
}
}

return false;
};


/**
* Removes an event listener which was added with listen() by the key
* returned by listen().
*
* @param {number?} key The key returned by listen() for this event
listener.
* @return {boolean} indicating whether the listener was there to
remove.
*/
goog.events.unlistenByKey = function(key) {
// Do not use key in listeners here since that will cast the number
to a
// string which will allocate one string object.
if (!goog.events.listeners_[key]) {
return false;
}
var listener = goog.events.listeners_[key];

if (listener.removed) {
return false;
}

var src = listener.src;
var type = listener.type;
var proxy = listener.proxy;
var capture = listener.capture;

if (src.removeEventListener) {
// EventTarget calls unlisten so we need to ensure that the source
is not
// an event target to prevent re-entry.
// TODO: What is this goog.global for? Why would anyone listen to
// events on the [[Global]] object? Is it supposed to be window?
Why would
// we not want to allow removing event listeners on the window?
if (src == goog.global || !src.customEvent_) {
src.removeEventListener(type, proxy, capture);
}
} else if (src.detachEvent) {
src.detachEvent(goog.events.getOnString_(type), proxy);
}

var srcHashCode = goog.getHashCode(src);
var listenerArray = goog.events.listenerTree_[type][capture]
[srcHashCode];

// In a perfect implementation we would decrement the remaining_
field here
// but then we would need to know if the listener has already been
fired or
// not. We therefore skip doing this and in this uncommon case the
entire
// ancestor chain will need to be traversed as before.

// Remove from sources_
if (goog.events.sources_[srcHashCode]) {
var sourcesArray = goog.events.sources_[srcHashCode];
goog.array.remove(sourcesArray, listener);
if (sourcesArray.length == 0) {
delete goog.events.sources_[srcHashCode];
}
}

listener.removed = true;
listenerArray.needsCleanup_ = true;
goog.events.cleanUp_(type, capture, srcHashCode, listenerArray);

delete goog.events.listeners_[key];

return true;
};


/**
* 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.
*/
goog.events.unlistenWithWrapper = function(src, wrapper, listener,
opt_capt,
opt_handler) {
wrapper.unlisten(src, listener, opt_capt, opt_handler);
};


/**
* Cleans up the listener array as well as the listener tree
* @param {string} type The type of the event.
* @param {boolean} capture Whether to clean up capture phase
listeners instead
* bubble phase listeners.
* @param {number} srcHashCode The hash code of the source.
* @param {Array.<goog.events.Listener>} listenerArray The array being
cleaned.
* @private
*/
goog.events.cleanUp_ = function(type, capture, srcHashCode,
listenerArray) {
// The listener array gets locked during the dispatch phase so that
removals
// of listeners during this phase does not screw up the indeces.
This method
// is called after we have removed a listener as well as after the
dispatch
// phase in case any listeners were removed.
if (!listenerArray.locked_) { // catches both 0 and not set
if (listenerArray.needsCleanup_) {
// Loop over the listener array and remove listeners that have
removed set
// to true. This could have been done with filter or something
similar but
// we want to change the array in place and we want to minimize
// allocations. Adding a listener during this phase adds to the
end of the
// array so that works fine as long as the length is rechecked
every in
// iteration.
for (var oldIndex = 0, newIndex = 0;
oldIndex < listenerArray.length;
oldIndex++) {
if (listenerArray[oldIndex].removed) {
var proxy = listenerArray[oldIndex].proxy;
proxy.src = null;
goog.events.pools.releaseProxy(proxy);
goog.events.pools.releaseListener(listenerArray[oldIndex]);
continue;
}
if (oldIndex != newIndex) {
listenerArray[newIndex] = listenerArray[oldIndex];
}
newIndex++;
}
listenerArray.length = newIndex;

listenerArray.needsCleanup_ = false;

// In case the length is now zero we release the object.
if (newIndex == 0) {
goog.events.pools.releaseArray(listenerArray);
delete goog.events.listenerTree_[type][capture][srcHashCode];
goog.events.listenerTree_[type][capture].count_--;

if (goog.events.listenerTree_[type][capture].count_ == 0) {
goog.events.pools.releaseObject(
goog.events.listenerTree_[type][capture]);
delete goog.events.listenerTree_[type][capture];
goog.events.listenerTree_[type].count_--;
}

if (goog.events.listenerTree_[type].count_ == 0) {
goog.events.pools.releaseObject(goog.events.listenerTree_
[type]);
delete goog.events.listenerTree_[type];
}
}

}
}
};


/**
* Removes all listeners from an object, if no object is specified it
will
* remove all listeners that have been registered. You can also
optionally
* remove listeners of a particular type or capture phase.
*
* @param {Object} opt_obj Object to remove listeners from.
* @param {string} opt_type Type of event to, default is all types.
* @param {boolean} opt_capt Whether to remove the listeners from the
capture or
* bubble phase. If unspecified, will remove both.
* @return {number} Number of listeners removed.
*/
goog.events.removeAll = function(opt_obj, opt_type, opt_capt) {
var count = 0;

var noObj = opt_obj == null;
var noType = opt_type == null;
var noCapt = opt_capt == null;
opt_capt = !!opt_capt;

if (!noObj) {
var srcHashCode = goog.getHashCode(/** @type {Object} */
(opt_obj));
if (goog.events.sources_[srcHashCode]) {
var sourcesArray = goog.events.sources_[srcHashCode];
for (var i = sourcesArray.length - 1; i >= 0; i--) {
var listener = sourcesArray[i];
if ((noType || opt_type == listener.type) &&
(noCapt || opt_capt == listener.capture)) {
goog.events.unlistenByKey(listener.key);
count++;
}
}
}
} else {
// Loop over the sources_ map instead of over the listeners_ since
it is
// smaller which results in fewer allocations.
goog.object.forEach(goog.events.sources_, function(listeners) {
for (var i = listeners.length - 1; i >= 0; i--) {
var listener = listeners[i];
if ((noType || opt_type == listener.type) &&
(noCapt || opt_capt == listener.capture)) {
goog.events.unlistenByKey(listener.key);
count++;
}
}
});
}

return count;
};


/**
* Gets the listeners for a given object, type and capture phase.
*
* @param {Object} obj Object to get listeners for.
* @param {string} type Event type.
* @param {boolean} capture Capture phase?.
* @return {Array.<goog.events.Listener>} Array of listener objects.
*/
goog.events.getListeners = function(obj, type, capture) {
return goog.events.getListeners_(obj, type, capture) || [];
};


/**
* Gets the listeners for a given object, type and capture phase.
*
* @param {Object} obj Object to get listeners for.
* @param {string?} type Event type.
* @param {boolean} capture Capture phase?.
* @return {Array.<goog.events.Listener>?} Array of listener objects.
* Returns null if object has no lsiteners of that type.
* @private
*/
goog.events.getListeners_ = function(obj, type, capture) {
var map = goog.events.listenerTree_;
if (type in map) {
map = map[type];
if (capture in map) {
map = map[capture];
var objHashCode = goog.getHashCode(obj);
if (map[objHashCode]) {
return map[objHashCode];
}
}
}

return null;
};


/**
* Gets the goog.events.Listener for the event or null if no such
listener is
* in use.
*
* @param {EventTarget|goog.events.EventTarget} src The node to stop
* listening to events on.
* @param {string?} type The name of the event without the 'on'
prefix.
* @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.Listener?} the found listener or null if not
found.
*/
goog.events.getListener = function(src, type, listener, opt_capt,
opt_handler) {
var capture = !!opt_capt;
var listenerArray = goog.events.getListeners_(src, type, capture);
if (listenerArray) {
for (var i = 0; i < listenerArray.length; i++) {
if (listenerArray[i].listener == listener &&
listenerArray[i].capture == capture &&
listenerArray[i].handler == opt_handler) {
// We already have this listener. Return its key.
return listenerArray[i];
}
}
}
return null;
};


How many methods does this interface have? They all look like initial
efforts to me.


/**
* Returns whether an event target has 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 {EventTarget|goog.events.EventTarget} obj Target to get
listeners for.
* @param {string} opt_type Event type.
* @param {boolean} opt_capture Whether to check for capture or bubble-
phase
* listeners.
* @return {boolean} Whether an event target has one or more listeners
matching
* the requested type and/or capture phase.
*/
goog.events.hasListener = function(obj, opt_type, opt_capture) {
var objHashCode = goog.getHashCode(obj)
var listeners = goog.events.sources_[objHashCode];

if (listeners) {
var hasType = goog.isDef(opt_type);
var hasCapture = goog.isDef(opt_capture);

if (hasType && hasCapture) {
// Lookup in the listener tree whether the specified listener
exists.
var map = goog.events.listenerTree_[opt_type]
return !!map && !!map[opt_capture] && objHashCode in map
[opt_capture];

} else if (!(hasType || hasCapture)) {
// Simple check for whether the event target has any listeners
at all.
return true;

} else {
// Iterate through the listeners for the event target to find a
match.
return goog.array.some(listeners, function(listener) {
return (hasType && listener.type == opt_type) ||
(hasCapture && listener.capture == opt_capture);
});
}
}

return false;
};


What a mess.


/**
* Provides a nice string showing the normalized event objects public
members
* @param {Object} e Event Object.
* @return {string} String of the public members of the normalized
event object.
*/
goog.events.expose = function(e) {
var str = [];
for (var key in e) {
if (e[key] && e[key].id) {
str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');
} else {
str.push(key + ' = ' + e[key]);
}
}
return str.join('\n');
};


Not nice.


/**
* Constants for event names.
* @enum {string}
*/
// TODO: Move to its own file.


I think they've got enough files at this point.


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
// TODO: Test these. I experienced problems with DOMFocusIn, the
event
// just wasn't firing.


A little late isn't? The people who would use this will not read the
code comments.


FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',

// Forms
CHANGE: 'change',
SELECT: 'select',
SUBMIT: 'submit',

// Misc
CONTEXTMENU: 'contextmenu',
DRAGSTART: 'dragstart',
ERROR: 'error',
HASHCHANGE: 'hashchange',
HELP: 'help',
LOAD: 'load',
LOSECAPTURE: 'losecapture',
READYSTATECHANGE: 'readystatechange',
RESIZE: 'resize',
SCROLL: 'scroll',
UNLOAD: 'unload'
};


/**
* Returns a string wth on prepended to the specified type. This is
used for IE
* which expects "on" to be prepended. This function caches the string
in order
* to avoid extra allocations in steady state.
* @param {string} type Event type strng.
* @return {string} The type string with 'on' prepended.
* @private
*/
goog.events.getOnString_ = function(type) {
if (type in goog.events.onStringMap_) {
return goog.events.onStringMap_[type];
}
return goog.events.onStringMap_[type] = goog.events.onString_ +
type;
};


/**
* Fires an object's listeners of a particular type and phase
*
* @param {Object} obj Object whose listeners to call.
* @param {string} type Event type.
* @param {boolean} capture Which event phase.
* @param {Object} eventObject Event object to be passed to listener.
* @return {boolean} True if all listeners returned true else false.
*/
goog.events.fireListeners = function(obj, type, capture, eventObject)
{
var map = goog.events.listenerTree_;
if (type in map) {
map = map[type];
if (capture in map) {
return goog.events.fireListeners_(map[capture], obj, type,
capture, eventObject);
}
}
return true;
};


/**
* Fires an object's listeners of a particular type and phase.
*
* @param {Object} map Object with listeners in it.
* @param {Object} obj Object whose listeners to call.
* @param {string} type Event type.
* @param {boolean} capture Which event phase.
* @param {Object} eventObject Event object to be passed to listener.
* @return {boolean} True if all listeners returned true else false.
* @private
*/
goog.events.fireListeners_ = function(map, obj, type, capture,
eventObject) {
var retval = 1;

var objHashCode = goog.getHashCode(obj);
if (map[objHashCode]) {
map.remaining_--;
var listenerArray = map[objHashCode];

// If locked_ is not set (and if already 0) initialize it to 1.
if (!listenerArray.locked_) {
listenerArray.locked_ = 1;
} else {
listenerArray.locked_++;
}

try {
// Events added in the dispatch phase should not be dispatched
in
// the current dispatch phase. They will be included in the next
// dispatch phase though.
var length = listenerArray.length;
for (var i = 0; i < length; i++) {
var listener = listenerArray[i];
// We might not have a listener if the listener was removed.
if (listener && !listener.removed) {
retval &=
goog.events.fireListener(listener, eventObject) !==
false;
}
}
} finally {
listenerArray.locked_--;
goog.events.cleanUp_(type, capture, objHashCode, listenerArray);
}


try...finally (!) is too expansive.


}

return Boolean(retval);


Odd they weren't dealing with a boolean all along.


};


/**
* Fires a listener with a set of arguments
*
* @param {goog.events.Listener} listener The listener object to call.
* @param {Object} eventObject The event object to pass to the
listener.
* @return {boolean} Result of listener.
*/
goog.events.fireListener = function(listener, eventObject) {
var rv = listener.handleEvent(eventObject);
if (listener.callOnce) {
goog.events.unlistenByKey(listener.key);
}
return rv;
};


/**
* Gets the total number of listeners currently in the system.
* @return {number} Number of listeners.
*/
goog.events.getTotalListenerCount = function() {
return goog.object.getCount(goog.events.listeners_);
};


/**
* 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.EventTarget} src The event target.
* @param {string|Object|goog.events.Event} e Event object.
* @return {boolean} If anyone called preventDefault on the event
object (or
* if any of the handlers returns false) this will also return
false.
* If there are no handlers, or if all handlers return true, this
returns
* true.
*/
goog.events.dispatchEvent = function(src, e) {
// If accepting a string or object, create a custom event object so
that
// preventDefault and stopPropagation work with the event.


If accepting a string or object?


if (goog.isString(e)) {
e = new goog.events.Event(e, src);
} else if (!(e instanceof goog.events.Event)) {
var oldEvent = e;
e = new goog.events.Event(e.type, src);
goog.object.extend(e, oldEvent);
} else {
e.target = e.target || src;


This is clearly awful code in any language. No wonder it has sat
since 2005 (who is going to untangle such a mess?) Provides insight
into the constant unraveling effects caused by Google's attempts to
patch their apps.


}

var rv = 1, ancestors;

var type = e.type;
var map = goog.events.listenerTree_;

if (!(type in map)) {
return true;
}

map = map[type];
var hasCapture = true in map;


What do you say about a line of code like that?


var targetsMap;

if (hasCapture) {
// Build ancestors now
ancestors = [];
for (var parent = src; parent; parent = parent.getParentEventTarget
()) {
ancestors.push(parent);
}

targetsMap = map[true];
targetsMap.remaining_ = targetsMap.count_;

// Call capture listeners
for (var i = ancestors.length - 1;
!e.propagationStopped_ && i >= 0 && targetsMap.remaining_;
i--) {
e.currentTarget = ancestors[i];
rv &= goog.events.fireListeners_(targetsMap, ancestors[i],
e.type,
true, e) &&
e.returnValue_ != false;
}
}

var hasBubble = false in map;


Again.


if (hasBubble) {
targetsMap = map[false];
targetsMap.remaining_ = targetsMap.count_;

if (hasCapture) { // We have the ancestors.

// Call bubble listeners
for (var i = 0; !e.propagationStopped_ && i < ancestors.length
&&
targetsMap.remaining_;
i++) {
e.currentTarget = ancestors[i];
rv &= goog.events.fireListeners_(targetsMap, ancestors[i],
e.type,
false, e) &&
e.returnValue_ != false;
}
} else {
// In case we don't have capture we don't have to build up the
// ancestors array.

for (var current = src;
!e.propagationStopped_ && current && targetsMap.remaining_;
current = current.getParentEventTarget()) {
e.currentTarget = current;
rv &= goog.events.fireListeners_(targetsMap, current, e.type,
false, e) &&
e.returnValue_ != false;
}
}
}

return Boolean(rv);

And again. Why not use a boolean to begin with?

};


/**
* Installs exception protection for the browser event entry point
using the
* given error handler.
*
* @param {goog.debug.ErrorHandler} errorHandler Error handler with
which to
* protect the entry point.
* @param {boolean} opt_tracers Whether to install tracers around the
browser
* event entry point.
*/
goog.events.protectBrowserEventEntryPoint = function(
errorHandler, opt_tracers) {
goog.events.handleBrowserEvent_ = errorHandler.protectEntryPoint(
goog.events.handleBrowserEvent_, opt_tracers);
goog.events.pools.setProxyCallbackFunction
(goog.events.handleBrowserEvent_);
};


/**
* Handles an event and dispatches it to the correct listeners. This
* function is a proxy for the real listener the user specified.
*
* @param {string} key Unique key for the listener.
* @param {Event} opt_evt Optional event object that gets passed in
via the
* native event handlers.
* @return {boolean} Result of the event handler.
* @this {goog.events.EventTarget|Object} The object or Element that
* fired the event.
* @private
*/
goog.events.handleBrowserEvent_ = function(key, opt_evt) {
// If the listener isn't there it was probably removed when
processing
// another listener on the same event (e.g. the later listener is
// not managed by closure so that they are both fired under IE)


This is certainly well documented (as an exercise in futility). If
only it were in English. :)


if (!goog.events.listeners_[key]) {
return true;
}

var listener = goog.events.listeners_[key];
var type = listener.type;
var map = goog.events.listenerTree_;

if (!(type in map)) {
return true;
}
map = map[type];
var retval, targetsMap;
if (goog.userAgent.IE) {

All right, put that away sonny.

Garrett Smith

unread,
Nov 18, 2009, 9:10:43 PM11/18/09
to
David Mark wrote:
> Event parts.
>
This is a very long posting of code.

It is hard to read the code because it wraps.

Wrap code at 72 chars so that it is easier to follow.

If you want others to digest something, don't post such a long post.
Instead, focus on something smaller. You might choose a format such as:

I saw a problem [description]
This is bad because [demonstration/evidence]
Here is the relevant part [formatted code]
Recap on problem
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/

David Mark

unread,
Nov 18, 2009, 9:36:52 PM11/18/09
to
On Nov 18, 9:10 pm, Garrett Smith <dhtmlkitc...@gmail.com> wrote:
> David Mark wrote:
> > Event parts.
>
> This is a very long posting of code.

I didn't write it (the code that is).

>
> It is hard to read the code because it wraps.

I know.

>
> Wrap code at 72 chars so that it is easier to follow.

Sure, I'll get right on it. :)

>
> If you want others to digest something, don't post such a long post.

Much of the post is indigestible (the code parts).

> Instead, focus on something smaller. You might choose a format such as:
>
>    I saw a problem [description]
>    This is bad because [demonstration/evidence]
>    Here is the relevant part [formatted code]
>    Recap on problem

Of course not. There's a problem on practically every line. The
whole thing is a waste of time. That's the point.

David Mark

unread,
Nov 18, 2009, 9:42:35 PM11/18/09
to
On Nov 18, 9:10 pm, Garrett Smith <dhtmlkitc...@gmail.com> wrote:
> David Mark wrote:
> > Event parts.
>
> This is a very long posting of code.
>

[...]

And leave my subject lines alone. Twit.

Matt Kruse

unread,
Nov 19, 2009, 1:11:33 PM11/19/09
to

Your rants are unreadable, because I can't even distinguish between
their code and your complaints.

When I can figure out where your comments are, they read like a script
for Statler and Waldorf, the two muppets in the theater balcony that
insult and complain about everything. Just little snide remarks thrown
out with some vague context and no supporting evidence or arguments.

Even if you have valid criticisms, they are all lost because of your
laziness in snipping, quoting, and organizing your thoughts for
comprehension.

At least your new anti-Closure crusade is a welcome change from your
anti-jQuery crusade.

Anxiously awaiting the awesome new Dojo,
Matt Kruse

David Mark

unread,
Nov 19, 2009, 4:38:38 PM11/19/09
to
On Nov 19, 1:11 pm, Matt Kruse <m...@thekrusefamily.com> wrote:
> On Nov 18, 8:36 pm, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Nov 18, 9:10 pm, Garrett Smith <dhtmlkitc...@gmail.com> wrote:
> > > Instead, focus on something smaller. You might choose a format such as:
> > >    I saw a problem [description]
> > >    This is bad because [demonstration/evidence]
> > >    Here is the relevant part [formatted code]
> > >    Recap on problem
> > Of course not.  There's a problem on practically every line.  The
> > whole thing is a waste of time.  That's the point.
>
> Your rants are unreadable, because I can't even distinguish between
> their code and your complaints.

I don't care if you can read them.

>
> When I can figure out where your comments are, they read like a script
> for Statler and Waldorf, the two muppets in the theater balcony that
> insult and complain about everything. Just little snide remarks thrown
> out with some vague context and no supporting evidence or arguments.

Who has time to re-hash the same old bullshit. We had most of these
same discussions over the last couple of years with regard to your
favorite library.

>
> Even if you have valid criticisms, they are all lost because of your
> laziness in snipping, quoting, and organizing your thoughts for
> comprehension.

Rubbish. The organization is the code. It is what it is. :)

>
> At least your new anti-Closure crusade is a welcome change from your
> anti-jQuery crusade.

You are such a waste of time.

>
> Anxiously awaiting the awesome new Dojo,

I bet you are. ;)

JR

unread,
Nov 19, 2009, 4:59:52 PM11/19/09
to

This kind of long text should be posted on a blog or website.

David Mark

unread,
Nov 19, 2009, 5:00:43 PM11/19/09
to

What a great idea! :)

David Mark

unread,
Nov 19, 2009, 5:01:40 PM11/19/09
to

Fixed subject.

JR

unread,
Nov 19, 2009, 5:19:13 PM11/19/09
to

David,
I'm not being ironic (this time :-), but I really think you should
post your important comments on your site (as you did recently in
www.cinsoft.net/attributes.html), instead of repeating them dozens of
times here in the newsgroup. For example, I find very positive when
you point out errors or inefficiencies in codes of other developers /
companies, but it is difficult to search for your relevant comments in
c.l.js because of countless flame wars so common (unfortunately) here.
Think about it.

Cheers,
JR

David Mark

unread,
Nov 19, 2009, 5:41:45 PM11/19/09
to
On Nov 19, 5:19 pm, JR <groups_j...@yahoo.com.br> wrote:
> On Nov 19, 8:00 pm, David Mark <dmark.cins...@gmail.com> wrote:
>
>
>
> > On Nov 19, 4:59 pm, JR <groups_j...@yahoo.com.br> wrote:
>
> > > On Nov 19, 12:10 am, Garrett Smith <dhtmlkitc...@gmail.com> wrote:
>
> > > > David Mark wrote:
> > > > > Event parts.
>
> > > > This is a very long posting of code.
>
> > > > It is hard to read the code because it wraps.
>
> > > > Wrap code at 72 chars so that it is easier to follow.
>
> > > > If you want others to digest something, don't post such a long post.
> > > > Instead, focus on something smaller. You might choose a format such as:
>
> > > >    I saw a problem [description]
> > > >    This is bad because [demonstration/evidence]
> > > >    Here is the relevant part [formatted code]
> > > >    Recap on problem
> > > > --
> > > > Garrett
> > > > comp.lang.javascript FAQ:http://jibbering.com/faq/
>
> > > This kind of long text should be posted on a blog or website.
>
> > What a great idea!  :)
>
> David,
> I'm not being ironic (this time :-), but I really think you should
> post your important comments on your site (as you did recently inwww.cinsoft.net/attributes.html), instead of repeating them dozens of

> times here in the newsgroup.

Believe it or not, I wasn't being (entirely) ironic either. It would
be hysterical to add GoogClosure's attribute handling as an additional
test case to that very page. Same for jQuery. But I don't have time
for that right now. I do have time to glance at their code and post a
small sample of the more obvious blunders. For some people, the glass
is always half full. :(

But realize that that project is painted into a corner (even worse
than jQuery). The whole design is tied to the browser sniffing, so if
they lose the browser sniffing (inevitable), they lose the way-cool
"compiler" that they use to cover up the fact that these scripts are
bloated beyond belief. Somehow they relate this quandary to their
mobile aspirations, which are clearly pipe dreams (slow code doesn't
fare well on those either.) ;)

> For example, I find very positive when
> you point out errors or inefficiencies in codes of other developers /
> companies, but it is difficult to search for your relevant comments in
> c.l.js because of countless flame wars so common (unfortunately) here.
> Think about it.

I have thought about it and I completely agree. The twits show up and
start changing the subject from ideas to people (e.g. I don't like
your posts, they are too hard to read). They just want attention. ;)

Garrett Smith

unread,
Nov 20, 2009, 3:09:45 AM11/20/09
to
David Mark wrote:
> On Nov 19, 5:19 pm, JR <groups_j...@yahoo.com.br> wrote:
>> On Nov 19, 8:00 pm, David Mark <dmark.cins...@gmail.com> wrote:
>>
>>
>>
>>> On Nov 19, 4:59 pm, JR <groups_j...@yahoo.com.br> wrote:
>>>> On Nov 19, 12:10 am, Garrett Smith <dhtmlkitc...@gmail.com> wrote:
>>>>> David Mark wrote:
>>>>>> Event parts.
>>>>> This is a very long posting of code.
>>>>> It is hard to read the code because it wraps.
>>>>> Wrap code at 72 chars so that it is easier to follow.
>>>>> If you want others to digest something, don't post such a long post.
>>>>> Instead, focus on something smaller. You might choose a format such as:
>>>>> I saw a problem [description]
>>>>> This is bad because [demonstration/evidence]
>>>>> Here is the relevant part [formatted code]
>>>>> Recap on problem
[snip sig]

>>>> This kind of long text should be posted on a blog or website.

Try to keep the review focused, explain things well. This has potential
to be valuable archived material if done properly. Publishing code
review here will reach many. What you

Look carefully to Cornford's recent review of Google Groups'
Function.prototype.bind strategy, using [].peek, bungling the thisArg,
converting object args to strings. All carefully and professionally
explained.

>
> But realize that that project is painted into a corner (even worse
> than jQuery). The whole design is tied to the browser sniffing, so if
> they lose the browser sniffing (inevitable), they lose the way-cool
> "compiler" that they use to cover up the fact that these scripts are
> bloated beyond belief. Somehow they relate this quandary to their
> mobile aspirations, which are clearly pipe dreams (slow code doesn't
> fare well on those either.) ;)
>

No, and particularly not with a browser such as Blackberry.

The blackberry has limited memory. In exhaustive testing, I have
witnessed the emulator freezing and barfing up
java.lang.NullPointerException (npe). The Blackberry browser is a very
light browser intended for the battery powered device. Javascript is
turned off, by default. The browser does not handle inefficient
javascript elegantly.

Mobile devices in general have much more limited CPU, much less battery
life.

Javascript that is less efficient uses phone battery faster.

Javascript will not execute as quickly on a Mobile device as it would on
a 2ghz dual processor.

The more javascript that is downloaded, the less likely it will be
cached. Indeed, this is a good reason to minify the script file to the
smallest possible size. Cached code is going to be available much
sooner than code that is downloaded. Connection speed varies and
bandwidth fees may apply.

The iphone is not a matter of light discussion.

Of the many things Apple is doing with the iPhone, none are for the
greater good; all are intended to benefit Apple. All of it preying on
consumer ignorance. In a nutshell, the Evil parts are: the app store,
choosing the carrier for the user (not allowed to use as a pure
device), and browser Touch Events (an incompatible API for "standard"
RIAs). Apple and Nokia are also (pay-to-play) sponsoring the w3c efforts
(I accidentally just typed "w3c errors", but that may have been a more
fitting term for what is going on).

I'll omit discussion of considering Apple iPhone (in contempt).

Your opinion of what is "evil" falls quite short of what I see Apple
doing, the things I have experienced in life, and of the significantly
ill-minded and dire acts known to man.

Part of my annoyance with this title is that naivete it portrays.

*sigh*

Now that I've gotten that out...

Take time to review each small part and double check how it reads in the
NG. You should want to reformat the code so that it wraps properly.
Explain the problems and the consequences carefully. Don't focus on any
small things like "bad form" or make flippant remarks.


--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/

[1]http://github.com/GarrettS/ape-javascript-library/blob/master/adhoctest/offsetTopUpdated.test

David Mark

unread,
Nov 20, 2009, 3:51:44 AM11/20/09
to

Yes and it would be nice if others did the same. There's enough ammo
there to sink it.

>
>
> > But realize that that project is painted into a corner (even worse
> > than jQuery).  The whole design is tied to the browser sniffing, so if
> > they lose the browser sniffing (inevitable), they lose the way-cool
> > "compiler" that they use to cover up the fact that these scripts are
> > bloated beyond belief.  Somehow they relate this quandary to their
> > mobile aspirations, which are clearly pipe dreams (slow code doesn't
> > fare well on those either.)  ;)
>
> No, and particularly not with a browser such as Blackberry.
>
> The blackberry has limited memory. In exhaustive testing, I have
> witnessed the emulator freezing and barfing up
> java.lang.NullPointerException (npe). The Blackberry browser is a very
> light browser intended for the battery powered device. Javascript is
> turned off, by default. The browser does not handle inefficient
> javascript elegantly.

I imagine not. Even worse than the iPhone. I see Peter Higgins has
requested a TaskSpeed profile from them. If they are smart they will
demur as it will likely make jQuery look fast in comparison (they've
always brought up the rear).

>
> Mobile devices in general have much more limited CPU, much less battery
> life.
>
> Javascript that is less efficient uses phone battery faster.

Yes, there are all sorts of future shocks in store for the average "JS
hacker". A guy posts a blog that decries this:-

for (var i = 0; i < a.length; i++) {
...
}

...which is seen throughout GoogClosure. The solution is obvious:-

for (var i = 0, j = a.length; i < j; i++) {
...
}

Helps out with node collections as well.

The guy was just trying to demonstrate the developers' lack of
proficiency with the language. There must have been a hundred
responses that mentioned micro-optimization. Most went on to assert
that such optimizations are a terrible idea, done to show off, make
code less readable, etc. And then there are the crackpots who claim
the JS language is inherently broken to the point of being unworkable
(thanks Douglas) and that's why Google and everyone else must produce
crappy scripts.

>
> Javascript will not execute as quickly on a Mobile device as it would on
> a 2ghz dual processor.

That's a good theory and observations have confirmed it.

>
> The more javascript that is downloaded, the less likely it will be
> cached.

Yes and some draw the line at 10-20K of script. Anything larger is
ignored.

> Indeed, this is a good reason to minify the script file to the
> smallest possible size.

That's the only reason I can think of to do _aggressive_
minification. I'm sure everyone can agree that comments and white
space can and should be removed. And, of course, GoogChrome is hardly
designed to optimize minification.

> Cached code is going to be available much
> sooner than code that is downloaded. Connection speed varies and
> bandwidth fees may apply.
>
> The iphone is not a matter of light discussion.

You are preaching to the choir. There's an app for that (and it ain't
written in jQuery).

>
> Of the many things Apple is doing with the iPhone, none are for the
> greater good; all are intended to benefit Apple.

Oh, well no doubt there.

> All of it preying on
> consumer ignorance.

Well, you've lost me. The subject seems to have changed abruptly.

> In a nutshell, the Evil parts are: the app store,
> choosing the carrier for the user (not allowed to use as a pure
> device), and browser Touch Events (an incompatible API for "standard"
> RIAs).

I haven't given those matters much thought.

> Apple and Nokia are also (pay-to-play) sponsoring the w3c efforts
> (I accidentally just typed "w3c errors", but that may have been a more
> fitting term for what is going on).

I don't know anything about it.

>
> I'll omit discussion of considering Apple iPhone (in contempt).

I think they are nice. Same for the new Macs.

>
> Your opinion of what is "evil" falls quite short of what I see Apple
> doing, the things I have experienced in life, and of the significantly
> ill-minded and dire acts known to man.
>
> Part of my annoyance with this title is that naivete it portrays.
>
> *sigh*

It was a silly joke. As in, oops I meant _events_.

>
> Now that I've gotten that out...
>
> Take time to review each small part and double check how it reads in the
> NG. You should want to reformat the code so that it wraps properly.

I know that. For something that large it was out of the question. If
I post smaller bits (or more likely a wrap-up with highlights), I'll
wrap them. Or maybe I'll just post it on a Web page, but that defeats
the archival purposes of doing this.

> Explain the problems and the consequences carefully. Don't focus on any
> small things like "bad form" or make flippant remarks.

See, that's the problem. I don't take orders from fellow NG posters.
I know what I'm doing.

Hans-Georg Michna

unread,
Nov 20, 2009, 10:03:54 AM11/20/09
to
On Thu, 19 Nov 2009 14:41:45 -0800 (PST), David Mark wrote:

>> > > This kind of long text should be posted on a blog or website.

>> [...] I really think you should post your important comments on


>> your site (as you did recently inwww.cinsoft.net/attributes.html),
>> instead of repeating them dozens of times here in the newsgroup.

>But realize that that project is painted into a corner (even worse


>than jQuery). The whole design is tied to the browser sniffing, so if
>they lose the browser sniffing (inevitable), they lose the way-cool
>"compiler" that they use to cover up the fact that these scripts are
>bloated beyond belief. Somehow they relate this quandary to their
>mobile aspirations, which are clearly pipe dreams (slow code doesn't
>fare well on those either.) ;)

If anybody wants a personal blog for such purposes, you can have
one on http://winhlp.com/ . There are already some JavaScript
articles on that site in the programming forum. (Type javascript
into the search field to find them quickly.)

Hans-Georg

SteveYoungTbird

unread,
Nov 20, 2009, 12:28:29 PM11/20/09
to
David Mark wrote:

> I know that. For something that large it was out of the question. If
> I post smaller bits (or more likely a wrap-up with highlights), I'll
> wrap them. Or maybe I'll just post it on a Web page, but that defeats
> the archival purposes of doing this.
>

And who maintains the archives? Isn't it those incompetent ignoramuses
working from Mountain View that you are railing against?

David Mark

unread,
Nov 20, 2009, 6:26:45 PM11/20/09
to

You seem to have missed something. For one, GG is a piece of crap
(archive interface included). For two, we're talking about JS.

Thomas 'PointedEars' Lahn

unread,
Dec 15, 2009, 5:32:35 PM12/15/09
to
Garrett Smith wrote:

> David Mark wrote:
>> Event parts.
>
> This is a very long posting of code.
>
> It is hard to read the code because it wraps.
>
> Wrap code at 72 chars so that it is easier to follow.
>
> If you want others to digest something, don't post such a long post.
> Instead, focus on something smaller. You might choose a format such as:
>
> I saw a problem [description]
> This is bad because [demonstration/evidence]
> Here is the relevant part [formatted code]
> Recap on problem

I second that, although I am fine with max. 76 characters per line, too.
David's 1874(!) lines posting was filtered out because of its length (so I
would not even read it at first), and as I used the References it is very
hard to follow because code mixes with comments about the code. I have seen
this in other (of his) postings as well.

I think it would be best, if a review of code from outside the newsgroup is
posted, that the code be prefixed with the `| ' character recommended for
third-party quotations or, if the used newsreader does not provide the means
(however all good ones do) or the code is supposed to be tested, that an
unambiguous non-whitespace marker like a line consisting of `-'s clearly
separates the source code from the intermittent comments about it. In the
comments, if a code suggestion is posted, it should probably be indented
with at least two spaces from the left margin so that it can clearly be
distinguished from the original code and the comments. I try to follow that
suggestion in my postings and I have found it to work well.


PointedEars
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)

Garrett Smith

unread,
Dec 15, 2009, 5:55:26 PM12/15/09
to
Thomas 'PointedEars' Lahn wrote:
> Garrett Smith wrote:
>
>> David Mark wrote:
>>> Event parts.
>> This is a very long posting of code.
>>
>> It is hard to read the code because it wraps.
>>
>> Wrap code at 72 chars so that it is easier to follow.
>>
>> If you want others to digest something, don't post such a long post.
>> Instead, focus on something smaller. You might choose a format such as:
>>
>> I saw a problem [description]
>> This is bad because [demonstration/evidence]
>> Here is the relevant part [formatted code]
>> Recap on problem
>
> I second that, although I am fine with max. 76 characters per line, too.


Well Matt Kruse has some issues with code reviews too (expressing his
own frustrations with being a reviewer). I suggested to Matt to write a
code review guidelines doc but he didn't bite on that one.

I think it would be great material for the FAQ. It couldn't hurt the
quality of code reviews.

Thomas 'PointedEars' Lahn

unread,
Dec 15, 2009, 7:55:07 PM12/15/09
to
Garrett Smith wrote:

You may use my suggestion -- which was considerably more detailed than
quoted here -- or whatever consensus it produces in the process.


PointedEars
--
Danny Goodman's books are out of date and teach practices that are
positively harmful for cross-browser scripting.
-- Richard Cornford, cljs, <cife6q$253$1$8300...@news.demon.co.uk> (2004)

0 new messages