Closed Bug 385641 Opened 17 years ago Closed 17 years ago

general webpage load considerably enhanced

Categories

(Firefox :: File Handling, enhancement)

x86
Windows XP
enhancement
Not set
normal

Tracking

()

VERIFIED INVALID

People

(Reporter: holpo, Unassigned)

Details

Attachments

(1 file)

13.84 KB, application/x-javascript
Details
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) Gecko/20070518 Firefox/2.0.0.3 Sulfur/0.8.0.99
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) Gecko/20070518 Firefox/2.0.0.3 Sulfur/0.8.0.99

Through analyzing leaks and error console messages on the workings of GetElementsById and GetElementsByTagname the browser does not process the events in a selected way, this hampers the onload of pages. 
After installing the JavaScript SelectableElementsTable both on dial-up as on broadband the page load was faster by heaps.

Reproducible: Always

Steps to Reproduce:
1. Normal hampered page load
2. Install SelectableElementsTable.js in the components folder
3. Faster page load
Actual Results:  
Many reported faster load.


The code to mend the bug: [code]
/* SelectableElementsTable.js

function SelectableElements(oElement, bMultiple) {
	if (oElement == null)
		return;

	this._htmlElement = oElement;
	this._multiple = Boolean(bMultiple);

	this._selectedItems = [];
	this._fireChange = true;

	var oThis = this;
	this._onclick = function (e) {
		if (e == null) e = oElement.ownerDocument.parentWindow.event;
		oThis.click(e);
	};

	if (oElement.addEventListener)
		oElement.addEventListener("click", this._onclick, false);
	else if (oElement.attachEvent)
		oElement.attachEvent("onclick", this._onclick);
}

SelectableElements.prototype.setItemSelected = function (oEl, bSelected) {
	if (!this._multiple) {
		if (bSelected) {
			var old = this._selectedItems[0]
			if (oEl == old)
				return;
			if (old != null)
				this.setItemSelectedUi(old, false);
			this.setItemSelectedUi(oEl, true);
			this._selectedItems = [oEl];
			this.fireChange();
		}
		else {
			if (this._selectedItems[0] == oEl) {
				this.setItemSelectedUi(oEl, false);
				this._selectedItems = [];
			}
		}
	}
	else {
		if (Boolean(oEl._selected) == Boolean(bSelected))
			return;

		this.setItemSelectedUi(oEl, bSelected);

		if (bSelected)
			this._selectedItems[this._selectedItems.length] = oEl;
		else {
			// remove
			var tmp = [];
			var j = 0;
			for (var i = 0; i < this._selectedItems.length; i++) {
				if (this._selectedItems[i] != oEl)
					tmp[j++] = this._selectedItems[i];
			}
			this._selectedItems = tmp;
		}
		this.fireChange();
	}
};

// This method updates the UI of the item
SelectableElements.prototype.setItemSelectedUi = function (oEl, bSelected) {
	if (bSelected)
		addClassName(oEl, "selected");
	else
		removeClassName(oEl, "selected");

	oEl._selected = bSelected;
};

SelectableElements.prototype.getItemSelected = function (oEl) {
	return Boolean(oEl._selected);
};

SelectableElements.prototype.fireChange = function () {
	if (!this._fireChange)
		return;
	if (typeof this.onchange == "string")
		this.onchange = new Function(this.onchange);
	if (typeof this.onchange == "function")
		this.onchange();
};


SelectableElements.prototype.click = function (e) {
	var oldFireChange = this._fireChange;
	this._fireChange = false;

	// create a copy to compare with after changes
	var selectedBefore = this.getSelectedItems();	// is a cloned array

	// find row
	var el = e.target != null ? e.target : e.srcElement;
	while (el != null && !this.isItem(el))
		el = el.parentNode;

	if (el == null) {	// happens in IE when down and up occur on different items
		this._fireChange = oldFireChange;
		return;
	}

	var rIndex = el;
	var aIndex = this._anchorIndex;

	// test whether the current row should be the anchor
	if (this._selectedItems.length == 0 || (e.ctrlKey && !e.shiftKey && this._multiple)) {
		aIndex = this._anchorIndex = rIndex;
	}

	if (!e.ctrlKey && !e.shiftKey || !this._multiple) {
		// deselect all
		var items = this._selectedItems;
		for (var i = items.length - 1; i >= 0; i--) {
			if (items[i]._selected && items[i] != el)
				this.setItemSelectedUi(items[i], false);
		}
		this._anchorIndex = rIndex;
		if (!el._selected) {
			this.setItemSelectedUi(el, true);
		}
		this._selectedItems = [el];
	}

	// ctrl
	else if (this._multiple && e.ctrlKey && !e.shiftKey) {
		this.setItemSelected(el, !el._selected);
		this._anchorIndex = rIndex;
	}

	// ctrl + shift
	else if (this._multiple && e.ctrlKey && e.shiftKey) {
		// up or down?
		var dirUp = this.isBefore(rIndex, aIndex);

		var item = aIndex;
		while (item != null && item != rIndex) {
			if (!item._selected && item != el)
				this.setItemSelected(item, true);
			item = dirUp ? this.getPrevious(item) : this.getNext(item);
		}

		if (!el._selected)
			this.setItemSelected(el, true);
	}

	// shift
	else if (this._multiple && !e.ctrlKey && e.shiftKey) {
		// up or down?
		var dirUp = this.isBefore(rIndex, aIndex);

		// deselect all
		var items = this._selectedItems;
		for (var i = items.length - 1; i >= 0; i--)
			this.setItemSelectedUi(items[i], false);
		this._selectedItems = [];

		// select items in range
		var item = aIndex;
		while (item != null) {
			this.setItemSelected(item, true);
			if (item == rIndex)
				break;
			item = dirUp ? this.getPrevious(item) : this.getNext(item);
		}
	}

	// find change!!!
	var found;
	var changed = selectedBefore.length != this._selectedItems.length;
	if (!changed) {
		for (var i = 0; i < selectedBefore.length; i++) {
			found = false;
			for (var j = 0; j < this._selectedItems.length; j++) {
				if (selectedBefore[i] == this._selectedItems[j]) {
					found = true;
					break;
				}
			}
			if (!found) {
				changed = true;
				break;
			}
		}
	}

	this._fireChange = oldFireChange;
	if (changed && this._fireChange)
		this.fireChange();
};

SelectableElements.prototype.getSelectedItems = function () {
	//clone
	var items = this._selectedItems;
	var l = items.length;
	var tmp = new Array(l);
	for (var i = 0; i < l; i++)
		tmp[i] = items[i];
	return tmp;
};

SelectableElements.prototype.isItem = function (node) {
	return node != null && node.nodeType == 1 && node.parentNode == this._htmlElement;
};

SelectableElements.prototype.destroy = function () {
	if (this._htmlElement.removeEventListener)
		this._htmlElement.removeEventListener("click", this._onclick, false);
	else if (this._htmlElement.detachEvent)
		this._htmlElement.detachEvent("onclick", this._onclick);

	this._htmlElement = null;
	this._onclick = null;
	this._selectedItems = null;
};

/* Traversable Collection Interface */

