diff --git a/tracks/app/controllers/context_controller.rb b/tracks/app/controllers/context_controller.rb index 976be1bc..b36dcf18 100644 --- a/tracks/app/controllers/context_controller.rb +++ b/tracks/app/controllers/context_controller.rb @@ -24,12 +24,11 @@ class ContextController < ApplicationController # e.g. /project/show/ shows just . # def show - self.init - self.init_todos - self.check_user_set_context + init + init_todos @page_title = "TRACKS::Context: #{@context.name}" end - + # Creates a new context via Ajax helpers # def new_context @@ -58,6 +57,19 @@ class ContextController < ApplicationController render :text => "" end end + + # Toggles the 'done' status of the action + # + def toggle_check + self.init + + item = check_user_return_item + item.toggle!('done') + item.completed = Time.now () # For some reason, the before_save in todo.rb stopped working + if item.save + render :partial => 'context/show_items', :object => item + end + end # Fairly self-explanatory; deletes the context # If the context contains actions, you'll get a warning dialogue. @@ -84,6 +96,16 @@ class ContextController < ApplicationController end protected + def check_user_return_item + item = Todo.find( @params['id'] ) + if @session['user'] == item.user + return item + else + flash["warning"] = "Item and session user mis-match: #{item.user.name} and #{@session['user'].name}!" + render_text "" + end + end + def check_user_set_context @user = @session['user'] diff --git a/tracks/app/controllers/project_controller.rb b/tracks/app/controllers/project_controller.rb index d4375d0e..e480125e 100644 --- a/tracks/app/controllers/project_controller.rb +++ b/tracks/app/controllers/project_controller.rb @@ -56,6 +56,19 @@ class ProjectController < ApplicationController render_text "" end end + + # Toggles the 'done' status of the action + # + def toggle_check + self.init + + item = check_user_return_item + item.toggle!('done') + item.completed = Time.now () # For some reason, the before_save in todo.rb stopped working + if item.save + render :partial => 'project/show_items', :object => item + end + end # Delete a project # @@ -81,6 +94,16 @@ class ProjectController < ApplicationController end protected + + def check_user_return_item + item = Todo.find( @params['id'] ) + if @session['user'] == item.user + return item + else + flash["warning"] = "Item and session user mis-match: #{item.user.name} and #{@session['user'].name}!" + render_text "" + end + end def check_user_set_project @user = @session['user'] diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb index 0a6068a9..804cf4b4 100644 --- a/tracks/app/controllers/todo_controller.rb +++ b/tracks/app/controllers/todo_controller.rb @@ -69,7 +69,7 @@ class TodoController < ApplicationController else item.due = "" end - + if item.save render :partial => 'item', :object => item, :project => @params["project"] else @@ -119,7 +119,10 @@ class TodoController < ApplicationController item = check_user_return_item item.toggle!('done') - render :partial => 'item', :object => item + item.completed = Time.now () # For some reason, the before_save in todo.rb stopped working + if item.save + render :partial => 'item', :object => item + end end protected diff --git a/tracks/app/helpers/application_helper.rb b/tracks/app/helpers/application_helper.rb index 018eb0b1..00dd1064 100644 --- a/tracks/app/helpers/application_helper.rb +++ b/tracks/app/helpers/application_helper.rb @@ -86,4 +86,5 @@ module ApplicationHelper str << ",step:1,inputField:\"" + input_field + "\",cache:true,align:\"TR\" })" javascript_tag str end + end diff --git a/tracks/app/models/todo.rb b/tracks/app/models/todo.rb index ae86b38b..22a65799 100644 --- a/tracks/app/models/todo.rb +++ b/tracks/app/models/todo.rb @@ -3,7 +3,7 @@ class Todo < ActiveRecord::Base belongs_to :context, :order => 'name' belongs_to :project belongs_to :user - + attr_protected :user # Description field can't be empty, and must be < 100 bytes @@ -12,16 +12,6 @@ class Todo < ActiveRecord::Base validates_length_of :description, :maximum => 100 validates_length_of :notes, :maximum => 60000 - # Add a creation date (Ruby object format) to item before it's saved - # if there is no existing creation date (this prevents creation date - # being reset to completion date when item is completed) - # - def before_save - if self.done == 1 - self.completed = Time.now() - end - end - def self.not_done( id=id ) self.find(:all, :conditions =>[ "done = 0 AND context_id = ?", id], \ :order =>"due IS NULL, due ASC, created_at ASC") diff --git a/tracks/app/views/context/_empty.rhtml b/tracks/app/views/context/_empty.rhtml new file mode 100644 index 00000000..f92ff187 --- /dev/null +++ b/tracks/app/views/context/_empty.rhtml @@ -0,0 +1 @@ +

