diff --git a/tracks/app/views/context/_context.rhtml b/tracks/app/views/context/_context.rhtml index 4fc2328a..b3f58962 100644 --- a/tracks/app/views/context/_context.rhtml +++ b/tracks/app/views/context/_context.rhtml @@ -2,7 +2,7 @@

<% if collapsible -%> - <%= image_tag("collapse.png") %> + <%= image_tag("collapse.png") %> <% end -%> <%= link_to( sanitize("#{context.name}"), { :controller => "context", :action => "show", :name => urlize(context.name) }, { :title => "Go to the #{context.name} context page" } ) %>

diff --git a/tracks/app/views/layouts/standard.rhtml b/tracks/app/views/layouts/standard.rhtml index 7bdb7894..1fc2901a 100644 --- a/tracks/app/views/layouts/standard.rhtml +++ b/tracks/app/views/layouts/standard.rhtml @@ -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" %> diff --git a/tracks/app/views/project/_project.rhtml b/tracks/app/views/project/_project.rhtml index 9fbd4121..f1898682 100644 --- a/tracks/app/views/project/_project.rhtml +++ b/tracks/app/views/project/_project.rhtml @@ -3,7 +3,7 @@

<% if collapsible %> - <%= image_tag("collapse.png") %> + <%= image_tag("collapse.png") %> <% end %> <%= sanitize("#{project.name}") %>

diff --git a/tracks/app/views/todo/_completed.rhtml b/tracks/app/views/todo/_completed.rhtml index 56a92e1a..db0df5b4 100644 --- a/tracks/app/views/todo/_completed.rhtml +++ b/tracks/app/views/todo/_completed.rhtml @@ -1,8 +1,8 @@ <% suffix = append_descriptor ? append_descriptor : '' -%> -
+

<% if collapsible %> - <%= image_tag("collapse.png") %> + <%= image_tag("collapse.png") %> <% end %> Completed actions <%= append_descriptor ? append_descriptor : '' %>

diff --git a/tracks/public/javascripts/application.js b/tracks/public/javascripts/application.js index ad53d973..40164b21 100644 --- a/tracks/public/javascripts/application.js +++ b/tracks/public/javascripts/application.js @@ -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); diff --git a/tracks/public/javascripts/selector-addon-v1.js b/tracks/public/javascripts/selector-addon-v1.js new file mode 100644 index 00000000..796f8777 --- /dev/null +++ b/tracks/public/javascripts/selector-addon-v1.js @@ -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 + * + * 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(); +} diff --git a/tracks/public/javascripts/todo-items.js b/tracks/public/javascripts/todo-items.js index ae50494a..12e0d92f 100644 --- a/tracks/public/javascripts/todo-items.js +++ b/tracks/public/javascripts/todo-items.js @@ -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; } }