SelectableElements.prototype.getNext = function (el) {
	var n = el.nextSibling;
	if (n == null || this.isItem(n))
		return n;
	return this.getNext(n);
};

SelectableElements.prototype.getPrevious = function (el) {
	var p = el.previousSibling;
	if (p == null || this.isItem(p))
		return p;
	return this.getPrevious(p);
};

SelectableElements.prototype.isBefore = function (n1, n2) {
	var next = this.getNext(n1);
	while (next != null) {
		if (next == n2)
			return true;
		next = this.getNext(next);
	}
	return false;
};

/* End Traversable Collection Interface */

/* Indexable Collection Interface */

SelectableElements.prototype.getItems = function () {
	var tmp = [];
	var j = 0;
	var cs = this._htmlElement.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		if (cs[i].nodeType == 1)
			tmp[j++] = cs[i]
	}
	return tmp;
};

SelectableElements.prototype.getItem = function (nIndex) {
	var j = 0;
	var cs = this._htmlElement.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		if (cs[i].nodeType == 1) {
			if (j == nIndex)
				return cs[i];
			j++;
		}
	}
	return null;
};

SelectableElements.prototype.getSelectedIndexes = function () {
	var items = this.getSelectedItems();
	var l = items.length;
	var tmp = new Array(l);
	for (var i = 0; i < l; i++)
		tmp[i] = this.getItemIndex(items[i]);
	return tmp;
};


