Major javascript performance improvements.

git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@281 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2006-07-13 04:50:26 +00:00
parent ef42421f64
commit 13f7f443af
7 changed files with 196 additions and 28 deletions

View file

@ -2,7 +2,7 @@
<div id="c<%= context.id %>" class="container context">
<h2>
<% if collapsible -%>
<a href="#" class="container_toggle"><%= image_tag("collapse.png") %></a>
<a href="#" class="container_toggle" id="toggle_c<%= context.id %>"><%= image_tag("collapse.png") %></a>
<% end -%>
<%= link_to( sanitize("#{context.name}"), { :controller => "context", :action => "show", :name => urlize(context.name) }, { :title => "Go to the #{context.name} context page" } ) %>
</h2>

View file

@ -6,6 +6,7 @@
<%= stylesheet_link_tag "print", :media => "print" %>
<%= javascript_include_tag "toggle_notes" %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag "selector-addon-v1" %>
<%= stylesheet_link_tag 'calendar-system.css' %>
<%= javascript_include_tag 'calendar', 'calendar-en', 'calendar-setup' %>
<%= javascript_include_tag "accesskey-hints" %>

View file

@ -3,7 +3,7 @@
<div id="p<%= project.id %>" class="container project">
<h2>
<% if collapsible %>
<a href="#" class="container_toggle"><%= image_tag("collapse.png") %></a>
<a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("collapse.png") %></a>
<% end %>
<%= sanitize("#{project.name}") %>
</h2>

View file

@ -1,8 +1,8 @@
<% suffix = append_descriptor ? append_descriptor : '' -%>
<div class="container completed">
<div class="container completed" id="completed_container">
<h2>
<% if collapsible %>
<a href="#" class="container_toggle"><%= image_tag("collapse.png") %></a>
<a href="#" class="container_toggle" id="toggle_completed"><%= image_tag("collapse.png") %></a>
<% end %>
Completed actions <%= append_descriptor ? append_descriptor : '' %>
</h2>

View file

