Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > Sencha Touch--Support 2 browsers in just 228K!

Reply
Thread Tools

Sencha Touch--Support 2 browsers in just 228K!

 
 
David Mark
Guest
Posts: n/a
 
      07-16-2010
On Jul 16, 2:28*pm, Garrett Smith <(E-Mail Removed)> wrote:
> On 2010-07-15 11:31 PM, David Mark wrote:
>
> > On Jul 16, 1:28 am, Garrett Smith<(E-Mail Removed)> *wrote:
> >> On 2010-07-15 08:38 PM, David Mark wrote:

>
> >>> Sencha Touch is a bizarre mish-mash of ExtJS and a jQuery plug-in

>
> [...]
>
> >>> This is not a minor quibble. *IE can be configured to disallow host
> >>> object expandos

>
> >> Can it? How so?

>
> > I think you know full well how so.

>
> > document.expando = false;

>
> That is a document setting. There's no good reason for doing that.
>
> > Perhaps you are quibbling with the use of the term "configure"?

>
> I suspected you didn't actually mean what you wrote. A setting that is
> performed by the user is not controllable by the page author.
>
> Contrast to that, document.expando, a document setting is set
> exclusively by the page is not entirely out of the authors' hands. There
> may be cases such as an advert which affects the document, but those can
> often be mitigated. In the case of the advert, keeping the advert in an
> iframe would avoid that.
>
> Any third party library that sets document.expando = false is probably
> going to cause a lot of problems.
>
> Among reasons for not using expandos, the fact that document.expando may
> be set to false seems not a good one.


It's just one. Certainly not the most important.

>
> When it is known that IE will throw errors on simple things like:
> alert.a = 12; and that alert is a host method, or that adding
> document.rootElement might result in an error where the implementation
> comes along and defines that. Those possibilities can easily be factored
> out by not adding expandos.


Yes, that's my point; particularly when they are completely unneeded
(as with that window.undefined nonsense).

>
> [...]
>
>
>
> >> Other parts of Sencha have
> >> code for Internet Explorer, so why did they design it to fail in IE on
> >> this line? It would be trivial to provide a fallback for IE on that.

>
> > Because it is a hastily thrown together mish-mash dressed up as a
> > breakthrough.

>
> IT is because they've copy'n'pasted from the existing Ext-js code and
> that code, despite all the OO plumbing, has interdependencies and code
> duplication. Many things from Ext.util.Element
>


You seem to have trailed off.

>
>
>
>
>
>
> >> [...]

>
> >> You're pulling weeds out of a patch of poison ivy. The design is the
> >> problem and fixing those issues isn't gonna make much difference.

>
> > I agree that the entire thing should be scrapped and the authors are
> > clearly nowhere near proficient enough to write cross-browser
> > scripts. *They've got little more than some pretty graphics and
> > bluster. *But that never stopped jQuery (and many similar efforts) and
> > it didn't seem to register on the Ajaxian editors (of course, nothing
> > ever does).

>
> >>> Pretty lousy name too. *Sencha doesn't exactly roll off the tongue,
> >>> does it? *

>
> >> No, if they want to sell, then they got the name right,

>
> > How do you consider Sencha to be "right"?

>
> Because it works for them.


Works in what way? And who is to say that another name would have
worked better or worse?

>
> >> and they sure
> >> did get investment money from it, so it is working.

>
> > I know all about the millions they got recently, but that's not
> > exclusively because of this product. *They have a whole host of bad
> > scripts and a history of selling them. *It will be interesting to see
> > if they use any of that money to hire competent programmers.

>
> >> Trends say that a name should be Japanese or have an "x" or an "i" in
> >> it.

>
> > Who conducted that study? *And I don't see an "x" or an "i" in Sencha..

>
> Sencha is Japamese.


One out of three ain't bad?
 
Reply With Quote
 
 
 
 
David Mark
Guest
Posts: n/a
 
      07-16-2010
On Jul 16, 12:05*pm, john <(E-Mail Removed)> wrote:
> On 16 Jul 9:10 AM, KCL wrote:
>
> > On Jul 15, 10:38 pm, David Mark<(E-Mail Removed)> *wrote:
> > The first things that come to mind after reading all the criticism is
> > this: *Are all of you who are tearing these guys a new one against the
> > concept of a "framework"? *Is there some framework you DON'T think is
> > a piece of crap?

>
> No one bats an eye when a movie critic rips into the latest release
> despite most critics never having written, directed or produced a film
> of their own.


Yes. In fact the typical "fanboy" loves to rag on movies, but that
mindset rarely carries over to JS libraries. Instead it is "I'm okay,
you're okay" and anyone who dares to say different is labeled as
"elitist" and/or "bitter". It is amusing that every "retort" from
such people (and perhaps they all come from the same anonymous clod?)
is a carbon copy of all that came before it.

* Why do you want to write everything from scratch?

* Wow, that must have taken a lot of time to sort out!

* Let's see you write something better.

* Okay, you wrote something better; now UR just jealous coz nobody
uses it.

The first one indicates a fundamental misunderstanding of the debate
about GP frameworks. The second is a lame attempt at humor. The
third is child-like baiting and reflects the same misunderstanding as
the first. The last is a predictable Hail Mary from people who have
found themselves boxed into a logical corner.

It really makes you wonder about the reasoning ability (or lack
thereof) of the average Web developer. After half a decade of
listening to the same old oversimplifications and bleating about Ajax
and JS libraries, I've come to wonder if their median age is twelve.
Of course, that's an insult to at least some twelve-year-olds.

> Why is it a problem here? Why are you worried about
> negative criticism of a lifeless mass of characters? Don't worry their
> feeling won't be hurt.


The issue is that the typical Web developer is incapable of writing
cross-browser scripts. They really, really want to do it, but reality
is so unfair.

So they are constantly searching for that magic framework that will
allow them to fake it. As such, they get very irritable when told
that no such framework exists. They are much more open to listen to
those who nurse their fantasies than those who burst their balloons.
Again, it's a very child-like mentality. Don't tell them Santa Claus
is not real.

>
> > Give some balance, eh? *Either give examples of alternatives you think
> > "measure up" or write your own.

>
> You could have taken the few seconds required to navigate to google.com
> and type in "David Mark javascript library".


They never do until they are told. Then they change their tune to
suit their delusions (the jealousy angle). Anything to avoid the
unpleasant reality of their own shortcomings. Of course, if they
spent more time learning and less bleating, perhaps they might someday
be capable of doing what they so desperately want to do.
 
Reply With Quote
 
 
 
 
David Mark
Guest
Posts: n/a
 
      07-17-2010
On Jul 15, 11:38*pm, David Mark <(E-Mail Removed)> wrote:
> Sencha Touch is a bizarre mish-mash of ExtJS and a jQuery plug-in
> called JQTouch.


And I am so jealous (and have so much time on my hands) that I will
pick up where I left off. The public outcry was just not loud enough
to suit my taste.

And yes, that's sarcasm. My primary motivation for posting code
reviews is to prevent people from wasting their own time (and money)
on garbage. Then there's always the possibility that people might
learn something from them.

Of course, this particular script is so awful that anyone who could
learn from its dissection probably won't, instead choosing to focus on
my motivations, time management, etc.

Now where was I? Ah yes, the conditional augmentation of native
prototypes.