SelectableElements.prototype.getItemIndex = function (el) {
	var j = 0;
	var cs = this._htmlElement.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		if (cs[i] == el)
			return j;
		if (cs[i].nodeType == 1)
			j++;
	}
	return -1;
};

/* End Indexable Collection Interface */



function addClassName(el, sClassName) {
	var s = el.className;
	var p = s.split(" ");
	if (p.length == 1 && p[0] == "")
		p = [];

	var l = p.length;
	for (var i = 0; i < l; i++) {
		if (p[i] == sClassName)
			return;
	}
	p[p.length] = sClassName;
	el.className = p.join(" ");
}

function removeClassName(el, sClassName) {
	var s = el.className;
	var p = s.split(" ");
	var np = [];
	var l = p.length;
	var j = 0;
	for (var i = 0; i < l; i++) {
		if (p[i] != sClassName)
			np[j++] = p[i];
	}
	el.className = np.join(" ");
}
function filterTestsForBrowser() {
    var testTables = document.getElementsByTagName("table");
    for (var i = 0; i < testTables.length; i++)
    {
        filterTestTableForBrowser(testTables[i]);
    }
}

function filterTestTableForBrowser(testTable)
{
    for(rowNum = testTable.rows.length - 1; rowNum >= 0; rowNum--)
    {
        var row = testTable.rows[rowNum];
        var filterString = row.getAttribute("if");
        if (filterString && !eval(filterString))
        {
          testTable.deleteRow(rowNum)
        }
    }
}
function filterTestsForBrowser() {
    var testTables = document.getElementsByTagName("table");
    for (var i = 0; i < testTables.length; i++)
    {
        filterTestTableForBrowser(testTables[i]);
    }
}

function filterTestTableForBrowser(testTable)
{
    for(rowNum = testTable.rows.length - 1; rowNum >= 0; rowNum--)
    {
        var row = testTable.rows[rowNum];
        var filterString = row.getAttribute("if");
        if (filterString && !eval(filterString))
        {
          testTable.deleteRow(rowNum)
        }
    }
}

window.onload=filterTestsForBrowser;

window.onload=filterTestsForBrowser;
function filter2 (phrase, _id){
	var words = phrase.value.toLowerCase().split(" ");
	var table = document.getElementById(_id);
	var ele;
	for (var r = 1; r < table.rows.length; r++){
		ele = table.rows[r].innerHTML.replace(/<[^>]+>/g,"");
	        var displayStyle = 'none';
	        for (var i = 0; i < words.length; i++) {
		    if (ele.toLowerCase().indexOf(words[i])>=0)
			displayStyle = '';
		    else {
			displayStyle = 'none';
			break;
		    }
	        }
		table.rows[r].style.display = displayStyle;
	}
}
function filter (term, _id, cellNr){
	var suche = term.value.toLowerCase();
	var table = document.getElementById(_id);
	var ele;
	for (var r = 1; r < table.rows.length; r++){
		ele = table.rows[r].cells[cellNr].innerHTML.replace(/<[^>]+>/g,"");
		if (ele.toLowerCase().indexOf(suche)>=0 )
			table.rows[r].style.display = '';
		else table.rows[r].style.display = 'none';
	}
}

<cfset var tmpHolder = "" />

<cfloop from="1" to="100" index="idx">
   <cfset tmpHolder=createObject("component","testChild").init() />
   <cfset arrayAppend(arrayToReturn,tmpHolder) />
</cfloop>
function filter( list, if_func )
    {
    var filtered_list = []
    for ( var i = 0; i < list.length; ++i )
        {
        var x = list[i]
        if ( null == if_func || if_func( x ) ) 
            filtered_list.push( x )
        }
    return filtered_list;
    };

 return { filter: filter };

} )
function reduce( op, list, init )
        {
        var r = init;
        for ( var i = 0; i < list.length; ++i )
            r = op( r, list[i] );
        return r;
        }

    return {reduce: reduce};
} ); [/code]
I reported the bug adviced by the admin at mozilla general firefox user forum.
The code given here is the actual solution to the bug, we had to create the new bug to present the solution. Can you affirm that adding this code SelectElementsTable.js to the componets folder. The load performance is considerably faster as was reported by many. This is especially important for users in Australia and New Zealand where they have broadband infrastructure problems, but broadband users can also benefit from bringing this code into the firefox components folder.