<%= message %>

\ No newline at end of file diff --git a/tracks/app/views/context/_show_items.rhtml b/tracks/app/views/context/_show_items.rhtml index 0ae0e2ed..ec8f6485 100644 --- a/tracks/app/views/context/_show_items.rhtml +++ b/tracks/app/views/context/_show_items.rhtml @@ -1,12 +1,13 @@ <% item = show_items %> + <% if !item.done? %>
-<%= form_remote_tag( :url => url_for( :controller => "todo", :action => "toggle_check", :id => item.id ), +<%= form_remote_tag( :url => url_for( :controller => "context", :action => "toggle_check", :id => item.id ), :html => { :id=> "checkbox-notdone-#{item.id}", :class => "inline-form" }, :update => "completed", :position => "top", :loading => "Form.disable('checkbox-notdone-#{item.id}');", - :complete => "new Effect.Fade('item-#{item.id}-container', true);" + :complete => visual_effect(:fade, "item-#{item.id}-container") ) %>
@@ -14,7 +15,7 @@ <%= link_to_remote( image_tag("blank", :title =>"Delete this action", :class=>"delete_item"), :update => "item-#{item.id}-container", - :loading => "new Effect.Fade('item-#{item.id}-container', true)", + :loading => visual_effect(:fade, "item-#{item.id}-container"), :url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " + link_to_function(image_tag( "blank", :title => "Edit item", :class=>"edit_item"), "Element.toggle('item-#{item.id}','action-#{item.id}-edit-form'); new Effect.Appear('action-#{item.id}-edit-form'); Form.focusFirstElement('form-action-#{item.id}');" ) + " " %> @@ -48,20 +49,21 @@ <%= form_remote_tag :url => { :controller => 'todo', :action => 'update_action', :id => item.id }, :html => { :id => "form-action-#{item.id}", :class => "inline-form" }, :update => "item-#{item.id}-container", - :complete => "new Effect.Appear('item-#{item.id}-container');" %> - <%= render_partial 'todo/action_edit_form', item %> + :complete => visual_effect(:appear, "item-#{item.id}-container") %> + + <%= render :partial => 'todo/action_edit_form', :object => item %> <%= end_form_tag %>
<% else %>
-<%= form_remote_tag( :url => url_for( :controller => "todo", :action => "toggle_check", :id => item.id ), +<%= form_remote_tag( :url => url_for( :controller => "context", :action => "toggle_check", :id => item.id ), :html => { :id=> "checkbox-done-#{item.id}", :class => "inline-form" }, :update => "next_actions", :position => "bottom", :loading => "Form.disable('checkbox-done-#{item.id}');", - :complete => "new Effect.Fade('done-item-#{item.id}-container', true);" + :complete => visual_effect(:fade, "done-item-#{item.id}-container") ) %>
@@ -69,7 +71,7 @@ <%= link_to_remote( image_tag("blank", :title =>"Delete this action", :class=>"delete_item"), :update => "done-item-#{item.id}-container", - :loading => "new Effect.Fade('done-item-#{item.id}-container', true)", + :loading => visual_effect(:fade, "done-item-#{item.id}-container"), :url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " %> <%= image_tag("blank") %> diff --git a/tracks/app/views/context/show.rhtml b/tracks/app/views/context/show.rhtml index bafd2b5c..9186318d 100644 --- a/tracks/app/views/context/show.rhtml +++ b/tracks/app/views/context/show.rhtml @@ -4,13 +4,18 @@

<%= sanitize(@context.name) %>

- <% if @not_done.empty? %> -

There are no next actions yet in this context

- <% else %> - <% for item in @not_done %> - <%= render_partial "show_items", item %> - <% end %> - <% end %> + <% if @not_done.empty? -%> +
+ <%= render :partial => "empty", + :locals => { :message => "There are currently no uncompleted actions in this context"} %> +
+ <% else -%> + + <% end -%> + <%= render :partial => "show_items", :collection => @not_done %>
@@ -19,12 +24,17 @@
<% if @done.empty? %> -

There are no completed next actions yet in this context

- <% else %> - <% for done_item in @done %> - <%= render_partial "show_items", done_item %> - <% end %> - <% end %> +
+ <%= render :partial => "empty", + :locals => {:message => "There are currently no completed next actions in this context"} %> +
+ <% else -%> + + <% end -%> + <%= render :partial => "show_items", :collection => @done %>
diff --git a/tracks/app/views/project/_show_items.rhtml b/tracks/app/views/project/_show_items.rhtml index bdb7ea81..211c5470 100644 --- a/tracks/app/views/project/_show_items.rhtml +++ b/tracks/app/views/project/_show_items.rhtml @@ -1,12 +1,12 @@ <% item = show_items %> <% if !item.done? %>
-<%= form_remote_tag( :url => url_for( :controller => "todo", :action => "toggle_check", :id => item.id ), +<%= form_remote_tag( :url => url_for( :controller => "project", :action => "toggle_check", :id => item.id ), :html => { :id=> "checkbox-notdone-#{item.id}", :class => "inline-form" }, :update => "completed", :position => "top", :loading => "Form.disable('checkbox-notdone-#{item.id}');", - :complete => "new Effect.Fade('item-#{item.id}-container', true);" + :complete => visual_effect(:fade, "item-#{item.id}-container") ) %>
@@ -14,9 +14,9 @@ <%= link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"), :update => "item-#{item.id}-container", - :loading => "new Effect.Fade('item-#{item.id}-container', true)", + :loading => visual_effect(:fade, "item-#{item.id}-container"), :url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " + - link_to_function(image_tag( "blank", :title => "Edit action", :class=>"edit_item"), "Element.toggle('item-#{item.id}','action-#{item.id}-edit-form'); new Effect.Appear('action-#{item.id}-edit-form'); Form.focusFirstElement('form-action-#{item.id}');" ) + " " + link_to_function(image_tag( "blank", :title => "Edit action", :class => "edit_item"), "Element.toggle('item-#{item.id}','action-#{item.id}-edit-form'); new Effect.Appear('action-#{item.id}-edit-form'); Form.focusFirstElement('form-action-#{item.id}');" ) + " " %>
@@ -49,7 +49,7 @@ <%= form_remote_tag :url => { :controller => 'todo', :action => 'update_action', :id => item.id }, :html => { :id => "form-action-#{item.id}", :class => "inline-form" }, :update => "item-#{item.id}-container", - :complete => "new Effect.Appear('item-#{item.id}-container', true);" %> + :complete => visual_effect(:appear, "item-#{item.id}-container") %> <%= render_partial 'todo/action_edit_form', item %> <%= end_form_tag %>
@@ -57,12 +57,12 @@ <% else %>
-<%= form_remote_tag( :url => url_for( :controller => "todo", :action => "toggle_check", :id => item.id ), +<%= form_remote_tag( :url => url_for( :controller => "project", :action => "toggle_check", :id => item.id ), :html => { :id=> "checkbox-done-#{item.id}", :class => "inline-form" }, :update => "next_actions", :position => "bottom", :loading => "Form.disable('checkbox-done-#{item.id}');", - :complete => "new Effect.Fade('done-item-#{item.id}-container', true);" + :complete => visual_effect(:fade, "done-item-#{item.id}-container") ) %>
@@ -70,7 +70,7 @@ <%= link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"), :update => "done-item-#{item.id}-container", - :loading => "new Effect.Fade('done-item-#{item.id}-container', true)", + :loading => visual_effect(:fade, "done-item-#{item.id}-container"), :url => { :controller => "todo", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " %>
diff --git a/tracks/app/views/project/show.rhtml b/tracks/app/views/project/show.rhtml index 68496e35..32225085 100644 --- a/tracks/app/views/project/show.rhtml +++ b/tracks/app/views/project/show.rhtml @@ -9,9 +9,7 @@
<% if @project.done == 1 -%>

This project has been completed

- <% for item in @not_done -%> - <%= render_partial "show_items", item %> - <% end -%> + <%= render :partial => "show_items", :collection => @not_done %> <% elsif @not_done.empty? -%>

There are no next actions yet in this project

<% else -%> diff --git a/tracks/app/views/shared/add_new_item_form.rhtml b/tracks/app/views/shared/add_new_item_form.rhtml index 4a216ea5..0cf62e84 100644 --- a/tracks/app/views/shared/add_new_item_form.rhtml +++ b/tracks/app/views/shared/add_new_item_form.rhtml @@ -14,7 +14,7 @@ <%= link_to_function( add_string, - "Element.toggle('todo_new_action');Form.focusFirstElement('todo-form-new-action');Element.toggle('new_actions');", +"Element.toggle('todo_new_action');Form.focusFirstElement('todo-form-new-action');Element.toggle('new_actions');", {:title => "Add the next action", :accesskey => "n"}) %>

Older completed items: <%= link_to( "Older than 31 days", :controller => "todo", :action => "completed_archive" ) %>

-
+
diff --git a/tracks/public/javascripts/controls.js b/tracks/public/javascripts/controls.js index a7436bcf..6da58854 100644 --- a/tracks/public/javascripts/controls.js +++ b/tracks/public/javascripts/controls.js @@ -184,7 +184,10 @@ Autocompleter.Base.prototype = { this.show(); this.active = true; } - } else this.hide(); + } else { + this.active = false; + this.hide(); + } }, markPrevious: function() { @@ -425,6 +428,15 @@ Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { // // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { + setTimeout(function() { + Field.activate(field); + }, 1); +} + Ajax.InPlaceEditor = Class.create(); Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; Ajax.InPlaceEditor.prototype = { @@ -490,7 +502,7 @@ Ajax.InPlaceEditor.prototype = { Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); } }, - enterEditMode: function() { + enterEditMode: function(evt) { if (this.saving) return; if (this.editing) return; this.editing = true; @@ -501,11 +513,12 @@ Ajax.InPlaceEditor.prototype = { Element.hide(this.element); this.createForm(); this.element.parentNode.insertBefore(this.form, this.element); - Field.focus(this.editField); + Field.scrollFreeActivate(this.editField); // stop the event to avoid a page refresh in Safari - if (arguments.length > 1) { - Event.stop(arguments[0]); + if (evt) { + Event.stop(evt); } + return false; }, createForm: function() { this.form = document.createElement("form"); diff --git a/tracks/public/javascripts/dragdrop.js b/tracks/public/javascripts/dragdrop.js index 5445d748..63a68243 100644 --- a/tracks/public/javascripts/dragdrop.js +++ b/tracks/public/javascripts/dragdrop.js @@ -280,7 +280,7 @@ Draggable.prototype = { style.position = "relative"; if(this.options.zindex) { - this.options.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); style.zIndex = this.options.zindex; } @@ -355,8 +355,8 @@ var Sortable = { hoverclass: null, ghosting: false, format: null, - onChange: function() {}, - onUpdate: function() {} + onChange: Prototype.emptyFunction, + onUpdate: Prototype.emptyFunction }, arguments[1] || {}); // clear any old sortable with same element @@ -472,7 +472,10 @@ var Sortable = { onEmptyHover: function(element, dropon) { if(element.parentNode!=dropon) { + var oldParentNode = element.parentNode; dropon.appendChild(element); + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon).onChange(element); } }, diff --git a/tracks/public/javascripts/effects.js b/tracks/public/javascripts/effects.js index 7e65d922..3f929920 100644 --- a/tracks/public/javascripts/effects.js +++ b/tracks/public/javascripts/effects.js @@ -4,296 +4,181 @@ // Mark Pilgrim (http://diveintomark.org/) // Martin Bialasinki // -// See scriptaculous.js for full license. +// See scriptaculous.js for full license. -Object.debug = function(obj) { - var info = []; - - if(typeof obj in ["string","number"]) { - return obj; - } else { - for(property in obj) - if(typeof obj[property]!="function") - info.push(property + ' => ' + - (typeof obj[property] == "string" ? - '"' + obj[property] + '"' : - obj[property])); - } - - return ("'" + obj + "' #" + typeof obj + - ": {" + info.join(", ") + "}"); -} - - -/*--------------------------------------------------------------------------*/ - -var Builder = { - NODEMAP: { - AREA: 'map', - CAPTION: 'table', - COL: 'table', - COLGROUP: 'table', - LEGEND: 'fieldset', - OPTGROUP: 'select', - OPTION: 'select', - PARAM: 'object', - TBODY: 'table', - TD: 'table', - TFOOT: 'table', - TH: 'table', - THEAD: 'table', - TR: 'table' - }, - // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, - // due to a Firefox bug - node: function(elementName) { - elementName = elementName.toUpperCase(); - - // try innerHTML approach - var parentTag = this.NODEMAP[elementName] || 'div'; - var parentElement = document.createElement(parentTag); - parentElement.innerHTML = "<" + elementName + ">"; - var element = parentElement.firstChild || null; - - // see if browser added wrapping tags - if(element && (element.tagName != elementName)) - element = element.getElementsByTagName(elementName)[0]; - - // fallback to createElement approach - if(!element) element = document.createElement(elementName); - - // abort if nothing could be created - if(!element) return; - - // attributes (or text) - if(arguments[1]) - if(this._isStringOrNumber(arguments[1]) || - (arguments[1] instanceof Array)) { - this._children(element, arguments[1]); - } else { - var attrs = this._attributes(arguments[1]); - if(attrs.length) { - parentElement.innerHTML = "<" +elementName + " " + - attrs + ">"; - element = parentElement.firstChild || null; - // workaround firefox 1.0.X bug - if(!element) { - element = document.createElement(elementName); - for(attr in arguments[1]) - element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; - } - if(element.tagName != elementName) - element = parentElement.getElementsByTagName(elementName)[0]; - } - } - - // text, or array of children - if(arguments[2]) - this._children(element, arguments[2]); - - return element; - }, - _text: function(text) { - return document.createTextNode(text); - }, - _attributes: function(attributes) { - var attrs = []; - for(attribute in attributes) - attrs.push((attribute=='className' ? 'class' : attribute) + - '="' + attributes[attribute].toString().escapeHTML() + '"'); - return attrs.join(" "); - }, - _children: function(element, children) { - if(typeof children=='object') { // array can hold nodes and text - children.flatten().each( function(e) { - if(typeof e=='object') - element.appendChild(e) - else - if(Builder._isStringOrNumber(e)) - element.appendChild(Builder._text(e)); - }); - } else - if(Builder._isStringOrNumber(children)) - element.appendChild(Builder._text(children)); - }, - _isStringOrNumber: function(param) { - return(typeof param=='string' || typeof param=='number'); - } -} - -/* ------------- element ext -------------- */ - -// converts rgb() and #xxx to #xxxxxx format, -// returns self (or first argument) if not convertable -String.prototype.parseColor = function() { - color = "#"; - if(this.slice(0,4) == "rgb(") { - var cols = this.slice(4,this.length-1).split(','); - var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); - } else { - if(this.slice(0,1) == '#') { - if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); - if(this.length==7) color = this.toLowerCase(); - } - } - return(color.length==7 ? color : (arguments[0] || this)); -} - -Element.collectTextNodesIgnoreClass = function(element, ignoreclass) { - var children = $(element).childNodes; - var text = ""; - var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i"); - - for (var i = 0; i < children.length; i++) { - if(children[i].nodeType==3) { - text+=children[i].nodeValue; - } else { - if((!children[i].className.match(classtest)) && children[i].hasChildNodes()) - text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass); - } - } +/* ------------- element ext -------------- */ + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + color = "#"; + if(this.slice(0,4) == "rgb(") { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if(this.slice(0,1) == '#') { + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if(this.length==7) color = this.toLowerCase(); + } + } + return(color.length==7 ? color : (arguments[0] || this)); +} +Element.collectTextNodesIgnoreClass = function(element, ignoreclass) { + var children = $(element).childNodes; + var text = ""; + var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i"); + + for (var i = 0; i < children.length; i++) { + if(children[i].nodeType==3) { + text+=children[i].nodeValue; + } else { + if((!children[i].className.match(classtest)) && children[i].hasChildNodes()) + text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass); + } + } + return text; } -Element.setContentZoom = function(element, percent) { - element = $(element); - element.style.fontSize = (percent/100) + "em"; - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +Element.setContentZoom = function(element, percent) { + element = $(element); + element.style.fontSize = (percent/100) + "em"; + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); } -Element.getOpacity = function(element){ - var opacity; - if (opacity = Element.getStyle(element, "opacity")) - return parseFloat(opacity); - if (opacity = (Element.getStyle(element, "filter") || '').match(/alpha\(opacity=(.*)\)/)) - if(opacity[1]) return parseFloat(opacity[1]) / 100; - return 1.0; -} - -Element.setOpacity = function(element, value){ - element= $(element); - var els = element.style; - if (value == 1){ - els.opacity = '0.999999'; - if(/MSIE/.test(navigator.userAgent)) - els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,''); - } else { - if(value < 0.00001) value = 0; - els.opacity = value; - if(/MSIE/.test(navigator.userAgent)) - els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + - "alpha(opacity="+value*100+")"; - } -} - -Element.getInlineOpacity = function(element){ - element= $(element); - var op; - op = element.style.opacity; - if (typeof op != "undefined" && op != "") return op; - return ""; -} - -Element.setInlineOpacity = function(element, value){ - element= $(element); - var els = element.style; - els.opacity = value; -} - -/*--------------------------------------------------------------------------*/ - -Element.Class = { - // Element.toggleClass(element, className) toggles the class being on/off - // Element.toggleClass(element, className1, className2) toggles between both classes, - // defaulting to className1 if neither exist - toggle: function(element, className) { - if(Element.Class.has(element, className)) { - Element.Class.remove(element, className); - if(arguments.length == 3) Element.Class.add(element, arguments[2]); - } else { - Element.Class.add(element, className); - if(arguments.length == 3) Element.Class.remove(element, arguments[2]); - } - }, - - // gets space-delimited classnames of an element as an array - get: function(element) { - return $(element).className.split(' '); - }, - - // functions adapted from original functions by Gavin Kistner - remove: function(element) { - element = $(element); - var removeClasses = arguments; - $R(1,arguments.length-1).each( function(index) { - element.className = - element.className.split(' ').reject( - function(klass) { return (klass == removeClasses[index]) } ).join(' '); - }); - }, - - add: function(element) { - element = $(element); - for(var i = 1; i < arguments.length; i++) { - Element.Class.remove(element, arguments[i]); - element.className += (element.className.length > 0 ? ' ' : '') + arguments[i]; - } - }, - - // returns true if all given classes exist in said element - has: function(element) { - element = $(element); - if(!element || !element.className) return false; - var regEx; - for(var i = 1; i < arguments.length; i++) { - if((typeof arguments[i] == 'object') && - (arguments[i].constructor == Array)) { - for(var j = 0; j < arguments[i].length; j++) { - regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)"); - if(!regEx.test(element.className)) return false; - } - } else { - regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)"); - if(!regEx.test(element.className)) return false; - } - } - return true; - }, - - // expects arrays of strings and/or strings as optional paramters - // Element.Class.has_any(element, ['classA','classB','classC'], 'classD') - has_any: function(element) { - element = $(element); - if(!element || !element.className) return false; - var regEx; - for(var i = 1; i < arguments.length; i++) { - if((typeof arguments[i] == 'object') && - (arguments[i].constructor == Array)) { - for(var j = 0; j < arguments[i].length; j++) { - regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)"); - if(regEx.test(element.className)) return true; - } - } else { - regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)"); - if(regEx.test(element.className)) return true; - } - } - return false; - }, - - childrenWith: function(element, className) { - var children = $(element).getElementsByTagName('*'); - var elements = new Array(); - - for (var i = 0; i < children.length; i++) - if (Element.Class.has(children[i], className)) - elements.push(children[i]); - - return elements; - } +Element.getOpacity = function(element){ + var opacity; + if (opacity = Element.getStyle(element, "opacity")) + return parseFloat(opacity); + if (opacity = (Element.getStyle(element, "filter") || '').match(/alpha\(opacity=(.*)\)/)) + if(opacity[1]) return parseFloat(opacity[1]) / 100; + return 1.0; } +Element.setOpacity = function(element, value){ + element= $(element); + var els = element.style; + if (value == 1){ + els.opacity = '0.999999'; + if(/MSIE/.test(navigator.userAgent)) + els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,''); + } else { + if(value < 0.00001) value = 0; + els.opacity = value; + if(/MSIE/.test(navigator.userAgent)) + els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + + "alpha(opacity="+value*100+")"; + } +} + +Element.getInlineOpacity = function(element){ + element= $(element); + var op; + op = element.style.opacity; + if (typeof op != "undefined" && op != "") return op; + return ""; +} + +Element.setInlineOpacity = function(element, value){ + element= $(element); + var els = element.style; + els.opacity = value; +} + +/*--------------------------------------------------------------------------*/ + +Element.Class = { + // Element.toggleClass(element, className) toggles the class being on/off + // Element.toggleClass(element, className1, className2) toggles between both classes, + // defaulting to className1 if neither exist + toggle: function(element, className) { + if(Element.Class.has(element, className)) { + Element.Class.remove(element, className); + if(arguments.length == 3) Element.Class.add(element, arguments[2]); + } else { + Element.Class.add(element, className); + if(arguments.length == 3) Element.Class.remove(element, arguments[2]); + } + }, + + // gets space-delimited classnames of an element as an array + get: function(element) { + return $(element).className.split(' '); + }, + + // functions adapted from original functions by Gavin Kistner + remove: function(element) { + element = $(element); + var removeClasses = arguments; + $R(1,arguments.length-1).each( function(index) { + element.className = + element.className.split(' ').reject( + function(klass) { return (klass == removeClasses[index]) } ).join(' '); + }); + }, + + add: function(element) { + element = $(element); + for(var i = 1; i < arguments.length; i++) { + Element.Class.remove(element, arguments[i]); + element.className += (element.className.length > 0 ? ' ' : '') + arguments[i]; + } + }, + + // returns true if all given classes exist in said element + has: function(element) { + element = $(element); + if(!element || !element.className) return false; + var regEx; + for(var i = 1; i < arguments.length; i++) { + if((typeof arguments[i] == 'object') && + (arguments[i].constructor == Array)) { + for(var j = 0; j < arguments[i].length; j++) { + regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)"); + if(!regEx.test(element.className)) return false; + } + } else { + regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)"); + if(!regEx.test(element.className)) return false; + } + } + return true; + }, + + // expects arrays of strings and/or strings as optional paramters + // Element.Class.has_any(element, ['classA','classB','classC'], 'classD') + has_any: function(element) { + element = $(element); + if(!element || !element.className) return false; + var regEx; + for(var i = 1; i < arguments.length; i++) { + if((typeof arguments[i] == 'object') && + (arguments[i].constructor == Array)) { + for(var j = 0; j < arguments[i].length; j++) { + regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)"); + if(regEx.test(element.className)) return true; + } + } else { + regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)"); + if(regEx.test(element.className)) return true; + } + } + return false; + }, + + childrenWith: function(element, className) { + var children = $(element).getElementsByTagName('*'); + var elements = new Array(); + + for (var i = 0; i < children.length; i++) + if (Element.Class.has(children[i], className)) + elements.push(children[i]); + + return elements; + } +} + /*--------------------------------------------------------------------------*/ var Effect = { @@ -371,6 +256,9 @@ Effect.Transitions.full = function(pos) { Effect.Queue = { effects: [], + _each: function(iterator) { + this.effects._each(iterator); + }, interval: null, add: function(effect) { var timestamp = new Date().getTime(); @@ -407,6 +295,7 @@ Effect.Queue = { this.effects.invoke('loop', timePos); } } +Object.extend(Effect.Queue, Enumerable); Effect.Base = function() {}; Effect.Base.prototype = { @@ -632,6 +521,8 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), this.start(options); }, setup: function() { + // Prevent executing on elements not in the layout flow + if(this.element.style.display=='none') { this.cancel(); return; } // Disable background image during the effect this.oldBgImage = this.element.style.backgroundImage; this.element.style.backgroundImage = "none"; @@ -862,7 +753,7 @@ Effect.SlideDown = function(element) { }, afterUpdateInternal: function(effect) { effect.element.firstChild.style.bottom = - (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + (effect.dims[0] - effect.element.clientHeight) + 'px'; }, afterFinishInternal: function(effect) { Element.undoClipping(effect.element); Element.undoPositioned(effect.element.firstChild); @@ -889,7 +780,7 @@ Effect.SlideUp = function(element) { }, afterUpdateInternal: function(effect) { effect.element.firstChild.style.bottom = - (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + (effect.dims[0] - effect.element.clientHeight) + 'px'; }, afterFinishInternal: function(effect) { Element.hide(effect.element); Element.undoClipping(effect.element); @@ -987,7 +878,7 @@ Effect.Grow = function(element) { els.top = oldTop; els.left = oldLeft; els.height = oldHeight; - els.width = originalWidth; + els.width = originalWidth + 'px'; Element.setInlineOpacity(el, oldOpacity); } }, options) diff --git a/tracks/public/javascripts/prototype.js b/tracks/public/javascripts/prototype.js index 120f4cb9..0ba70a77 100644 --- a/tracks/public/javascripts/prototype.js +++ b/tracks/public/javascripts/prototype.js @@ -1,4 +1,4 @@ -/* Prototype JavaScript framework, version 1.4.0_rc0 +/* Prototype JavaScript framework, version 1.4.0_rc2 * (c) 2005 Sam Stephenson * * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff @@ -11,7 +11,7 @@ /*--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.4.0_rc0', + Version: '1.4.0_rc2', emptyFunction: function() {}, K: function(x) {return x} @@ -607,8 +607,8 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { try { this.url = url; - if (this.options.method == 'get') - this.url += '?' + parameters; + if (this.options.method == 'get' && parameters.length > 0) + this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; Ajax.Responders.dispatch('onCreate', this, this.transport); @@ -626,6 +626,8 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), { this.transport.send(this.options.method == 'post' ? body : null); } catch (e) { + (this.options.onException || Prototype.emptyFunction)(this, e); + Ajax.Responders.dispatch('onException', this, e); } }, @@ -783,9 +785,9 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { } }); document.getElementsByClassName = function(className, parentElement) { - var children = (document.body || $(parentElement)).getElementsByTagName('*'); + var children = ($(parentElement) || document.body).getElementsByTagName('*'); return $A(children).inject([], function(elements, child) { - if (Element.hasClassName(child, className)) + if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) elements.push(child); return elements; }); @@ -1129,7 +1131,7 @@ var Form = { }, getElements: function(form) { - var form = $(form); + form = $(form); var elements = new Array(); for (tagName in Form.Element.Serializers) { @@ -1141,7 +1143,7 @@ var Form = { }, getInputs: function(form, typeName, name) { - var form = $(form); + form = $(form); var inputs = form.getElementsByTagName('input'); if (!typeName && !name) @@ -1177,7 +1179,7 @@ var Form = { }, focusFirstElement: function(form) { - var form = $(form); + form = $(form); var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; @@ -1195,7 +1197,7 @@ var Form = { Form.Element = { serialize: function(element) { - var element = $(element); + element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); @@ -1205,7 +1207,7 @@ Form.Element = { }, getValue: function(element) { - var element = $(element); + element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); diff --git a/tracks/script/about b/tracks/script/about new file mode 100644 index 00000000..62133381 --- /dev/null +++ b/tracks/script/about @@ -0,0 +1,3 @@ +#!/usr/local/bin/ruby +require File.dirname(__FILE__) + '/../config/boot' +require 'commands/about' \ No newline at end of file diff --git a/tracks/script/plugin b/tracks/script/plugin new file mode 100644 index 00000000..d3c4ba40 --- /dev/null +++ b/tracks/script/plugin @@ -0,0 +1,3 @@ +#!/usr/local/bin/ruby +require File.dirname(__FILE__) + '/../config/boot' +require 'commands/plugin' \ No newline at end of file