/**
* @class String
* These functions are available on every String object.
*/
Ext.applyIf(String.prototype, {
/**
* Escapes the passed string for ' and \
* @param {String} string The string to escape
* @return {String} The escaped string
* @static
*/
escape : function(string) {
return string.replace(/('|\\)/g, "\\$1");
},

What's wrong with that picture? For one, extending native prototypes
is a bad idea for a Web framework, which will likely have to coexist
(i.e. play nice) with other scripts. Of course, extending them if and
only if they haven't been previously extended with *something* is
madness. I mean, there's no way for the script to know if that
something is compatible with its own prospective augmentations.

[skip lots more of those]

(function() {
Ext.Element = Ext.extend(Object, {
/**
* The default unit to append to CSS values where a unit isn't
provided (defaults to px).
* @type String
*/
defaultUnit : "px",

constructor : function(element, forceNew) {

This is a very awkward way to write a constructor.


var dom = typeof element == 'string'
? document.getElementById(element)
: element,
id;

if (!dom) {
return null;
}

And that's a very odd thing to do in a constructor.


id = dom.id;
if (!forceNew && id && Ext.cache[id]) {
return Ext.cache[id].el;

As is that.

}

/**
* The DOM element
* @type HTMLElement
*/
this.dom = dom;

Ill-advised to hitch a host object to an Object object. This sets the
stage for unintended circular references, but it's a moot point as
this script will never run in IE anyway. ISTM that this code was
copied and pasted from ExtJS though, which is troubling.

/**
* The DOM element ID
* @type String
*/
this.id = id || Ext.id(dom);

This is ridiculously invasive. Every "wrapped" DOM element must have
an ID? And didn't I see this being used to wrap document nodes as
well?

return this;

That line clearly does nothing as this is a constructor.

},

/**
* Sets the passed attributes as attributes of this element (a
style attribute can be a string, object or function)

Oh here we go. Why is it that library authors refuse to understand
attributes/properties? How do you simplify DOM manipulation for the
masses when you don't understand the first thing about DOM
manipulation?

* @param {Object} o The object with the attributes
* @param {Boolean} useSet (optional) false to override the
default setAttribute to use expandos.

Make it stop.

* @return {Ext.Element} this
*/
set : function(o, useSet) {
var el = this.dom,
attr,
value;

for (attr in o) {
if (o.hasOwnProperty(attr)) {
value = o[attr];
if (attr == 'style') {
this.applyStyles(value);
}
else if (attr == 'cls') {
el.className = value;
}

Why? Is "cls" easier to recognize than "class"?

else if (useSet !== false) {
el.setAttribute(attr, value);
}
else {
el[attr] = value;
}

Zero value-add there; it's left solely to the application developer to
deal with the attributes vs. properties issues. I suppose that's
better than trying to add value and hopelessly fouling everything up
(see jQuery, Dojo, YUI, etc.)

}
}
return this;
},

/**
* Returns true if this element matches the passed simple selector
(e.g. div.some-class or span:first-child)
* @param {String} selector The simple selector to test
* @return {Boolean} True if this element matches the selector,
else false
*/
is : function(simpleSelector) {
return Ext.DomQuery.is(this.dom, simpleSelector);
},

In this case, Ext.DomQuery is simply a wafer-thin wrapper for QSA,
which is nowhere near consistent cross-browser and will not match the
expectations of developers raised on jQuery (and the like).

/**
* Returns the value of the "value" attribute
* @param {Boolean} asNumber true to parse the value as a number
* @return {String/Number}

Or undefined or NaN. That's the trouble with trying to abstract a
diverse set of DOM node types with a single wrapper (see jQuery).

*/
getValue : function(asNumber){
var val = this.dom.value;
return asNumber ? parseInt(val, 10) : val;
},

addListener : function(eventName, fn, scope, options){
Ext.EventManager.on(this.dom, eventName, fn, scope || this,
options);
return this;
},

It should be pointed out that all of these unnecessary dot operations
slow the code down and impede the efforts of minifiers as well.
Again, odd choices for a mobile framework.

/**
* <p>Removes this element's dom reference. Note that event and
cache removal is handled at {@link Ext#removeNode}</p>
*/
remove : function() {
var me = this,
dom = me.dom;

if (dom) {

Just let it crash if they try to remove it twice. The developers will
thank you as it will make tracking down bugs in their applications
that much easier.

delete me.dom;
Ext.removeNode(dom);
}
},


isAncestor : function(c) {
var p = this.dom;
c = Ext.getDom(c);
if (p && c) {

Same problem as above. Trying to hard to be "robust" without
considering the ramifications for the application developers.

return p.contains(c);
}
return false;
},

Missing documentation for that one.

/**
* Determines if this element is a descendent of the passed in
Element.
* @param {Mixed} element An Ext.Element, HTMLElement or string
linking to an id of an Element.

Mixed === Botched. Never design systems that must discriminate
between host and native objects.

* @returns {Boolean}
*/
isDescendent : function(p) {
return Ext.fly(p).isAncestorOf(this);
},

/**
* Returns true if this element is an ancestor of the passed
element
* @param {HTMLElement/String} el The element to check
* @return {Boolean} True if this element is an ancestor of el,
else false
*/
contains : function(el) {
return !el ? false : this.isAncestor(el);
},

If an application passes this method an argument that type-converts to
false then something is very wrong with the application (and a
"robust" silent failure is not going to help matters). Get the
feeling that if they removed all of this "robustness", the framework
itself would fall apart? It's been noted several times that many of
these functions pose the question of what sort of odd application
would require them. The obvious answer is that the framework itself
requires them (which indicates that its overall "design" is faulty).

/**
* Returns the value of an attribute from the element's underlying
DOM node.
* @param {String} name The attribute name
* @param {String} namespace (optional) The namespace in which to
look for the attribute
* @return {String} The attribute value
*/
getAttribute : function(name, ns) {
var d = this.dom;
return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" +
name) || d.getAttribute(name) || d[name];
},

For starters, this script has no chance of working in XML parse mode.
Perhaps they were thinking of SVG, but the only place they call this
is in that muddled META injection code at the very start. Second,
attribute values are allowed to be empty strings. And last but not
least, attribute names do not always match their associated property
names.

Again, you've got to wonder if the ostensible experts behind this
"ground-breaking" framework are really this ignorant of basic DOM
operations (a la jQuery) or are simply trying to make this stuff as
confusing as possible to keep curious Web developers in the dark (and
therefore perpetually dependent on scripts like this).

Either way, it's poison, creating problems while solving nothing at
all. This is what the typical wheels look like. So why shouldn't
they be re-invented? They are square after all. In this particular
case there is no need for such a wheel in 99.9% of Web applications
(and sure as hell not in that form).

/**
* Set the innerHTML of this element
* @param {String} html The new HTML
* @return {Ext.Element} this
*/
setHTML : function(html) {
if(this.dom) {

If an applications attempts to set the inner HTML after it has
destroyed the reference to the element, the developers need to know so
they can track down the problem in their code. Sorry for the
repetition, but it is caused by the code which makes the same mistakes
over and over.

this.dom.innerHTML = html;
}
return this;

And thanks to the ill-advised "chaining" design, there's no way to
return an indication of failure, so the only practical thing to do in
case of failures is to let it blow up.

},

/**
* Returns the innerHTML of an Element or an empty string if the
element's
* dom no longer exists.
*/
getHTML : function() {
return this.dom ? this.dom.innerHTML : '';
},

Again.

setVisible : function(visible, animate) {
var me = this,

Why?

dom = me.dom,
mode = this.getVisibilityMode();

switch (mode) {
case Ext.Element.VISIBILITY:
this.removeClass(['x-hidden-display', 'x-hidden-
offsets']);
this[visible ? 'removeClass' : 'addClass']('x-hidden-
visibility');
break;

It is absurd to couple this basic (and critical) functionality with
CSS classes. Inefficient too.

case Ext.Element.DISPLAY:
this.removeClass(['x-hidden-visibility', 'x-hidden-
offsets']);
this[visible ? 'removeClass' : 'addClass']('x-hidden-
display');
break;

case Ext.Element.OFFSETS:
this.removeClass(['x-hidden-visibility', 'x-hidden-
display']);
this[visible ? 'removeClass' : 'addClass']('x-hidden-
offsets');
break;

Delete that line.

}

return me;

Did they think that - this - (scope in Ext-speak) would change in the
course of adding and removing classes?

},

getVisibilityMode: function() {
var dom = this.dom,
mode = Ext.Element.data(dom, 'visibilityMode');

if (mode === undefined) {
Ext.Element.data(dom, 'visibilityMode', mode =
Ext.Element.DISPLAY);
}

Don't do sets in gets!

return mode;
},

setDisplayMode : function(mode) {
Ext.Element.data(this.dom, 'visibilityMode', mode);
return this;
}

Bit of an incongruity there. You get "visibility mode" but set
"display mode". And they call my API subterranean.

El.prototype.on = El.prototype.addListener;
El.prototype.un = El.prototype.removeListener;

More "shorthand" waste.

// Alias for people used to Ext JS and Ext Core
El.prototype.update = El.prototype.setHTML;

Same.

El.get = function(el){
var extEl,
dom,
id;

if(!el){
return null;
}

Delete that (I'm done repeating myself about the "robustness" issue).


if (typeof el == "string") { // element id
if (!(dom = document.getElementById(el))) {
return null;
}
if (Ext.cache[el] && Ext.cache[el].el) {

They seem to look at property access as a freebie.

extEl = Ext.cache[el].el;
extEl.dom = dom;
} else {
extEl = El.addToCache(new El(dom));

There's that oddball constructor, which jumps through the same hoops
as this function.

}
return extEl;
} else if (el.tagName) { // dom element
if(!(id = el.id)){
id = Ext.id(el);
}
if (Ext.cache[id] && Ext.cache[id].el) {
extEl = Ext.cache[id].el;
extEl.dom = el;
} else {
extEl = El.addToCache(new El(el));
}

Deja vu all over again.

return extEl;
} else if (el instanceof El) {
if(el != El.docEl){
// refresh dom element in case no longer valid,
// catch case where it hasn't been appended
el.dom = document.getElementById(el.id) || el.dom;
}
return el;
} else if(el.isComposite) {
return el;

The isComposite property might indicate that this is one of their
jQuery-like wrappers (multiple elements). Well, at least they
recognized that using one abstraction for single and multiple nodes is
silly. But why would one pass such an object to this method?

} else if(Ext.isArray(el)) {

D'oh!

return El.select(el);
} else if(el == document) {
// create a bogus element object representing the document
object

Yes, the word "bogus" seems appropriate. This has got to be one of
the strangest API's I've ever seen (and that's saying something).

if(!El.docEl){
var F = function(){};
F.prototype = El.prototype;
El.docEl = new F();
El.docEl.dom = document;

It should be noted that this script will never work for multi-frame
applications.

}
return El.docEl;
}
return null;
};

// private

Like hell it is.

El.addToCache = function(el, id){
id = id || el.id;

Why would they want to override the ID?

Ext.cache[id] = {
el: el,
data: {},
events: {}
};
return el;
};

// private method for getting and setting element data

Nope. It's just as public as every other JS property. They really do
seem to go out of their way to confuse the hell out of beginners.

El.data = function(el, key, value) {
el = El.get(el);
if (!el) {
return null;
}
var c = Ext.cache[el.id].data;
if(arguments.length == 2) {

How about:-

if (typeof value == 'undefined') {

return c[key];
}
else {
return (c[key] = value);
}
};

jQuery-ish getter/setter, which is about the least intuitive interface
imaginable.

// private

No.

// Garbage collection - uncache elements/purge listeners on orphaned
elements

ISTM that if you really feel the need to "purge listeners" (rather
than leaving that to the built-in GC) then you need to do it before
the elements are orphaned. Of course, this is all stems from an IE
issue and this script will never run in IE.

// so we don't hold a reference and cause the browser to retain them
El.garbageCollect = function(){
if(!Ext.enableGarbageCollector){
clearInterval(El.collectorThreadId);

Thread ID?

} else {
var id,
el,
dom,
o;

for(id in Ext.cache){

They waffle back and forth between filtered and unfiltered for-in
loops.

o = Ext.cache[id];
if(o.skipGarbageCollection){
continue;
}

Don't use - continue - as it just makes the code harder to follow.

el = o.el;
dom = el.dom;
if(!dom || !dom.parentNode || (!dom.offsetParent && !
document.getElementById(id))){

If dom does not hold a reference to an element then there's nothing to
do here. And if the element does not have an offsetParent (a test
that will blow up for orphans in IE), it sure as hell won't be found
by getEBI.

if(Ext.enableListenerCollection){
Ext.EventManager.removeAll(dom);

There's one instance where they are "saving" themselves with ill-
advised type-checking. In other words, they are hiding mistakes from
themselves, just as they do for application developers.

}
delete Ext.cache[eid];
}
}
}
};
//El.collectorThreadId = setInterval(El.garbageCollect, 20000);

Interesting.

/*Ext.EventManager.on(window, 'unload', function(){
delete Ext.cache;
delete El._flyweights;
});*/

As is that. For the record, this can be deleted for good.

Ext.applyIf(Ext.Element, {

Why conditional? Ext.Element is created by this script.

unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
camelRe: /(-[a-z])/gi,
opacityRe: /alpha\(opacity=(.*)\)/i,

No. And this is an IE-ism (so what is it doing in here?)

propertyCache: {},
borders: {l: 'border-left-width', r: 'border-right-width', t:
'border-top-width', b: 'border-bottom-width'},
paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-
top', b: 'padding-bottom'},
margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b:
'margin-bottom'},

Wastes of space.

addUnits : function(size) {
if (size === "" || size == "auto" || size === undefined) {
size = size || '';
}

So if size is an empty string or undefined, awkardly assign it an
empty string (which it was to begin with for the former case). If
size is "auto" this is also a no-op.

I see why they did this; it's the *how* that bothers me. Good code is
easy to follow and therefore easy to maintain. The jQuery-ish logic
above is obviously not good code.

else if (!isNaN(size) || !this.unitRe.test(size)) {

It's interesting that they left out null from the above check,
particularly as they will almost certainly be passing the results of
getComputedStyle to this function.

size = size + (this.defaultUnit || 'px');

One expected outcome is "nullpx".

}
return size;
},

parseBox : function(box) {
if (typeof box != 'string') {
box = box.toString();
}
var parts = box.split(' '),
ln = parts.length;

if (ln == 1) {
parts[1] = parts[2] = parts[3] = parts[0];
}
else if (ln == 2) {
parts[2] = parts[0];
parts[3] = parts[1];
}
else if (ln == 3) {
parts[3] = parts[1];
}


Should have used a switch statement.


return {
top arseInt(parts[0], 10) || 0,
right arseInt(parts[1], 10) || 0,
bottomarseInt(parts[2], 10) || 0,
left arseInt(parts[3], 10) || 0
};
},


/**
* Retrieves the document height
* @returns {Number} documentHeight
*/
getDocumentHeight: function() {
return Math.max(!Ext.isStrict ? document.body.scrollHeight :
document.documentElement.scrollHeight, this.getViewportHeight());
},

Wishful thinking that much of this script will function as expected in
quirks mode. And clearly the call to getViewportHeight is a mystical
incantation (the only way it could "work" is by coincidence). The
logic makes zero sense and without comments it's hard to know what
they were thinking.

/**
* Retrieves the viewport height of the window.
* @returns {Number} viewportHeight
*/
getViewportHeight: function(){
return window.innerHeight;
},

That (sort of) explains their confusion. The innerHeight is the
height of the viewport in Android (which is not resizable by the user,
except by rotating the device). But why wasn't document.scrollHeight
good enough for them?

In any event, window.innerHeight returns the height of the user-
controlled "viewfinder" in iPhone, which is a whole different story.
So in most cases the results will vary wildly between the two
browsers. To answer a reader question from earlier in the thread: can
I do better? You better ****ing believe it. I do it for my clients
every day. Just wrapped up a function to retrieve the viewport or
"viewfinder" dimensions (and position) and it works in hell of lot
more than two browsers.

/**
* Retrieves the viewport size of the window.

What's a window?

* @returns {Object} object containing width and height properties
*/
getViewSize : function() {
return {
width: window.innerWidth,
height: window.innerHeight
};
},

As mentioned, this is botched, even in the two devices this script
aspires to support.

/**
* Gets the current position of the element based on page
coordinates. Element must be part of the DOM tree to have page
coordinates (display:none or elements not appended return false).


The most overused, overrated and thoroughly unneeded function in the
history of browser scripting. Designing systems that rely on this
working is madness.


* @return {Array} The XY position of the element
*/
getXY : (function() {
// if(Ext.platform.hasGetBoundingClientRect) {
// return function() {
// var dom = this.dom,
// box, scroll;
//
// if (!dom || !dom.ownerDocument ||
dom.ownerDocument.body === dom) {
// return [0, 0];
// }
//
// box = dom.getBoundingClientRect();
// scroll = Ext.fly(document).getScroll();
// return [Math.round(box.left + scroll.left),
Math.round(box.top + scroll.top)];
// };
// }
// else {

I see why they commented out that fork. Like the YUI developers, they
haven't quite come to grips with gBCR.

return function() {
var body = document.body || document.documentElement,

Dear God. All the earmarks of a script being cobbled together without
any inkling of what it is actually doing. I guess this is where that
"show me where it fails" attitude comes from. Deep down, the authors
of the "major" GP libraries know they are clueless, so all they have
to cling to are unit test results in the handful of browsers they
observe. As soon as things appear to work, they declare a job well
done (and scream bloody murder when told they need to change
something).

dom = parent = this.dom,
x = y = 0;

if (!dom || dom === body) {
return [0, 0];
}

That makes no sense. I guess they only tested with the Ext CSS
"reset" in place. IIRC, it wipes out margins on the body (among many
other things). Sets EM and STRONG to use default font-weight and font-
style as well.

while (parent) {
x += parent.offsetLeft;
y += parent.offsetTop;

if(parent != dom) {
// For webkit, we need to add parent's
clientLeft/Top as well.

As is the case with most modern browsers.

x += parent.clientLeft || 0;
y += parent.clientTop || 0;
}

parent = parent.offsetParent;
}

// Safari absolute incorrectly account for body
offsetTop.
if (Ext.platform.isWebkit && this.isStyle('position',
'absolute')) {
y -= body.offsetTop;
}

Unneeded (as always) UA sniff. Easy enough to feature test, but given
that they don't "support" anything but two WebKit variants...

parent = dom.parentNode;
while (parent && parent != body) {
x -= parent.scrollLeft;
y -= parent.scrollTop;
parent = parent.parentNode;
}

That's an extremely expensive (and error-prone) operation that is only
needed in special cases. Doing it every time through is a terrible
waste.

return [x, y];
};
// }
})(),

/**
* Sets the position of the element in page coordinates,
regardless of how the element is positioned.

I sincerely doubt that. Though the task is not particularly
difficult. Take a guess (perhaps using the offsetLeft/Top property
values) and set the left/top styles, get the resulting absolute
position (as best you can) and adjust by the difference between what
it is and what you want it to be. Suffice to say, that's not what
they did.

* The element must be part of the DOM tree to have page
coordinates (display:none or elements not appended return false).
* @param {Array} pos Contains X & Y [x, y] values for new
position (coordinates are page-based)
* @return {Ext.Element} this
*/
setXY : function(pos) {
var me = this;

Again with the "me". (!)

if(arguments.length > 1) {
pos = [pos, arguments[1]];
}

// me.position();
var pts = me.translatePoints(pos),
style = me.dom.style;

for (pos in pts) {
if(!isNaN(pts[pos])) style[pos] = pts[pos] + "px";
}
return me;
},

/**
* Gets the left X coordinate
* @param {Boolean} local True to get the local css position
instead of page coordinate
* @return {Number}
*/
getLeft : function(local) {
return parseInt(this.getStyle('left'), 10) || 0;
},

Use parseFloat.


getBox : function(contentBox, local) {
var me = this,
dom = me.dom,
width = dom.offsetWidth,
height = dom.offsetHeight,
xy, box, l, r, t, b;

if (!local) {
xy = me.getXY();
}
else if (contentBox) {
xy = [0,0];
}
else {
xy = [parseInt(me.getStyle("left"), 10) || 0,
parseInt(me.getStyle("top"), 10) || 0];

Again, use parseFloat. I know it is counter-intuitive that fractions
of pixels could matter, but they do. The loss of precision will bite
you when doing subsequent arithmetic on the coordinates or dimensions.

}

if (!contentBox) {
box = {
x: xy[0],
y: xy[1],
0: xy[0],
1: xy[1],
width: width,
height: height
};

The CSS left/top combined with the offsetWidth/Height. Seems like an
odd (and impractical) combination.

}
else {
l = me.getBorderWidth.call(me, "l") +
me.getPadding.call(me, "l");
r = me.getBorderWidth.call(me, "r") +
me.getPadding.call(me, "r");
t = me.getBorderWidth.call(me, "t") +
me.getPadding.call(me, "t");
b = me.getBorderWidth.call(me, "b") +
me.getPadding.call(me, "b");

What's wrong with that picture?

box = {
x: xy[0] + l,
y: xy[1] + t,
0: xy[0] + l,
1: xy[1] + t,
width: width - (l + r),
height: height - (t + b)

And why didn't they just start with the clientLeft/Top/Height/Width
and add/subtract the padding?

};
}

box.left = box.x;
box.top = box.y;
box.right = box.x + box.width;
box.bottom = box.y + box.height;

return box;
},

/**
* Return an object defining the area of this Element which can be
passed to {@link #setBox} to
* set another Element's size/location to match this element.
* @param {Boolean} asRegion(optional) If true an Ext.util.Region
will be returned

Everything is so tangled up. And they assert that this thing is
modular.

* @return {Object} box An object in the format<pre><code>
{
x: &lt;Element's X position>,
y: &lt;Element's Y position>,
width: &lt;Element's width>,
height: &lt;Element's height>,
bottom: &lt;Element's lower bound>,
right: &lt;Element's rightmost bound>
}

getPageBox : function(getRegion) {
var me = this,

Why do they keep doing this (no pun intended)? If it is to save a few
bytes, they are wasting time they could spend effecting reductions
that would actually be significant (e.g. setting local references to
repeatedly called Ext methods, which would also increase performance).

el = me.dom,
w = el.offsetWidth,
h = el.offsetHeight,
xy = me.getXY(),
t = xy[1],
r = xy[0] + w,
b = xy[1] + h,
l = xy[0];

if (getRegion) {
return new Ext.util.Region(t, r, b, l);
}

So if you want to use this method, you are stuck with Ext.util.Region
(and all of its dependencies).

else {
return {
left: l,
top: t,
width: w,
height: h,
right: r,
bottom: b
};
}
},

/**
* Translates the passed page coordinates into left/top css values
for this element

As mentioned, I severely doubt it.

* @param {Number/Array} x The page x or an array containing [x,
y]
* @param {Number} y (optional) The page y, required if x is not
an array
* @return {Object} An object with left and top properties. e.g.
{left: (value), top: (value)}
*/
translatePoints : function(x, y) {
y = isNaN(x[1]) ? y : x[1];
x = isNaN(x[0]) ? x : x[0];

I find the constant use of isNaN disturbing. Certainly more readable
options are available.

var me = this,
relative = me.isStyle('position', 'relative'),
o = me.getXY(),
l = parseInt(me.getStyle('left'), 10),
t = parseInt(me.getStyle('top'), 10);

l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);

return {left: (x - o[0] + l), top: (y - o[1] + t)};
}

Not a chance. And I'm really bitter that they fouled it up.

addClass: function(className) {
var me = this,
i,
len,
v,
cls = [];

if (!Ext.isArray(className)) {

D'oh! The near-constant use of isArray is also a giant red flag, due
to the fact that it is impossible to write a reliable cross-browser
isArray function. Of course, it's not as big a concern for a script
that only aspires to support two browsers.

if (className && !this.hasClass(className)) {
me.dom.className += " " + className;
}

This has got to be straight out of ExtJS. They always end up with
leading spaces in their class names.

}
else {
for (i = 0, len = className.length; i < len; i++) {
v = className[i];
if (v && !me.hasClass(v)) {
cls.push(v);
}
}
if (cls.length) {
me.dom.className += " " + cls.join(" ");
}
}
return me;
},

/**
* Removes one or more CSS classes from the element.
* @param {String/Array} className The CSS class to remove, or an
array of classes
* @return {Ext.Element} this
*/
removeClass: function(className) {
var me = this,
i,
idx,
len,
cls,
elClasses;
if (!Ext.isArray(className)) {
className = [className];
}

Backwards. How about:-

if (typeof className == 'string') {

No function call, no iffy isArray determination (and therefore no
cross-browser worries) and no need for the awkard negative test (which
seem to be everywhere in this script).

/**
* Checks if the specified CSS class exists on this element's DOM
node.
* @param {String} className The CSS class to check for
* @return {Boolean} True if the class exists, else false
*/
hasClass: function(className) {
return className && (' ' + this.dom.className + ' ').indexOf('
' + className + ' ') != -1;
},

Unnecessary type conversion test, (lots of) unnecessary concatenation
and only allows for spaces as delimiters.

Suffice to say, it doesn't get any better from here. If this thing
isn't completely rewritten in competent fashion, it's going to drop
dead.
 
Reply With Quote
 
RobG
Guest
Posts: n/a
 
      07-17-2010
On Jul 17, 12:01*pm, David Mark <(E-Mail Removed)> wrote:
[...]
> Now where was I? *Ah yes, the conditional augmentation of native
> prototypes.


I think you meant built-in prototypes (those of Object, String, etc.).
Native prototypes (i.e. those of constructors created using
javascript) are the only ones that should be augmented.


--
Rob
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      07-17-2010
On Jul 17, 9:55*am, RobG <(E-Mail Removed)> wrote:
> On Jul 17, 12:01*pm, David Mark <(E-Mail Removed)> wrote:
> [...]
>
> > Now where was I? *Ah yes, the conditional augmentation of native
> > prototypes.

>
> I think you meant built-in prototypes (those of Object, String, etc.).
> Native prototypes (i.e. those of constructors created using
> javascript) are the only ones that should be augmented.
>


Yes. I meant the prototypes of built-ins like Number, String, etc.
They didn't go so far as to augment Object.prototype, which would have
played havoc with their unfiltered for-in loops.
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      07-17-2010
On Jul 17, 6:28*am, Andrew Poulos <(E-Mail Removed)> wrote:
> On 17/07/2010 12:01 PM, David Mark wrote:
>
> > On Jul 15, 11:38 pm, David Mark<(E-Mail Removed)> *wrote:
> >> Sencha Touch is a bizarre mish-mash of ExtJS and a jQuery plug-in
> >> called JQTouch.

>
> > Now where was I? *Ah yes, the conditional augmentation of native
> > prototypes.

>
> What gets me is that even though it must be quite emotionally hurtful to
> have something (that they are probably quite proud of) criticised in
> such a strident manner they never appear to even acknowledge the
> validity or otherwise of the comments nor to take any steps towards
> addressing them.


The authors of such efforts appear to work in a parallel universe
where their ignorance is seen as "pragmatism" (see Dojo). They
dismiss any and all technical criticism as the bitter ravings of those
who "write everything from scratch".

>
> It would be heartening to read something like "yes, you're right but
> we're working to improve".
>


When I posted a few of these comments below their Ajaxian commercial,
a couple of them showed up. But I don't think it was in an effort to
learn as much as it was to try to save face. After all, they know
their target market reads Ajaxian religiously.

IIRC, the first guy to show up insinuated that I must not have much
experience with mobile devices (LOL) and that sniffing the UA string
was necessary for such agents (even to support two WebKit variations).

Then the main author chimed in with some of the usual generalized non-
arguments. "We're not perfect" comes to mind. Of course, there is a
wide gap between perfection and failure. He also credited me for
turning them on to some "optimizations". That's what the Dojo-ers
said when I rewrote most of their crap. It's an attempt to
marginalize the input and allow them to use the suggestions without
giving away any significant credit. And then there is the time-worn
"gee, that must have taken a lot of time", which tries to paint their
simple efforts as impossibly intricate and excuse their shortcomings
as the result of very busy schedules. I take that last one as an
attempt at a sly insult.

So the "major" GP library developers don't show up here to defend
their decisions because they know they have no answers. They
generally don't ask questions because that would cause debate about
their own credentials (after all, aren't they supposed to have all of
the answers?) What they do is damage control, insulting critics,
questioning their motivations and eventually copying whatever bits
they can grasp (see jQuery).

And their fans, who worship these people like almighty saviors, are
typically happy to go along with such charades (as opposed to actually
trying to learn something). After all, if their "gods" turn out to be
false idols, where does that leave them? Basically looking foolish
and with a ton of catch-up work ahead of them. It's easier to accept
thin rationalizations that preserve their collective fantasies.
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      07-18-2010
On Jul 17, 6:28*am, Andrew Poulos <(E-Mail Removed)> wrote:

[...]

>
> It would be heartening to read something like "yes, you're right but
> we're working to improve".
>


Revisiting the rest of this thing, ISTM that they would have to first
learn a lot more about browser scripting and then rewrite the whole
thing from scratch. I mean, if there are glaring errors,
misconceptions and design missteps in virtually every function, it
indicates that most proficient of those involved is in over their
head.

/**
* Normalizes currentStyle and computedStyle.

No it doesn't, The currentStyle bit was cropped out as it is only
needed for IE.

* @param {String} property The style property whose value is
returned.
* @return {String} The current value of the style property for
this element.
*/
getStyle: function(prop) {
var dom = this.dom,
value,
computedStyle,
result,
display;

if (dom == document) {
return null;
}

That's ridiculous. If the application passes a reference to a
document node to this function, the application has a problem (which
needs to be tracked down). The null return value is needed to
represent other (more expected) calamities (like elements with a
display style of "none" or any number of possibilities in IE).

prop = Ext.Element.normalize(prop);

result = (value = dom.style[prop]) ? value : (computedStyle =
window.getComputedStyle(dom, null)) ? computedStyle[prop] : null;

First off, you don't use the inline style, except as a last resort.
It may well be something like "30%" or "3em" (according to the above
comments this function seeks to "normalize" computed/current styles).

Second, using window.getComputedStyle is simply wrong. The method is
to be found on the document.defaultView object, which by coincidence
often references the window object.

Third the "concise" nested ternary operations with assignments is a
pattern only a "Jorge" could love.

Instead:-

computedStyle = document.defaultView.getComputedStyle(dom, null));

if (computedStyle) {
computedStyle = computedStyle[prop];
}

if (!computedStyle) {
computedStyle = dom.style[prop] || null;
}

Of course, the last bit isn't really needed as it won't come into play
unless the element has a display style of "none" (in which case the
application developer has made a mistake).

// Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343

Yes, WebKit has at least a couple of problems with right margins.

if (Ext.platform.hasRightMarginBug && marginRightRe.test(prop)
&& out != '0px') {

That flag (hasRightMarginBug) is clearly from ExtJS (and almost
certainly set by a UA sniff). It is not set anywhere in this script.

This is the sort of issue that is to be designed around. Cross-
browser designs based on retrieving accurate computed style values are
flawed from the start. This is especially true for margins, which may
be collapsed (though not the case for horizontal margins). To
determine a horizontal (left/right) margin in pixels, I imagine I
would set the appropriate style to "0" and check if the offsetLeft
changed. Personally, I've never had a cause to do this in any
context.

Of course for an ostensibly cross-browser, GP framework, you can't
design around anything. You've got to try to cram in every solution
to every problem to suit every context (which is inherently
inappropriate for software that must be downloaded).

These are the hardest scripts to write and virtually impossible to get
100% correct. This is why only newcomers attempt them, making the
resulting failures easily predictable. What's troubling is that the
starry-eyed neophytes typically refuse to admit defeat, instead opting
to hide their unrealized designs behind browser sniffing (and
asserting not to care about browsers, old, unknown or yet to be
created that break such ill-advised inferences). That's why the
notion of users changing their UA strings makes them so
uncomfortable. If only they realized that it is the browser sniffing
scripts that force users to do so (i.e. they are the problem, not the
savvy users).

http://www.jibbering.com/faq/notes/detect-browser/

display = this.getStyle('display');

No good. The correct line for this occasion is:-

display = el.style.display;

el.style.display = 'inline-block';
result = view.getComputedStyle(el, '');
el.style.display = display;

See what I mean? Imagine if the inline display style is not specified
(fairly typical). And consider that this script uses classes to hide
elements. If this code were ever executed, there's a good possibility
that an inline style will be set (where none existed before) and the
element will rebuff all future attempts by the framework to hide it.
Alternatively, that last line may end up trying to set an inline style
of "null" (good for a warning or exception, depending on the browser).

}

// Webkit returns rgb values for transparent.

So what? The script only endeavors to support WebKit. As for
normalization, the rgb format is far more useful for a script as it
matches the other umpteen billion color/opacity combinations.

if (result == 'rgba(0, 0, 0, 0)') {
result = 'transparent';
}

return result;
},

/**
* Wrapper for setting style properties, also takes single object
parameter of multiple styles.
* @param {String/Object} property The style property to be set,
or an object of multiple styles.
* @param {String} value (optional) The value to apply to the
given property, or null if an object was passed.
* @return {Ext.Element} this
*/
setStyle: function(prop, value) {
var tmp,
style;

if (!Ext.isObject(prop)) {

Again, try this:-

if (typeof prop == 'string') {

No function call, no property lookup and no negative assertion. Use a
"constant" and it will minify to something like:-

if(typeof a==b){

....which is shorter than:-

if(!Ext.isObject(a)){

tmp = {};
tmp[prop] = value;
prop = tmp;
}

for (style in prop) {

Forgot the filter again.

value = prop[style];
style = Ext.Element.normalize(style);
this.dom.style[style] = value;

That's a lot of extra work each time through (glohal identifier
resolution and property lookups). Each function call opens the door
for a reflow/repaint as well.

}

return this;
},

/**
* <p>Returns the dimensions of the element available to lay
content out in.<p>

Available to lay content out in?

* <p>If the element (or any ancestor element) has CSS style
<code>display : none</code>, the dimensions will be zero.</p>
*/
getViewSize: function() {
var doc = document,
dom = this.dom;

if (dom == doc || dom == doc.body) {
return {
width: Ext.Element.getViewportWidth(),
height: Ext.Element.getViewportHeight()
};
}

Ridiculous. The client dimensions of the body have nothing to do with
the viewport dimensions (except in IE5 and IE quirks mode). Some
browsers in quirks mode (stupidly) store the clientHeight/Width of the
HTML element in those properties on the BODY in a botched attempt to
copy IE's quirks mode implementation. Yes, really.

http://www.cinsoft.net/viewport.asp

So here we have library authors misinterpreting a misinterpretation of
a ten-year-old MS standard.

Given how screwed up the non-IE browsers are in this respect, if you
really had a need to find the client dimensions of the BODY (hard to
imagine), you would need to subtract the border dimensions from its
offsetHeight/Width.

else {
return {
width: dom.clientWidth,
height: dom.clientHeight
};
}
},

So this method retrieves the clientHeight/Width of an element, except
for the BODY which returns window.innerHeight/Width. I suppose with
the Ext CSS reset in place, that might just pass superficial testing,
despite being completely wrong. This is why I dismiss the "show me
where it fails" mentality. If you don't understand what you are
doing, there is always the possibility of guessing and stumbling on to
an "answer" that appears to work in *some* cases (see also "we don't
care about other cases").

/**
* Forces the browser to repaint this element

Browsers repaint elements when they are damned good and ready. The
presence of a function like this indicates an attempt to solve a
problem created elsewhere in the script (and likely due to a
misunderstanding of how/when browsers reflow/repaint content). And
this is the second such function encountered in this script.

* @return {Ext.Element} this
*/
repaint: function() {
var dom = this.dom;
this.addClass("x-repaint");
dom.style.background = 'transparent none';
setTimeout(function() {
dom.style.background = null;

Dear Christ. They actually set a style to null *on purpose* (earlier
it was pointed out that they left the door open for this to happen by
accident).

Am I being to hard on them? Remember, they intend to charge money for
this tripe (and have already used it to induce investors to fork over
millions). It's well-known that jQuery (and especially its plug-ins)
are awful, but I'd sooner use those. At least they are free.

Ext.get(dom).removeClass("x-repaint");
},
1);
return this;
},

/**
* Retrieves the width of the element accounting for the left and
right
* margins.
*/
getOuterWidth: function() {
return this.getWidth() + this.getMargin('lr');
},

How about:-

return this.dom.offsetWidth + ...

Function calls are *expensive* and mobile devices don't have resources
to burn.

// private

Nope.

sumStyles: function(sides, styles) {
var val = 0,
m = sides.match(/\w/g),

Why not just split on spaces?

len = m.length,
s,
i;

for (i = 0; i < len; i++) {
s = m[i] && parseInt(this.getStyle(styles[m[i]]), 10);

As mentioned previously, this can result in a round-off error. Use
parseFloat.

if (s) {
val += Math.abs(s);
}

I guess they don't like negative margins.

}
return val;
}

And so on and so on (and scooby dooby dooby).

Skipping past more everyday code to try to find something close to
resembling HTML5...

if (Ext.platform.isPhone) {
cfg = {
tag: 'embed',
type: 'audio/mpeg',
target: 'myself',
src: this.src || this.url,
controls: 'true'
};
} else {
cfg = {
tag: 'audio',
src: this.src || this.url
};
}

That represents virtually the entire "HTML5" audio bit. This is
Ext.platform.isPhone:-

isPhone: /android|iphone/i.test(Ext.userAgent) && !(/ipad/
i.test(Ext.userAgent)),

As mentioned, this thing only *attempts* to work in two types of
phones (90% of the market according to some unidentified study) plus
one tablet. Still, the above code assumes that all iPhones/Androids
(past, present and future) are better off with a non-standard EMBED
element. My guess is they didn't like the way AUDIO elements take up
the entire screen when played on these devices (but that's what users
expect them to do).

This unfortunate "isPhone" heuristic is also used to style the body.

if (Ext.platform.isPhone) {
cls.push('x-phone');
}

Who needs media queries?

I don't see any reason to mince words. This thing stinks. You'd have
to be insane to use it on a mobile Website. Using it to create a faux
native app is ill-advised as well. The "Sencha" name got the green
part right, but this is almost (but not entirely) unlike tea.
 
Reply With Quote
 
Garrett Smith
Guest
Posts: n/a
 
      07-18-2010
On 2010-07-17 06:25 PM, David Mark wrote:
> On Jul 17, 6:28 am, Andrew Poulos<(E-Mail Removed)> wrote:
>
> [...]
>
>>
>> It would be heartening to read something like "yes, you're right but
>> we're working to improve".
>>

>
> Revisiting the rest of this thing, ISTM that they would have to first
> learn a lot more about browser scripting and then rewrite the whole
> thing from scratch. I mean, if there are glaring errors,
> misconceptions and design missteps in virtually every function, it
> indicates that most proficient of those involved is in over their
> head.
>


[...]

>
> /**
> * Forces the browser to repaint this element
>


The name "repaint" has the outward indication of a mystical incantation.
We know that there is no way to force a repaint, and so when I see a
name like that, I know that the method won't directly cause the browser
to repaint and that it will, at best, perform an unrelated action that
was associated with addressing a perceived problem.

> Browsers repaint elements when they are damned good and ready. The
> presence of a function like this indicates an attempt to solve a
> problem created elsewhere in the script (and likely due to a
> misunderstanding of how/when browsers reflow/repaint content). And
> this is the second such function encountered in this script.
>



> * @return {Ext.Element} this
> */
> repaint: function() {
> var dom = this.dom;
> this.addClass("x-repaint");
> dom.style.background = 'transparent none';
> setTimeout(function() {
> dom.style.background = null;
>


Neither null nor "null" are a valid value for background.

DOM 2 Style defines what must happen when the display property is set:
<http://www.w3.org/TR/1998/REC-CSS2-19980512/visuren.html#propdef-display>

| display of type DOMString
| See the /_background property definition_/ in CSS2.
| Exceptions on setting
| DOMException
|
|
| SYNTAX_ERR: Raised if the new value has a syntax error and is
| unparsable.

That says that a SYNTAX_ERR is if the new value is unparseable, and to
determine if the value `null` is parseable (it isn't, but just in case
that was not blatantly obvious), the reader is directed to the
definition in CSS 2 which is linked from the text "/_background property
definition_/" links to DOM 2 Style:
<http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSS2Properties-background>

Looking at CSS2:
| 'background'
| Value: [<'background-color'> || <'background-image'> ||
| <'background-repeat'> || <'background-attachment'> || <'background-
| position'>] | inherit

And so it is clear that neither null nor "null" are valid property
values. Setting `element.style.display = null`, if the value `null` is
interpreted as the string value "null", then it should raise a SYNTAX_ERR.

That error will happen in IE but since IE is not supported they don't
care. That exception won't happen in webkit so they get away with it there.

[...]

> native app is ill-advised as well. The "Sencha" name got the green
> part right,


That's actually funny.

Sencha, or Ext for TouchScreen devices, supports only a couple devices,
and at a nearly 500k, what does it do?

One might look to the documentation but the documentation is for Ext-JS
and the code for Sencha is largely different.

As various parts of Sencha have been discussed, the query engine has not
yet been mentioned. Of all of the javascript query engines I have
reviewed, Ext-JS is hands down, the worst (and by a very large margin).
The problems with the query engine depend on the version of Ext-JS go
back and forth depending on the version of Ext. The latest version uses
NFD (native first, dual) approach. I'll detail more on these, as well as
the NFD query antipattern, in a future post. For now, lets have a look
at the query engine for Sencha.

The current release of Sencha's query selector uses only
document.querySelectorAll, and supports none of the extensions that are
supported by other version of Ext-JS. Since Sencha splits input on ","
first, it breaks on any selector that contains a comma, such as
[title="Hey, Joe"].

These problems in the source code should be easy for anyone to see.

Splitting input on "," creates two significant problems, actually.

The first problem is that it creates invalid selectors out of any
perfectly valid selector that contains a comma. For example, Sencha will
convert the selector '[title="Hey, Joe"]' to '[title="Hey,' and ' Joe"],
both of which are invalid. Sencha then passes each item to
document.querySelectorAll which throws an uncaught error on the invalid
'[title="Hey,' selector.

The second problem is that when it "works", the result can contain
duplicates.

For example, for a document that contains three divs:

<body>
<div></div> <div></div> <div></div>
</body>

The following code:
[Ext.query("div").length , Ext.query("div, div").length]

- results [3, 6]

My code comments below are annotated with (GS), as in
// (GS) A review comment left by Garrett Smith.

Code review begins:

/**
* Selects a group of elements.
* @param {String} selector The selector/xpath query (can be a comma
* separated list of selectors)
* @param {Node/String} root (optional) The start of the query
* (defaults to document).
* @return {Array} An Array of DOM elements which match the selector.
* If there are no matches, and empty Array is returned.
*/
select : function(q, root) {
var results = [],
nodes,
i,
j,
qlen,
nlen;

root = root || document;
if (typeof root == 'string') {
// (GS) This call may return null.
root = document.getElementById(root);
}

// (GS) splitting the input on "," will break lexical rules and
// (GS) will match any simple selector that has a comma in it.
q = q.split(",");
for (i = 0, qlen = q.length; i < qlen; i++) {
if (typeof q[i] == 'string') {
// (GS) If root is null, an error will result.
nodes = root.querySelectorAll(q[i]);

for (j = 0, nlen = nodes.length; j < nlen; j++) {
results.push(nodes[j]);
}
}
}
return results;
},

As seen in the results, selectors that are separated by a comma are
added to the results. After reading the code, it is clear that the code
does exactly what should be expected of it.

There is no apparent reason for such design decision; the results are
surprising and probably undesirable for most. This appears to be a yet
another careless mistake and one that adds the overhead of creating an
array and looping through the result.

Misleading Comment
The code comment documents the selector param as a selector/xpath query.
That is misleading because the code is not designed to handle XPath
expressions. Method document.querySelectorAll uses CSS Selectors. It
will throw errors on any invalid syntax. There are a couple syntax
similiarties between XPath and CSS Selectors, however the differences
between the two are much greater. XPath expressions and CSS Selectors
are represented by different standards.

Since Sencha uses document.querySelectorAll, it can be assured that an
error will occur when using XPath. Ext.query("//foo"); will result in an
error being thrown.

Bigger is Better?
At nearly 500k, with comments stripped, the core of Sencha is larger
than jQuery and YUI combined. The drawbacks to such a large codebase on
mobile device that has a web browser include: slow download times, extra
cost to end user (depends on plan, connection and roaming charges),
memory and processing overhead of interpreting the javascript and
keeping it in memory, and filling or exceed a browser's cache-limit.

Despite the size, Sencha is only supported on a few browsers and only on
those browsers that have native NodeSelector support. Despite those
limitations, it still manages to introduce more bugs than the browsers
provide.

Other Browsers
Sencha is apparently not intended to be used on any version of Internet
Explorer. By including sencha js on a page, errors are thrown in
Internet Explorer due to calling document.addEventListener.
--
Garrett
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      07-18-2010
On Jul 18, 2:18*am, Garrett Smith <(E-Mail Removed)> wrote:

[...]

>
> That error will happen in IE but since IE is not supported they don't
> care. That exception won't happen in webkit so they get away with it there.


Yes, this is yet another case of script developers hiding (current or
potential) problems from *themselves*. I cringe every time I hear "I
don't care about browser XYZ" or "Nobody uses browser XYZ" as an
excuse for limited testing or slovenly feature detection. The former
of which is like testing a new car design exclusively on freshly paved
highways. You have to go off-road to conduct a proper stress-test.
And why is it always the most inexperienced developers (i.e. the ones
who would most benefit) who spout such drivel? For example, about a
year back I had some snot-nose nastily asking me why I was "wasting
time" loading a test page in IE7. (!) How much ****ing time does it
take to load a page and note whether or not it throws an exception?
Predictably their subsequent "effort" threw exceptions in IE7 (and IE6
and IE5 and IE8 compatibility mode and God knows what else), wasting
the time of countless end-users. And the punchline is that the
project was a credit card form, so the oversight certainly cost them
some sales (not to mention embarrassment).

>
> [...]
>
> > native app is ill-advised as well. *The "Sencha" name got the green
> > part right,

>
> That's actually funny.


I'm a funny guy.

>
> Sencha, or Ext for TouchScreen devices, supports only a couple devices,
> and at a nearly 500k, what does it do?


So as not to cause confusion, I prefer to compare minified sizes (and
I can't see penalizing scripts for using long variable names). It's
bad enough that marketers report sizes after compression. I assume
you are referring to the size with white space and without comments.
Of course, it is 228K minified, but the CSS and graphics easily push
the payload over 500K. Add the application script, markup and assets
and the total will approach 1MB. The ExtJS camp say to just use a
manifest (as if phones have unlimited room to store such
monstrosities). And due to the browser sniffing in the script and the
ever-changing mobile environments, "upgrades" are likely to be
frequent, requiring end-users to re-download this crap endlessly.

JFTR, I know from experience that you can do much of the flashy "CSS3"
stuff seen in the Sencha demos with 0K of script (and without the
ridiculous CSS "resets" and monolithic "themes" that they are
pushing).

>
> One might look to the documentation but the documentation is for Ext-JS
> and the code for Sencha is largely different.


Yes, it is an unlikely (and unwieldy) marriage of ExtJS and JQTouch.
There's a match made in hell.

>
> As various parts of Sencha have been discussed, the query engine has not
> yet been mentioned.


I think I mentioned at some point that it was simply a QSA wrapper
(and not a very good one).

> Of all of the javascript query engines I have
> reviewed, Ext-JS is hands down, the worst (and by a very large margin).


I recently went through the (much) bigger version and it is the usual
crap. Do all of these developers work in sound-proof booths with
blinders on? That can't be as most of the same mistakes show up in
all of them (indicating they copy one another). But none of them read
(or search) this newsgroup; that's for sure.

> The problems with the query engine depend on the version of Ext-JS go
> back and forth depending on the version of Ext.


Sounds like jQuery. It's because they are using guesswork rather than
understanding and then patching problems as they are reported. If
their patches break last year's browser they just cross that browser
off the "supported" list rather than trying to see where they went
wrong. It's laughable as these query engines boil down to basic
string parsing and DOM traversal, so should work in virtually any
browser released in the last five years (and many older ones). You
sure can't offer a clean degradation path for "ancient" or unforeseen
agents if your core logic (on which everything hinges) throws
exceptions and/or returns incorrect results in Opera 9. But then, the
developers stopped "caring" about Opera 9 as soon as their patchworks
stopped working in it (see also Dojo).

> The latest version uses
> NFD (native first, dual) approach.


The big one? The "Touch" version uses QSA only. The "dual" approach
is obviously flawed (as recounted numerous times) as the old
patchworks vary wildly from QSA. In other words, "Sizzle" was/is a
complete fraud. It's like using an MS SQL Server database, except
when it is down, then routing queries to some buggy, incompatible open-
source piece of **** maintained by "DB Ninjas".

> I'll detail more on these, as well as
> the NFD query antipattern, in a future post.


It may not have had a name, but the pattern has long since been outed
as bunk. Trouble is that library devotees don't listen. Try to
explain any of this stuff to them and they start bleating about
reinventing wheels and assembly language (see Kenny).

> For now, lets have a look
> at the query engine for Sencha.
>
> The current release of Sencha's query selector uses only
> document.querySelectorAll, and supports none of the extensions that are
> supported by other version of Ext-JS. Since Sencha splits input on ","
> first, it breaks on any selector that contains a comma, such as
> [title="Hey, Joe"].


It makes me wonder why they are splitting in the first place. Likely
a holdover from the bigger version. That's the sort of thing that
happens when "programmers" rearrange patterns of code without
understanding them.

>
> These problems in the source code should be easy for anyone to see.
>
> Splitting input on "," creates two significant problems, actually.
>
> The first problem is that it creates invalid selectors out of any
> perfectly valid selector that contains a comma. For example, Sencha will
> convert the selector '[title="Hey, Joe"]' to '[title="Hey,' and ' Joe"],
> both of which are invalid. Sencha then passes each item to
> document.querySelectorAll which throws an uncaught error on the invalid
> '[title="Hey,' selector.


Oh for Christ's sake; that's right. They didn't even bother to wrap
the QSA call in a try-catch. Probably too elitist for them. Such
incompetence makes me bitter (and jealous).

>
> The second problem is that when it "works", the result can contain
> duplicates.


Of course.

>
> For example, for a document that contains three divs:
>
> <body>
> * *<div></div> *<div></div> *<div></div>
> </body>
>
> The following code:
> [Ext.query("div").length , Ext.query("div, div").length]
>
> - results [3, 6]


Doesn't get much worse than that.

>
> My code comments below are annotated with (GS), as in
> // (GS) A review comment left by Garrett Smith.
>
> Code review begins:
>
> /**
> * * Selects a group of elements.
> * * @param {String} selector The selector/xpath query (can be a comma
> * * separated list of selectors)


As noted, that's pure fantasy (or complete ignorance).

> * * @param {Node/String} root (optional) The start of the query
> * * (defaults to document).
> * * @return {Array} An Array of DOM elements which match the selector.
> * * If there are no matches, and empty Array is returned.
> * */
> select : function(q, root) {
> * * *var results = [],
> * * * * *nodes,
> * * * * *i,
> * * * * *j,
> * * * * *qlen,
> * * * * *nlen;
>
> * * *root = root || document;
> * * *if (typeof root == 'string') {
> * * * * *// (GS) This call may return null.
> * * * * *root = document.getElementById(root);
> * * *}
>
> * * *// (GS) splitting the input on "," will break lexical rules and
> * * *// (GS) will match any simple selector that has a comma in it.
> * * *q = q.split(",");
> * * *for (i = 0, qlen = q.length; i < qlen; i++) {
> * * * * *if (typeof q[i] == 'string') {
> * * * * * * *// (GS) If root is null, an error will result.
> * * * * * * *nodes = root.querySelectorAll(q[i]);
>
> * * * * * * *for (j = 0, nlen = nodes.length; j < nlen;j++) {
> * * * * * * * * *results.push(nodes[j]);
> * * * * * * *}
> * * * * *}
> * * *}
> * * *return results;
>
> },
>
> As seen in the results, selectors that are separated by a comma are
> added to the results. After reading the code, it is clear that the code
> does exactly what should be expected of it.


Yes. It shouldn't take an "elitist" in an "ivory tower" to spot
that. If writing a QSA wrapper was a homework assignment for a first-
year CS student, this code would certainly result in a 0.

>
> There is no apparent reason for such design decision; the results are
> surprising and probably undesirable for most.


Probably?!

> This appears to be a yet
> another careless mistake and one that adds the overhead of creating an
> array and looping through the result.


All for no reason, unless they were literally *trying* to fall flat on
their face.

>
> Misleading Comment
> The code comment documents the selector param as a selector/xpath query.


Hard to tell whether that is a misconception or a copy/paste mistake.
That's the problem with "technical" writers who don't speak the
language (see scope, thread, literal, private, class, singleton, etc.)

> That is misleading because the code is not designed to handle XPath
> expressions. Method document.querySelectorAll uses CSS Selectors. It
> will throw errors on any invalid syntax. There are a couple syntax
> similiarties between XPath and CSS Selectors, however the differences
> between the two are much greater. XPath expressions and CSS Selectors
> are represented by different standards.
>
> Since Sencha uses document.querySelectorAll, it can be assured that an
> error will occur when using XPath.


An uncaught error no less. Odd considering their neurotic type-
checking tendencies. This is one case where they definitely needed to
handle exceptions due to variations in QSA implementations.

> Ext.query("//foo"); will result in an
> error being thrown.
>
> Bigger is Better?
> At nearly 500k, with comments stripped, the core of Sencha is larger
> than jQuery and YUI combined.


But it's much smaller if you GZIP the files! I've seen it described
as a "svelte" 80K which the clueless blogger found "amazing" given its
"unparalleled" feature set. And last I checked, the full build of My
Library was a little over half that.

> The drawbacks to such a large codebase on
> mobile device that has a web browser include: slow download times, extra
> cost to end user (depends on plan, connection and roaming charges),
> memory and processing overhead of interpreting the javascript and
> keeping it in memory, and filling or exceed a browser's cache-limit.


Yes, even with a magic manifest.

>
> Despite the size, Sencha is only supported on a few browsers and only on
> those browsers that have native NodeSelector support. Despite those
> limitations, it still manages to introduce more bugs than the browsers
> provide.


Yes, it clearly creates more problems than it solves. But just look
at those cool graphics! And (from the Ajaxian infomercial) two guys
dressed like Obi-wan Kenobi can't be wrong.

>
> Other Browsers
> Sencha is apparently not intended to be used on any version of Internet
> Explorer.


Or anything besides WebKit. Inexplicably, they equate proprietary
WebKit extensions with CSS3. Odd choice considering the fact that the
latest Opera and Firefox support most of the same stuff (including
transitions).

> By including sencha js on a page, errors are thrown in
> Internet Explorer due to calling document.addEventListener.


Yes, that's one of many reasons it won't run in any MSHTML-based user
agents. I guess they wasted so much space that they didn't have room
to fit in a call to attachEvent.

I've got an HTML5 audio add-on and video would require very similar
code (perhaps I'll combine them). Things like localStorage and
geolocation are trivial. And they left out a host of (real) HTML5
features too. I will have to make some time to "catch up" on these
fronts. Certainly I have plenty of room for such add-ons.
Prospective testers should contact me...
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      07-18-2010
On Jul 18, 9:10*am, kangax <(E-Mail Removed)> wrote:
> On 7/18/10 2:18 AM, Garrett Smith wrote:
>
> > On 2010-07-17 06:25 PM, David Mark wrote:

> [...]
>
> >> /**
> >> * Forces the browser to repaint this element

>
> > The name "repaint" has the outward indication of a mystical incantation..
> > We know that there is no way to force a repaint, and so when I see a
> > name like that, I know that the method won't directly cause the browser
> > to repaint and that it will, at best, perform an unrelated action that
> > was associated with addressing a perceived problem.

>
> I'm not sure where you coming from with this.
>
> There certainly are observable ways to trigger both — reflow and
> repaint; at least in WebKit (as that's the layout engine being discussed
> here).


It's certainly fairly predictable.

>
> In Chrome you can use Speed Tracer
> (<http://code.google.com/webtoolkit/speedtracer/>, developed by Google
> themselves, IIRC) to take a peek into when browser reflows the document
> and when it repaints the screen.


It should be noted that is but one flavor of WebKit. It's a desktop
version as well, so may behave in a slightly different manner than
mobile variations.

>
> Try something like this, to see both — reflow and repaing hapenning at
> ~3085ms:


Well sure changing the style rules at 3000ms will cause the browser to
reflow/repaint at some time shortly after 3000ms from current. At
least under normal circumstances.

>
> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
> * *"http://www.w3.org/TR/html4/strict.dtd">
> <html>
> * *<head>
> * * *<title></title>
> * * *<style type="text/css">
> * * * *#test { width: 100px; background: red; }
> * * *</style>
> * *</head>
> * *<body>
> * * *<div id="test">test</div>
> * * *<script type="text/javascript">
> * * * *setTimeout(function() {
> * * * * *document.styleSheets[0].addRule('#test', 'width: 200px');
> * * * *}, 3000);
> * * *</script>
> * *</body>
> </html>
>
> [...]
>
> Or am I missing something?
>


Yes; the code in question seems to assert that it can make the browser
reflow/repaint at a specific point in the execution. And it doesn't
change the style rules, but does the styling equivalent of a no-op.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Not just different browsers that sites need testing in? Tim W HTML 11 08-08-2012 09:29 PM
Browsers, browsers! Quo vadis? El Kabong HTML 23 05-13-2007 08:55 PM
Is there something RoboForm that works for all programs, not just browsers? Jimmy Dean Computer Support 6 02-28-2006 04:20 AM
Two Browsers work! Two browsers won't load. Internet game service won't load jimmie Computer Support 1 02-26-2006 08:36 AM
allow you to move just about any music to your iPud, MP3 player , or just burn it to disk sbcmynews Computer Support 4 05-01-2005 03:53 PM



Advertisments