polonus
i can confirm this major speed improvement on trunk
I cannot confirm this works on today's trunk.  I had errors in Console2 showing it did not start. 

Failed to load XPCOM component: C:\Program Files\Minefield\components\SelectableElementsTable.js 

Error: missing = in XML attribute 
Source file: file:///C:/Program%20Files/Minefield/components/SelectableElementsTable.js 
Line: 404, Column: 11 
Source code: 
<cfset> 

Added that code as an attachment because some people might accidentally copy the [code] [/code] tags at the beginning and end of the code.
I can't confirm this because CNN seems to load faster but most like foxnews, msnbc, neowin.net, usaa.com, digg.com seemed about the same.
The attachment still contains <cfset> and other coldfusion tags that aren't right for a javascript file.

Can I ask what you expect this code to do. The code doesn't contain a valid component so just sticking it in a js file in the app's components directory will have no effect whatsoever. I'm afraid I also cannot understand your explanation of the problem that this is trying to solve.
The forum gives no further technical details. The suggestion is that you are speeding up getElementsById and getElementsByTagName yet the javascript file appears to do nothing with these methods.

Here is what the attached javascript does:

Creates a js class SelectableElements that is never used anywhere in the code.
Creates some functions (addClassName, removeClassName) that are only used by the SelectableElements class.
The functions filterTestsForBrowser and filterTestTableForBrowser are created twice which I imagine would throw a JS error.
Attempts to (twice) assign filterTestTableForBrowser to window.onload, which should fail since there is no "window" in a component's scope.
Creates a filter2 and filter function that are never used anywhere.
Includes some extra coldfusion tags that the JS parser should fail on.
Creates a second filter function that isn't used anywhere.
Includes some trailing junk script that the parser should fail on.
Creates a reduce function that isn't used anywhere.

At no point does the code include an NSGetModule which is what would be necessary for this code to do any kind of interaction with any part of Firefox outside of it's own scope.
Hi Dave,

But when I have the x-javascript inside FF, Firebug gives strict-Error-checking = true
QueryInterface () gives Object
Did not code this for snake-oil, there must be something in it from where
to start,

polonus

Running inside a webpage is very different to running as a component. I'm afraid I don't really follow the rest of your comment though.
(In reply to comment #9)
> Did not code this for snake-oil, there must be something in it from where
> to start,
Except you didn't code most of it at all... I'm pretty sure Erik wouldn't appreciate seeing his license block chopped off his code and claimed as someone else's work.

Most of this script is part of Erik Arvidsson's WebFX library and is designed as a cross-browser (hence the IE-specific appendElement() in the first block) implementation of just as the function name implies: selectable elements.

From SelectableElements' constructor you can see that it takes two arguments: an HTMLElement (containing children which this script makes selectable) and a boolean (specifying whether or not to allow multiple selections) from there the code responds to clicks and click+shift/ctrl (for multiple selection) on the parent element's children and sets the selected children(s) class to "selected" (or removes it if already selected).

The code following Erik's, if it were free of all the things Mossop pointed out in comment 8 (minus the dead code) would act only on tables and delete rows with an 'if' attribute that eval()s (yuck) to 'false'.

Basically this script:
- Doesn't parse.
- Even if it did parse, it wouldn't load as a component.
- Even if it did load as a component, it wouldn't touch content.
- Even if it did touch content, it wouldn't do anything because it's not actually used anywhere.

This is absolutely invalid. Feel free to reopen with a patch that actually does something and evidence of such.
Status: UNCONFIRMED → RESOLVED
Closed: 17 years ago
Resolution: --- → INVALID
So the verdict fell, leave it at that, the code was redone from public source with the best of intentions, and if it is inactive, I leave it untouched, Thanks for looking into it,

polonus
Status: RESOLVED → VERIFIED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: