// 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 2006 Google Inc. All Rights Reserved.
/**
* @fileoverview Utilities for element styles.
*
* @see ../demos/inline_block_quirks.html
* @see ../demos/inline_block_standards.html
* @see ../demos/style_viewport.html
*/
goog.provide('goog.style');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.math.Box');
goog.require('goog.math.Coordinate');
goog.require('goog.math.Rect');
goog.require('goog.math.Size');
goog.require('goog.object');
goog.require('goog.userAgent');
goog.require('goog.userAgent.product');
Seems rather a lot. What's the point in modularity if low level
modules require the kitchen sink? And goog.userAgent is actually a
"namespace?"
/**
* Sets a style value on an element.
* @param {Element} element The element to change.
* @param {string|Object} style If a string, a style name. If an
object, a hash
* of style names to style values.
* @param {string|number|boolean} opt_value If style was a string,
then this
* should be the value.
*/
goog.style.setStyle = function(element, style, opt_value) {
if (goog.isString(style)) {
They got one! :)
goog.style.setStyle_(element, opt_value, style);
} else {
goog.object.forEach(style, goog.partial(goog.style.setStyle_,
element));
Major points off for this though. Re-flow on setting each style.
}
};
/**
* Sets a style value on an element, with parameters swapped to work
with
* {@code goog.object.forEach()}.
* @param {Element} element The element to change.
* @param {string|number|boolean|undefined} value Style value.
* @param {string|number|boolean} style Style name.
* @private
*/
goog.style.setStyle_ = function(element, value, style) {
element.style[goog.style.toCamelCase(style)] = value;
};
/**
* Retrieves an explicitly-set style value of a node. This returns ''
if there
* isn't a style attribute on the element or if this style property
has not been
* explicitly set in script.
Retrieves the inline style?
*
* @param {Element} element Element to get style of.
* @param {string} style Property to get, css-style (if you have a
camel-case
* property, use element.style[style]).
* @return {string} Style value.
*/
goog.style.getStyle = function(element, style) {
return element.style[goog.style.toCamelCase(style)];
};
/**
* Retrieves a computed style value of a node, or null if the value
cannot be
* computed (which will be the case in Internet Explorer).
*
* @param {Element} element Element to get style of.
* @param {string} style Property to get (camel-case).
* @return {string?} Style value.
*/
goog.style.getComputedStyle = function(element, style) {
var doc = goog.dom.getOwnerDocument(element);
if (doc.defaultView && doc.defaultView.getComputedStyle) {
var styles = doc.defaultView.getComputedStyle(element, '');
if (styles) {
return styles[style];
}
}
return null;
};
Inefficient. Tests for method every time through.
/**
* Gets the cascaded style value of a node, or null if the value
cannot be
* computed (only Internet Explorer can do this).
*
* @param {Element} element Element to get style of.
* @param {string} style Property to get (camel-case).
* @return {string} Style value.
*/
goog.style.getCascadedStyle = function(element, style) {
return element.currentStyle ? element.currentStyle[style] : null;
};
Similar problem here.
/**
* Cross-browser pseudo get computed style. It returns the computed
style where
* available. If not available it tries the cascaded style value (IE
* currentStyle) and in worst case the inline style value. It
shouldn't be
* called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle
for
* discussion.
*
* @param {Element} element Element to get style of.
* @param {string} style Property to get (must be camelCase, not css-
style.).
* @return {string} Style value.
* @private
*/
goog.style.getStyle_ = function(element, style) {
return goog.style.getComputedStyle(element, style) ||
goog.style.getCascadedStyle(element, style) ||
element.style[style];
};
Terribly wasteful.
/**
* Retrieves the computed value of the position CSS attribute.
* @param {Element} element The element to get the position of.
* @return {string} Position value.
*/
goog.style.getComputedPosition = function(element) {
return goog.style.getStyle_(element, 'position');
};
Useless.
/**
* Retrieves the computed background color string for a given element.
The
* string returned is suitable for assigning to another element's
* background-color, but is not guaranteed to be in any particular
string
* format. Accessing the color in a numeric form may not be possible
in all
* browsers or with all input.
*
* If the background color for the element is defined as a hexadecimal
value,
* the resulting string can be parsed by goog.color.parse in all
supported
* browsers.
*
* Whether named colors like "red" or "lightblue" get translated into
a
* format which can be parsed is browser dependent. Calling this
function on
* transparent elements will return "transparent" in most browsers or
* "rgba(0, 0, 0, 0)" in Safari.
* @param {Element} element The element to get the background color
of.
* @return {string} The computed string value of the background color.
*/
goog.style.getBackgroundColor = function(element) {
return goog.style.getStyle_(element, 'backgroundColor');
};
Again.
[snip several more]
/**
* Sets the top/left values of an element. If no unit is specified in
the
* argument then it will add px.
* @param {Element} el Element to move.
* @param {string|number|goog.math.Coordinate} arg1 Left position or
coordinate.
* @param {string|number} opt_arg2 Top position.
*/
goog.style.setPosition = function(el, arg1, opt_arg2) {
var x, y;
var buggyGeckoSubPixelPos = goog.userAgent.GECKO &&
(goog.userAgent.MAC || goog.userAgent.X11) &&
goog.userAgent.isVersion('1.9');
And it does this every time through. :(
if (arg1 instanceof goog.math.Coordinate) {
x = arg1.x;
y = arg1.y;
} else {
x = arg1;
y = opt_arg2;
}
el.style.left = typeof x == 'number' ?
(buggyGeckoSubPixelPos ? Math.round(x) : x) + 'px' :
/** @type {string} */ (x);
el.style.top = typeof y == 'number' ?
(buggyGeckoSubPixelPos ? Math.round(y) : y) + 'px' :
/** @type {string} */ (y);
};
All this for fractions of pixels? Just round for all (assuming there
really is a problem that can't be feature tested).
/**
* Gets the offsetLeft and offsetTop properties of an element and
returns them
* in a Coordinate object
* @param {Element} element Element.
* @return {goog.math.Coordinate} The position.
*/
goog.style.getPosition = function(element) {
return new goog.math.Coordinate(element.offsetLeft,
element.offsetTop);
};
Asymmetrical.
/**
* Returns the viewport element for a particular document
* @param {Node} opt_node DOM node (Document is OK) to get the
viewport element
* of.
* @return {Element} document.documentElement or document.body.
*/
goog.style.getClientViewportElement = function(opt_node) {
var doc;
if (opt_node) {
if (opt_node.nodeType == goog.dom.NodeType.DOCUMENT) {
doc = opt_node;
} else {
doc = goog.dom.getOwnerDocument(opt_node);
}
} else {
doc = goog.dom.getDocument();
}
// In old IE versions the document.body represented the viewport
Interesting observation. That sort of matches the next line. I think
they meant IE quirks mode (of course, IE doesn't have a monopoly on
that). That's the trouble with programming by observation.
if (goog.userAgent.IE && !goog.dom.getDomHelper(doc).isCss1CompatMode
()) {
return doc.body;
}
return doc.documentElement;
Wrong in quirks mode in virtually all modern browsers. Clearly this
has never been tested in quirks mode. Google apparently didn't have a
lot of people on this one. ;)
};
/**
* Gets the client rectangle of the DOM element.
*
* getBoundingClientRect is part of a new CSS object model draft (with
a
* long-time presence in IE), replacing the error-prone parent offset
* computation and the now-deprecated Gecko getBoxObjectFor.
*
* This utility patches common browser bugs in getClientBoundingRect.
It
* will fail if getClientBoundingRect is unsupported.
*
* @param {Element} el The element whose bounding rectangle is being
queried.
* @return {Object} A native bounding rectangle with numerical left,
top,
* right, and bottom. Reported by Firefox to be of object type
ClientRect.
* @private
*/
goog.style.getBoundingClientRect_ = function(el) {
var rect = el.getBoundingClientRect();
// Patch the result in IE only, so that this function can be inlined
if
// compiled for non-IE.
if (goog.userAgent.IE) {
// In IE, most of the time, 2 extra pixels are added to the top
and left
// due to the implicit 2-pixel inset border. In IE6/7 quirks mode
and
// IE6 standards mode, this border can be overridden by setting
the
// document element's border to zero -- thus, we cannot rely on
the
// offset always being 2 pixels.
Sort of.
// In quirks mode, the offset can be determined by querying the
body's
// clientLeft/clientTop, but in standards mode, it is found by
querying
// the document element's clientLeft/clientTop. Since we already
called
// getClientBoundingRect we have already forced a reflow, so it is
not
// too expensive just to query them all.
The problem is that you don't want to query them all.
// See: http://msdn.microsoft.com/en-us/library/ms536433
(VS.85).aspx
var doc = el.ownerDocument;
rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft;
rect.top -= doc.documentElement.clientTop + doc.body.clientTop;
Broken as designed. The body can certainly have a border.
}
return /** @type {Object} */ (rect);
};
/**
* Returns the first parent that could affect the position of a given
element.
* @param {Element} element The element to get the offset parent for.
* @return {Element?} The first offset parent or null if one cannot be
found.
*/
goog.style.getOffsetParent = function(element) {
// element.offsetParent does the right thing in IE, in other browser
it
// only includes elements with position absolute, relative or fixed,
not
// elements with overflow set to auto or scroll.
if (goog.userAgent.IE) {
return element.offsetParent;
}
var doc = goog.dom.getOwnerDocument(element);
var positionStyle = goog.style.getStyle_(element, 'position');
var skipStatic = positionStyle == 'fixed' || positionStyle ==
'absolute';
for (var parent = element.parentNode; parent && parent != doc;
parent = parent.parentNode) {
positionStyle =
goog.style.getStyle_(/** @type {!Element} */ (parent),
'position');
skipStatic = skipStatic && positionStyle == 'static' &&
parent != doc.documentElement && parent != doc.body;
if (!skipStatic && (parent.scrollWidth > parent.clientWidth ||
parent.scrollHeight > parent.clientHeight ||
positionStyle == 'fixed' ||
positionStyle == 'absolute')) {
return /** @type {!Element} */ (parent);
}
}
return null;
};
This is perhaps the most ill-advised function in the history of
browser scripting.
/**
* Calculates and returns the visible rectangle for a given element.
Returns a
* box describing the visible portion of the nearest scrollable
ancestor.
* Coordinates are given relative to the document.
*
* @param {Element} element Element to get the visible rect for.
* @return {goog.math.Box} Bounding elementBox describing the visible
rect or
* null if scrollable ancestor isn't inside the visible viewport.
*/
goog.style.getVisibleRectForElement = function(element) {
var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);
var dom = goog.dom.getDomHelper(element);
var scrollEl = dom.getDocumentScrollElement();
var inContainer;
// Determine the size of the visible rect by climbing the dom
accounting for
// all scrollable containers.
for (var el = element; el = goog.style.getOffsetParent(el); ) {
And they are actually using it.
// clientWidth is zero for inline block elements in IE.
This comment seems out of place.
if ((!goog.userAgent.IE || el.clientWidth != 0) &&
(el.scrollWidth != el.clientWidth ||
el.scrollHeight != el.clientHeight) &&
goog.style.getStyle_(el, 'overflow') != 'visible') {
var pos = goog.style.getPageOffset(el);
var client = goog.style.getClientLeftTop(el);
pos.x += client.x;
pos.y += client.y;
visibleRect.top = Math.max(visibleRect.top, pos.y);
visibleRect.right = Math.min(visibleRect.right,
pos.x + el.clientWidth);
visibleRect.bottom = Math.min(visibleRect.bottom,
pos.y + el.clientHeight);
visibleRect.left = Math.max(visibleRect.left, pos.x);
inContainer = inContainer || el != scrollEl;
}
}
// Compensate for document scroll in non webkit browsers.
Mystical incantation and doesn't reflect the code.
var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;
if (goog.userAgent.WEBKIT) {
visibleRect.left += scrollX;
visibleRect.top += scrollY;
} else {
visibleRect.left = Math.max(visibleRect.left, scrollX);
visibleRect.top = Math.max(visibleRect.top, scrollY);
}
if (!inContainer || goog.userAgent.WEBKIT) {
visibleRect.right += scrollX;
visibleRect.bottom += scrollY;
}
// Clip by the window's viewport.
var winSize = dom.getViewportSize();
visibleRect.right = Math.min(visibleRect.right, scrollX +
winSize.width);
visibleRect.bottom = Math.min(visibleRect.bottom, scrollY +
winSize.height);
return visibleRect.top >= 0 && visibleRect.left >= 0 &&
visibleRect.bottom > visibleRect.top &&
visibleRect.right > visibleRect.left ?
visibleRect : null;
Whatever.
};
/**
* Changes the scroll position of {@code container} with the minimum
amount so
* that the content and the borders of the given {@code element}
become visible.
* If the element is bigger than the container, its top left corner
will be
* aligned to the container's top left corner.
*
* @param {Element} element The element to make visible.
* @param {Element} container The container to scroll.
* @param {boolean} opt_center Whether to center the element in the
container.
* Defaults to false.
*/
goog.style.scrollIntoContainerView = function(element, container,
opt_center) {
// Absolute position of the element's border's top left corner.
var elementPos = goog.style.getPageOffset(element);
// Absolute position of the container's border's top left corner.
var containerPos = goog.style.getPageOffset(container);
var containerBorder = goog.style.getBorderBox(container);
// Relative pos. of the element's border box to the container's
content box.
var relX = elementPos.x - containerPos.x - containerBorder.left;
var relY = elementPos.y - containerPos.y - containerBorder.top;
// How much the element can move in the container, i.e. the
difference between
// the element's bottom-right-most and top-left-most position where
it's
// fully visible.
var spaceX = container.clientWidth - element.offsetWidth;
var spaceY = container.clientHeight - element.offsetHeight;
if (opt_center) {
// All browsers round non-integer scroll positions down.
container.scrollLeft += relX - spaceX / 2;
container.scrollTop += relY - spaceY / 2;
} else {
// This formula was designed to give the correct scroll values in
the
// following cases:
// - element is higher than container (spaceY < 0) => scroll down
by relY
// - element is not higher that container (spaceY >= 0):
// - it is above container (relY < 0) => scroll up by abs(relY)
// - it is below container (relY > spaceY) => scroll down by
relY - spaceY
// - it is in the container => don't scroll
container.scrollLeft += Math.min(relX, Math.max(relX - spaceX,
0));
container.scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));
}
};
/**
* Returns clientLeft (width of the left border and, if the
directionality is
* right to left, the vertical scrollbar) and clientTop as a
coordinate object.
*
* @param {Element} el Element to get clientLeft for.
* @return {goog.math.Coordinate} Client left and top.
*/
goog.style.getClientLeftTop = function(el) {
// NOTE: Gecko prior to 1.9 doesn't support clientTop/Left, see
// https://bugzilla.mozilla.org/show_bug.cgi?id=111207
So test:-
typeof el.clientLeft == 'undefined'
Shorter, faster and gets all of them. ;)
if (goog.userAgent.GECKO && !goog.userAgent.isVersion('1.9')) {
var left = parseFloat(goog.style.getComputedStyle(el,
'borderLeftWidth'));
if (goog.style.isRightToLeft(el)) {
var scrollbarWidth = el.offsetWidth - el.clientWidth - left -
parseFloat(goog.style.getComputedStyle(el,
'borderRightWidth'));
The assumption seems to be that all Gecko's and Gecko impersonators
(minus anything claiming to predate 1.9) support getComputedStyle,
offsetWidth, clientWidth, but not clientLeft/Top.
left += scrollbarWidth;
}
return new goog.math.Coordinate(left,
parseFloat(goog.style.getComputedStyle(el,
'borderTopWidth')));
}
return new goog.math.Coordinate(el.clientLeft, el.clientTop);
};
/**
* Returns a Coordinate object relative to the top-left of the HTML
document.
* Implemented as a single function to save having to do two recursive
loops in
* opera and safari just to get both coordinates. If you just want
one value do
* use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop
(), but
* note if you call both those methods the tree will be analysed
twice.
*
* @param {Element} el Element to get the page offset for.
* @return {goog.math.Coordinate} The page offset.
*/
goog.style.getPageOffset = function(el) {
var box, doc = goog.dom.getOwnerDocument(el);
var positionStyle = goog.style.getStyle_(el, 'position');
I thought there was a function for that. (?)
// NOTE: Gecko pre 1.9 normally use getBoxObjectFor to calculate the
// position. When invoked for an element with position absolute and
a negative
// position though it can be off by one. Therefor the recursive
implementation
// is used in those (relatively rare) cases.
var BUGGY_GECKO_BOX_OBJECT = goog.userAgent.GECKO &&
doc.getBoxObjectFor &&
!el.getBoundingClientRect && positionStyle == 'absolute' &&
(box = doc.getBoxObjectFor(el)) && (box.screenX < 0 ||
box.screenY < 0);
This method has been deprecated for years. Keep the "recursive
implementation" too. This is really too much.
They may not realize it yet, but they need you.
Please send in your c.v. along with more posts like these to (choose a
location):
http://www.google.com/intl/us/jobs/index.html
Or better yet go straight to: sb...@google.com
Apply for:
Peer reviewer / auditor, front-end client-side software engineering,
LOC-QA dept.
:-)
--
Jorge.
Are you really arguing that because they have 20,000 employees, the
logic in the posted code must be right?
> Starting around 10 years ago
> from nothing more than an idea, an growing to this size with
> incompetent people!
They didn't do it with JS. That's for sure. :)
> How much better they might have done with ...
Competent JS programmers? No question. ;)
They'd have certainly been much better off releasing My Library. ;)
> Please send in your c.v. along with more posts like these to (choose a
> location):
>
> http://www.google.com/intl/us/jobs/index.html
>
> Or better yet go straight to: sb...@google.com
>
> Apply for:
> Peer reviewer / auditor, front-end client-side software engineering,
> LOC-QA dept.
>
It's an open source project, genius. An endless Beta test. That's
their scheme (just like YUI). It's a cry for help. Of course, the
sort of JS "programmers" who would flock to such a codebase based on
the Google name are going to be just as clueless as the original
authors. And if Google plans to keep using it, the botched design is
going to be immutable (a la jQuery).
And yeah, I'm sure I could clean it up in a week if I had the time.
Certainly the browser sniffing. But that's when the weasels come out
of the woodwork and start demanding proof that their code still works
in "all browsers" (as if there is any technical basis to believe the
current code works in more than a few browsers). No thanks. But you
might "apply", Jorge. You can weasel with the best of them. ;)
Do Google have a QA department? My impression was that they just
published their stuff untested and waited for complaints form the
users.
>> :-)
>
> :) yes, I also noticed, how Google Corporation with their
> 20 000 employees strangely has happened to employ only stupid
> incompetent people, who do everything wrong.
That is not what is being said, nor is it something that anyone here
is in a position to judge (probably), or necessarily qualified to
judge.
The subject that is a reasonable subject for consideration in the
context of this group is the javascript that Google employees create.
And that javascript is very frequently very bad, not just subjectively
bad but often bad in ways that demonstrate near total ignorance and
incompetence, not just in the authors of the code in question but in
entire teams (as nobody in those teams is capable of eliminating the
evident nonsense).
Google groups has always been an excellent illustration of this point.
Take any post (the display of the message text) in Google groups,
right click on it to bring up the browser's context menu and use the
view source (or equivalent) option and take a look that the page used
to display the messages.
There is quite a lot of javascript on that page, in numerous SCRIPT
elements, none of which have a TYPE attribute. The validity of the
mark-up not being the issue here (the page has an XHTML DOCTYPE but
the mark-up is not even well-formed XML, let alone valid XHTML), but
rather the use of the LANGUAGE attribute, and specifically with things
like - language="javascirpt1.3". This style of attribute is about
conditionally executing different javascript in different
environments, in theory depending on which version of JavaScript(tm)
they support, but in practice depending on which language version
qualifiers they recognise (i.e. Internet Explorers 4 would not
recognise anything greater than language="javascript1.2", but did not
implemented any of the aberrant features of JavaScript(tm) 1.2 (at
least JScript 3, that shipped with IE 4, did not)).
One of the things attempted on this page is to provide a fall-back
element retrieval method for browsers that would not work with
language="javascript1.3". This is attempted in a SCRIPT element with
the opening tag - <script language="javascript"> -, which is the
default version attribute, and would be loaded by any browser that did
not respond to SCRIPT elements qualified as version 1.3. So this
element contains the script:-
| // ----------------------------------
| // cross-browser functions
|
| var IE_all_cache = new Object();
| function IE_getElementById(id) {
| if (IE_all_cache[id] == null) {
| IE_all_cache[id] = document.all[id];
| }
| return IE_all_cache[id];
| }
|
| if (document.all) {
| if (!document.getElementById) {
| document.getElementById = IE_getElementById;
| }
| }
- which is a bit pedestrian, but will (more or less) get the job done.
However, that is immediate followed, within the same script element,
by:-
| //----------------------------------
| // Timezone detection (sets cookie)
|
| try {
| document.cookie = 'GTZ=' + (new Date()).getTimezoneOffset() +
| ';path=/;expires=Mon, 01-Jan-2024 00:00:01 GMT';
| } catch(e) {}
And there you have a try-catch construct. JavaScript(tm) 1.3 did not
have try-catch, and so certainly no preceding versions of the language
did. In any of those language version try-catch is a syntax error, and
a syntax error guarantees that the whole content of a SCRIPT element
(or external script file) will not be executed. Thus we have what
appears to be a fall-back strategy for older browser that is utterly
compromised by the very code used to implement it.
There are a number of possible implications of this. For a start the
authors are trying to provide support for browsers so old that the
chances of the rest of what Google groups is attempting working on
them is close to zero (the pointlessness of this is witnessed by the
fact that they are not inundated with complaints). They are attempting
to do that using a very poor strategy (the LANGUAGE attribute). And
they are not aware of what is involved in doing that; they don't know
which code was available in which language version, or which browsers
supported which code from which language version.
The bottom line is that the people responsible for the javascript used
in Google groups don't understand the code they are using, else they
would understand that they may as well just delete large chunks of it
as it could never do anything useful, and save a little bandwidth in
the process.
Another script element on this page imports:-
"/groups/static/release/g2_common-87c7e48390369e8dd455614e2503700a.js"
- a 'compressed' javascript file. That file demonstrates more of the
same. If you search it for "Function.prototype.apply" you find this
code (re-wrapped for clarity):-
| if(!Function.prototype.apply)
| Function.prototype.apply=function(a,c){
| var d=[];
| a||(a=ra);
| c=c||[];
| for(var e=0;e<c.length;e++)d[e]="args["+e+"]";
| d="oScope.__applyTemp__.peek()("+d.join(",")+");";
| if(!a.__applyTemp__)a.__applyTemp__=[];
| a.__applyTemp__.push(this);
| d=eval(d);
| a.__applyTemp__.pop();
| return d
| }
What this code is is an emulation of standard -
Function.prototype.apply - for javascript versions that pre-date the
introduction of the method. That basically means IE 5, as Netscape 4
had the method and IE 4 would not load the script due to its being
qualified with - language="javascript1.3" -. So once again we may
wonder why they are bothering with this at all.
The - apply - methods of function allow the function to be called, and
passed arguments, with a particular object assigned to the - this-
value for the call. The simple way of doing this for an emulation is
to make a reference to the function a new property of the object that
you want to be the - this - value, and then call the function as a
method of the object, using the new property name. The problem with
this is passing the (possibly) variable number of arguments. The
general strategy for getting round this is to assemble a string that
represents the method call, containing the need arguments list, and
then using - eval - to actually execute that function call.
In the bad old days, before ES3 implementations became the norm, this
type of - apply - emulation was common. I even wrote a couple of
versions myself.
The first thing to point out about the above is that the 'compression'
process has rendered it non-functional because it has changed the
Identifier used to pass in the object to be used as the - this -. In
the pre-compressed version that Identifier was - oScope - (not a good
name as the - this- value is unrelated to scope, but a sufficiently
common misconception for this not to be surprising), post compression
the Identifier has become - a - . The problem being that the string
that is going to be - eval -ed is still trying to refer to the object
as - oScope -, so if this method ever gets executed it will throw a
ReferenceError.
The choice of compression process reflects badly on Google's
javascript programmers because they should have known that compression
by text analysis will always risk falling down whenever - eval - or -
with - are used in javascript. You need a compression process that
creates (at minimum) the parse tree, and then re-serializes that in
order to produce the compressed code. That is the only way in which
the compression process can perceive the - eval - an - with - used and
avoid acting on those parts of the code that should not be modified,
while still maximising the impact on the areas that can be.
The second thing to point out is that even if this function had not
been fatally compromised by the compression process applied to it, it
still would not have done what it needed to do. That is, it will not
call the function with the - this - value set to a reference to a
particular object, which is the primary purpose of an - apply -
emulation.
It would not call the function with the - this - value set to a
particular object because, instead of assigning a reference to the
function as a property of the object and then calling it as a method
of that object, it is adding a reference to the function to an array
property of the object and calling the function as the value returned
from a call to the array's - peek - method. Calling a function in that
way guarantees that the - this - value inside the function call will
be a reference to the global object.
(Incidentally, IE 5, which is probably the only browser likely to use
this - apply - emulation did not have a - peek - method on its arrays,
so it would be using this:-
| Array.prototype.peek=function(){
| return this[this.length-1]
| };
- code to emulate - peek -).
Last, but not least, the - apply - emulation is going to corrupt any
arguments it attempts to pass to the function because it is going to
type-convert them all to string. For string and number type arguments
that is unlikely to be critical, but for boolean arguments they all
gain trueness in the process, and object reference arguments might end
up in any state.
So to summarise Google's javascript programmer's attempt at an - apply
- emulation; It does satisfy the primary requirement of assigning a
specific object as the - this - value, it does not pass the arguments
to the function unmodified, and it has been rendered error-throwing by
the 'compression' process that they chose to apply. The only way they
could have done any worse would have been to include a syntax error in
the code.
> Starting around 10 years ago
> from nothing more than an idea, an growing to this size with
> incompetent people!
> How much better they might have done with ...
Indeed, how much better could they have done?
Richard.
[...]
>
> So to summarise Google's javascript programmer's attempt at an - apply
> - emulation; It does satisfy the primary requirement of assigning a
> specific object as the - this - value, it does not pass the arguments
> to the function unmodified, and it has been rendered error-throwing by
> the 'compression' process that they chose to apply.
As for that first one, did you not mean "It does not satisfy...?"
Yes, or maybe "doesn't", I don't remember which.
Richard.
I am wrong about this bit. The argument values will retain their
original values, or at least they would have if the 'compression'
process had not transformed the original parameter Identifier - args -
into - c -. I think I was looking too hard at the assignment to - d -
line and didn't properly take into account what the - for - loop was
doing.
Still, once you are passed one fatal fault not getting to three is not
much of an achievement.
Richard.