@ -6,7 +6,8 @@ Ajax.Responders.register({
onComplete: function() {
if($('busy') && Ajax.activeRequestCount==0)
Element.hide('busy');
},
}
//,
// uncomment the next three lines for easier debugging with FireBug
// onException: function(source, exception) {
// console.error(exception);

View file

@ -0,0 +1,163 @@
/************************************
*
* An add-on to Prototype 1.5 to speed up the $$ function in usual cases.
*
* http://www.sylvainzimmer.com/index.php/archives/2006/06/25/speeding-up-prototypes-selector/
*
* Authors:
* - Sylvain ZIMMER <sylvain _at_ jamendo.com>
*
* Changelog:
* v1 (2006/06/25)
* - Initial release
*
* License: AS-IS
*
* Trivia: Check out www.jamendo.com for some great Creative Commons music ;-)
*
************************************/
// We don't extend the Selector class because we want
// to be able to use it if the expression is too complicated.
var SelectorLiteAddon=Class.create();
SelectorLiteAddon.prototype = {
// This is the constructor. It parses the stack of selectors.
initialize: function(stack) {
this.r=[]; //results
this.s=[]; //stack of selectors
this.i=0; //stack pointer
//Parse the selectors
for (var i=stack.length-1;i>=0;i--) {
//This is the parsed selector. Format is : [tagname, id, classnames]
var s=["*","",[]];
//The unparsed current selector
var t=stack[i];
//Parse the selector backwards
var cursor=t.length-1;
do {
var d=t.lastIndexOf("#");
var p=t.lastIndexOf(".");
cursor=Math.max(d,p);
//Found a tagName
if (cursor==-1) {
s[0]=t.toUpperCase();
//Found a className
} else if (d==-1 || p==cursor) {
s[2].push(t.substring(p+1));
//Found an ID
} else if (!s[1]) {
s[1]=t.substring(d+1);
}
t=t.substring(0,cursor);
} while (cursor>0);
this.s[i]=s;
}
},
//Returns a list of matched elements below a given root.
get:function(root) {
this.explore(root || document,this.i==(this.s.length-1));
return this.r;
},
//Recursive function where the actual search is being done.
// elt: current root element
// leaf: boolean, are we in a leaf of the search tree?
explore:function(elt,leaf) {
//Parsed selector
var s=this.s[this.i];
//Results
var r=[];
//Selector has an ID, use it!
if (s[1]) {
e=$(s[1]);
if (e && (s[0]=="*" || e.tagName==s[0]) && e.childOf(elt)) {
r=[e];
}
//Selector has no ID, search by tagname.
} else {
r=$A(elt.getElementsByTagName(s[0]));
}
//Filter the results by classnames.
//Todo: by attributes too?
//Sidenote: The performance hit is often here.
//Single className : that's fast!
if (s[2].length==1) { //single classname
r=r.findAll(function(o) {
//If the element has only one classname too, the test is simple!
if (o.className.indexOf(" ")==-1) {
return o.className==s[2][0];
} else {
return o.className.split(/\s+/).include(s[2][0]);
}
});
//Multipe classNames, a bit slower.
} else if (s[2].length>0) {
r=r.findAll(function(o) {
//If the elemtn has only one classname, we can drop it.
if (o.className.indexOf(" ")==-1) {
return false;
} else {
//Check that all required classnames are present.
var q=o.className.split(/\s+/);
return s[2].all(function(c) {
return q.include(c);
});
}
});
}
//Append the results if we're in a leaf
if (leaf) {
this.r=this.r.concat(r);
//Continue exploring the tree otherwise
} else {
++this.i;
r.each(function(o) {
this.explore(o,this.i==(this.s.length-1));
}.bind(this));
}
}
}
//Overwrite the $$ function.
var $$old=$$;
var $$=function(a,b) {
//expression is too complicated, forward the call to prototype's function!
if (b || a.indexOf("[")>=0) return $$old.apply(this,arguments);
//Otherwise use our addon!
return new SelectorLiteAddon(a.split(/\s+/)).get();
}

View file

@ -13,26 +13,36 @@ ToDoItems.prototype = {
initialize: function()
{
this.initialized = true;
this.contextCollapseCookieManager = new CookieManager();
this.toggleItemsMap = {};
this.toggleContainerMap = {};
this.containerItemsMap = {};
},
addNextActionListingToggles: function()
{
this.containerToggles = $$('.container_toggle');
containerTogglesClick = this.toggleNextActionListing.bindAsEventListener(this);
for(i=0; i < this.containerToggles.length; i++)
{
Event.observe(this.containerToggles[i], 'click', this.toggleNextActionListing.bindAsEventListener(this));
toggleElem = this.containerToggles[i];
containerElem = toggleElem.parentNode.parentNode
itemsElem = document.getElementsByClassName('toggle_target',containerElem)[0];
this.toggleContainerMap[toggleElem.id] = containerElem;
this.toggleItemsMap[toggleElem.id] = itemsElem;
this.containerItemsMap[containerElem.id] = itemsElem;
Event.observe(this.containerToggles[i], 'click', containerTogglesClick);
this.containerToggles[i].onclick = function() {return false;}; //workaround for Event.stop problem with Safari 2.0.3. See http://particletree.com/notebook/eventstop/
}
this.setNextActionListingTogglesToCookiedState();
},
setNextActionListingTogglesToCookiedState: function()
{
contextCollapseCookieManager = new CookieManager();
for(i=0; i < this.containerToggles.length; i++)
{
toggleElem = this.containerToggles[i];
containerElem = this.findNearestParentByClassName(toggleElem, "container");
collapsedCookie = contextCollapseCookieManager.getCookie(this.buildCookieName(containerElem));
itemsElem = this.findItemsElem(toggleElem);
containerElem = this.toggleContainerMap[toggleElem.id];
collapsedCookie = this.contextCollapseCookieManager.getCookie(this.buildCookieName(containerElem));
itemsElem = this.toggleItemsMap[toggleElem.id];
isExpanded = Element.visible(itemsElem);
if (collapsedCookie && isExpanded)
{
@ -50,11 +60,13 @@ ToDoItems.prototype = {
{
toggleElem = this.containerToggles[i];
if (toggleElem != except)
itemsElem = this.findItemsElem(toggleElem);
isExpanded = Element.visible(itemsElem);
if (isExpanded)
{
this.collapseNextActionListing(toggleElem, itemsElem);
itemsElem = this.toggleItemsMap[toggleElem.id];
isExpanded = Element.visible(itemsElem);
if (isExpanded)
{
this.collapseNextActionListing(toggleElem, itemsElem);
}
}
}
},
@ -76,17 +88,17 @@ ToDoItems.prototype = {
{
Event.stop(event);
toggleElem = Event.element(event).parentNode;
itemsElem = this.findItemsElem(toggleElem);
containerElem = this.findNearestParentByClassName(toggleElem, "container");
itemsElem = this.toggleItemsMap[toggleElem.id];
containerElem = this.toggleContainerMap[toggleElem.id];
if (Element.visible(itemsElem))
{
this.collapseNextActionListing(toggleElem, itemsElem);
contextCollapseCookieManager.setCookie(this.buildCookieName(containerElem), true)
this.contextCollapseCookieManager.setCookie(this.buildCookieName(containerElem), true)
}
else
{
this.expandNextActionListing(toggleElem, itemsElem);
contextCollapseCookieManager.clearCookie(this.buildCookieName(containerElem))
this.contextCollapseCookieManager.clearCookie(this.buildCookieName(containerElem))
}
},
findToggleElemForContext : function(contextElem)
@ -136,7 +148,7 @@ ToDoItems.prototype = {
},
buildCookieName: function(containerElem)
{
tracks_login = contextCollapseCookieManager.getCookie('tracks_login');
tracks_login = this.contextCollapseCookieManager.getCookie('tracks_login');
return 'tracks_'+tracks_login+'_context_' + containerElem.id + '_collapsed';
},
@ -152,15 +164,6 @@ ToDoItems.prototype = {
parentElem = parentElem.parentNode;
}
return null;
},
findItemsElem : function(elem)
{
var containerElem = this.findNearestParentByClassName(elem, "container");
if (containerElem)
return document.getElementsByClassName('toggle_target',containerElem)[0];
else
return null;
}
}