From 9c912c18a5717c009c014a437d872a27ce83b167 Mon Sep 17 00:00:00 2001 From: bsag Date: Sat, 4 Jun 2005 15:55:58 +0000 Subject: [PATCH] New images (from eclipse.org) for the edit, delete, notes and up, down, top and bottom buttons. I've made a greyscale version for the default, then the coloured version gets loaded when the mouse is hovering over the button. git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@93 a4c988fc-2ded-0310-b66e-134b36920a42 --- tracks/app/controllers/application.rb | 14 + tracks/app/controllers/todo_controller.rb | 1 + tracks/app/models/todo.rb | 6 +- .../app/views/context/_context_listing.rhtml | 22 +- tracks/app/views/context/_show_items.rhtml | 29 +- .../app/views/project/_project_listing.rhtml | 18 +- tracks/app/views/project/_show_items.rhtml | 24 +- tracks/app/views/todo/_action_edit_form.rhtml | 3 +- tracks/app/views/todo/_show_items.rhtml | 29 +- tracks/app/views/todo/list.rhtml | 12 +- tracks/doc/CHANGENOTES.txt | 1 + tracks/lib/math/statistics.rb | 253 ++++++++++++++++++ tracks/public/dispatch.cgi | 2 +- tracks/public/dispatch.fcgi | 2 +- tracks/public/dispatch.rb | 2 +- tracks/public/images/blank.png | Bin 0 -> 156 bytes tracks/public/images/bottom_off.png | Bin 0 -> 3752 bytes tracks/public/images/bottom_on.png | Bin 0 -> 3754 bytes tracks/public/images/calendar.gif | Bin 103 -> 0 bytes tracks/public/images/delete_off.png | Bin 0 -> 3797 bytes tracks/public/images/delete_on.png | Bin 0 -> 3809 bytes tracks/public/images/down_off.png | Bin 0 -> 3775 bytes tracks/public/images/down_on.png | Bin 0 -> 3780 bytes .../images/{delete.png => edit_off.png} | Bin 3128 -> 3698 bytes .../public/images/{edit.png => edit_on.png} | Bin 3055 -> 3699 bytes tracks/public/images/notes_off.png | Bin 0 -> 3840 bytes tracks/public/images/notes_on.png | Bin 0 -> 3844 bytes .../public/images/{notes.png => top_off.png} | Bin 2963 -> 3737 bytes tracks/public/images/top_on.png | Bin 0 -> 3748 bytes tracks/public/images/up_off.png | Bin 0 -> 3759 bytes tracks/public/images/up_on.png | Bin 0 -> 3760 bytes tracks/public/stylesheets/standard.css | 76 +++++- tracks/script/benchmarker | 2 +- tracks/script/breakpointer | 2 +- tracks/script/console | 2 +- tracks/script/console_sandbox | 2 +- tracks/script/console_sandbox.rb | 2 +- tracks/script/destroy | 2 +- tracks/script/generate | 2 +- tracks/script/profiler | 2 +- tracks/script/runner | 2 +- tracks/script/server | 2 +- 42 files changed, 433 insertions(+), 81 deletions(-) create mode 100644 tracks/lib/math/statistics.rb create mode 100644 tracks/public/images/blank.png create mode 100644 tracks/public/images/bottom_off.png create mode 100644 tracks/public/images/bottom_on.png delete mode 100644 tracks/public/images/calendar.gif create mode 100644 tracks/public/images/delete_off.png create mode 100644 tracks/public/images/delete_on.png create mode 100644 tracks/public/images/down_off.png create mode 100644 tracks/public/images/down_on.png rename tracks/public/images/{delete.png => edit_off.png} (72%) rename tracks/public/images/{edit.png => edit_on.png} (72%) create mode 100644 tracks/public/images/notes_off.png create mode 100644 tracks/public/images/notes_on.png rename tracks/public/images/{notes.png => top_off.png} (71%) create mode 100644 tracks/public/images/top_on.png create mode 100644 tracks/public/images/up_off.png create mode 100644 tracks/public/images/up_on.png diff --git a/tracks/app/controllers/application.rb b/tracks/app/controllers/application.rb index 5fd627c2..5239b4e4 100644 --- a/tracks/app/controllers/application.rb +++ b/tracks/app/controllers/application.rb @@ -2,6 +2,7 @@ # Likewise will all the methods added be available for all controllers. require_dependency "login_system" +require_dependency "math/statistics" require 'date' @@ -29,5 +30,18 @@ class ApplicationController < ActionController::Base def errors_for( obj ) error_messages_for( obj ) unless instance_eval("@#{obj}").nil? end + + def av_completed + completed = Todo.find(:all, :conditions => "done=1") + days = [] + completed.each do |i| + days << (i.completed - i.created).to_f + end + return days.average, days.max + end +end + +class Array + include Math::Statistics end \ No newline at end of file diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb index 198bfc14..aae6dc09 100644 --- a/tracks/app/controllers/todo_controller.rb +++ b/tracks/app/controllers/todo_controller.rb @@ -23,6 +23,7 @@ class TodoController < ApplicationController @hidden_places = Context.find( :all, :conditions => "hide=1", :order => "position ASC" ) @done = Todo.find( :all, :conditions => "done=1", :order => "completed DESC", :limit => NO_OF_ACTIONS ) + @av_days_to_completion, @max_days_to_completion = av_completed() # Set count badge to number of not-done, not hidden context items @count = count_shown_items( @hidden_places ) diff --git a/tracks/app/models/todo.rb b/tracks/app/models/todo.rb index 54511650..56bb52f6 100644 --- a/tracks/app/models/todo.rb +++ b/tracks/app/models/todo.rb @@ -5,9 +5,9 @@ class Todo < ActiveRecord::Base # Description field can't be empty, and must be < 100 bytes # Notes must be < 60,000 bytes (65,000 actually, but I'm being cautious) - validates_presence_of :description, :message => "no description provided" - validates_length_of :description, :maximum => 100, :message => "description is too long" - validates_length_of :notes, :maximum => 60000, :message => "notes are too long" + validates_presence_of :description + 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 diff --git a/tracks/app/views/context/_context_listing.rhtml b/tracks/app/views/context/_context_listing.rhtml index 9ce946ee..48e59fbe 100644 --- a/tracks/app/views/context/_context_listing.rhtml +++ b/tracks/app/views/context/_context_listing.rhtml @@ -1,17 +1,21 @@ <% context = context_listing %> -
+
-
+
<% if context.position % 2 == 0 %>
<% else %>
<% end %>
- <%= link_to( "⇞", {:action => "move_top", :id => context.id}, :title => "Move to top" ) %> - <%= link_to( "↑", {:action => "move_up", :id => context.id}, :title => "Move up" ) %> - <%= link_to( "↓", {:action => "move_down", :id => context.id}, :title => "Move down" ) %> - <%= link_to( "⇟", {:action => "move_bottom", :id => context.id}, :title => "Move to bottom" ) %> + <%= link_to(image_tag("blank", :title=>"Move to top", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_top", :id => context.id}, :title => "Move to top", :class=>"to_top") %> + <%= link_to(image_tag("blank", :title=>"Move up", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_up", :id => context.id}, :title => "Move up", :class=>"up" ) %> + <%= link_to(image_tag("blank", :title=>"Move down", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_down", :id => context.id}, :title => "Move down", :class=>"down") %> + <%= link_to(image_tag("blank", :title=>"Move to bottom", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_bottom", :id => context.id}, :title => "Move to bottom", :class=>"to_bottom") %>
<%= link_to( "#{context.name}", :action => "show", :name => urlize(context.name) ) %><%= " (" + Todo.count( "context_id=#{context.id} AND done=0" ).to_s + " actions)" %> @@ -24,11 +28,11 @@ <% end %> -<%= link_to_function(image_tag( "edit", :title => "Edit item", :width=>"10", :height=>"10", :border=>"0"), "Element.toggle('context-#{context.id}','context-#{context.id}-edit-form'); new Effect.Appear('context-#{context.id}-edit-form'); Form.focus_first('form-context-#{context.id}');" ) + " " + - link_to_remote( image_tag("delete", :title =>"Delete this context", :height=>"10", :width=>"10", :border=>"0"), +<%= link_to_remote( image_tag("blank", :title =>"Delete context", :class=>"delete_item"), :update => "context-#{context.id}-container", :loading => "new Effect.Squish('context-#{context.id}-container')", - :url => { :controller => "context", :action => "destroy", :id => context.id }, :confirm => "Are you sure that you want to delete the context \'#{context.name}\'?" ) %> + :url => { :controller => "context", :action => "destroy", :id => context.id }, :confirm => "Are you sure that you want to delete the context \'#{context.name}\'?" ) + " " + + link_to_function( image_tag( "blank", :title => "Edit context", :class=>"edit_item"), "Element.toggle('context-#{context.id}','context-#{context.id}-edit-form'); new Effect.Appear('context-#{context.id}-edit-form'); Form.focus_first('form-context-#{context.id}');" ) %>
diff --git a/tracks/app/views/context/_show_items.rhtml b/tracks/app/views/context/_show_items.rhtml index 7a52d20f..074521c5 100644 --- a/tracks/app/views/context/_show_items.rhtml +++ b/tracks/app/views/context/_show_items.rhtml @@ -11,14 +11,18 @@
- <%= - link_to_function(image_tag( "edit", :title => "Edit item", :width=>"10", :height=>"10", :border=>"0"), "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_remote( image_tag("delete", :title =>"Delete this action"), - :update => "item-#{item.id}-container", - :loading => "new Effect.Squish('item-#{item.id}-container')", - :url => { :controller => "context", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " + link_to_remote( image_tag("blank", :title =>"Delete this action", :class=>"delete_item"), + :update => "item-#{item.id}-container", + :loading => "new Effect.Squish('item-#{item.id}-container')", + :url => { :controller => "context", :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}');" ) + " " %> + +
+ +
+
<%= staleness(item.created) %> @@ -28,7 +32,7 @@ <%= link_to( "[P]", { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" ) %> <% end %> <% if item.notes? %> - <%= "" + image_tag( "notes", :width=>"10", :height=>"10", :border=>"0") + "" %> + <%= "" + image_tag( "blank", :width=>"16", :height=>"16", :border=>"0" ) + "" %> <% m_notes = markdown( item.notes ) %> <%= "
" + m_notes + "
" %> <% end %> @@ -58,14 +62,19 @@
- <%= - link_to_remote( image_tag("delete", :title =>"Delete this action"), + link_to_remote( image_tag("blank", :title =>"Delete this action", :class=>"delete_item"), :update => "done-item-#{item.id}-container", :loading => "new Effect.Squish('done-item-#{item.id}-container')", :url => { :controller => "context", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " %> + <%= image_tag("blank") %>
+ +
+ +
+
<%= format_date( item.completed ) %> <%= item.description %> @@ -73,7 +82,7 @@ <%= link_to( "[P]", { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" ) %> <% end %> <% if item.notes? %> - <%= "" + image_tag( "notes", :width=>"10", :height=>"10", :border=>"0") + "" %> + <%= "" + image_tag( "blank", :width=>"16", :height=>"16", :border=>"0" ) + "" %> <% m_notes = markdown( item.notes ) %> <%= "
" + m_notes + "
" %> <% end %> diff --git a/tracks/app/views/project/_project_listing.rhtml b/tracks/app/views/project/_project_listing.rhtml index 7dc69ee4..9d01e1e4 100644 --- a/tracks/app/views/project/_project_listing.rhtml +++ b/tracks/app/views/project/_project_listing.rhtml @@ -8,10 +8,14 @@
<% end %>
- <%= link_to( "⇞", {:action => "move_top", :id => project.id}, :title => "Move to top" ) %> - <%= link_to( "↑", {:action => "move_up", :id => project.id}, :title => "Move up" ) %> - <%= link_to( "↓", {:action => "move_down", :id => project.id}, :title => "Move down" ) %> - <%= link_to( "⇟", {:action => "move_bottom", :id => project.id}, :title => "Move to bottom" ) %> + <%= link_to(image_tag("blank", :title=>"Move to top", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_top", :id => project.id}, :title => "Move to top", :class=>"to_top") %> + <%= link_to(image_tag("blank", :title=>"Move up", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_up", :id => project.id}, :title => "Move up", :class=>"up" ) %> + <%= link_to(image_tag("blank", :title=>"Move down", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_down", :id => project.id}, :title => "Move down", :class=>"down") %> + <%= link_to(image_tag("blank", :title=>"Move to bottom", :border=>"0", :width=>"16", :height=>"16"), + {:action => "move_bottom", :id => project.id}, :title => "Move to bottom", :class=>"to_bottom") %>
<%= link_to( "#{project.name}", :action => "show", :name => urlize(project.name) ) %><%= " (" + Todo.count( "project_id=#{project.id} AND done=0" ).to_s + " actions)" %> @@ -23,11 +27,11 @@ <% end -%> - <%= link_to_function(image_tag( "edit", :title => "Edit item", :width=>"10", :height=>"10", :border=>"0"), "Element.toggle('project-#{project.id}','project-#{project.id}-edit-form'); new Effect.Appear('project-#{project.id}-edit-form'); Form.focus_first('form-project-#{project.id}');" ) + " " + - link_to_remote( image_tag("delete", :title =>"Delete this project",:width=>"10", :height=>"10", :border=>"0"), + <%= link_to_remote( image_tag("blank", :title =>"Delete project", :class=>"delete_item"), :update => "project-#{project.id}-container", :loading => "new Effect.Squish('project-#{project.id}-container')", - :url => { :controller => "project", :action => "destroy", :id => project.id }, :confirm => "Are you sure that you want to delete the project \'#{project.name}\'?" ) %> + :url => { :controller => "project", :action => "destroy", :id => project.id }, :confirm => "Are you sure that you want to delete the project \'#{project.name}\'?" ) + " " + + link_to_function(image_tag( "blank", :title => "Edit item", :class=>"edit_item"), "Element.toggle('project-#{project.id}','project-#{project.id}-edit-form'); new Effect.Appear('project-#{project.id}-edit-form'); Form.focus_first('form-project-#{project.id}');" ) %>
diff --git a/tracks/app/views/project/_show_items.rhtml b/tracks/app/views/project/_show_items.rhtml index a60268ca..e7663186 100644 --- a/tracks/app/views/project/_show_items.rhtml +++ b/tracks/app/views/project/_show_items.rhtml @@ -11,15 +11,19 @@
- <%= - link_to_function(image_tag( "edit", :title => "Edit item", :width=>"10", :height=>"10", :border=>"0"), "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_remote( image_tag("delete", :title =>"Delete this action"), + link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"), :update => "item-#{item.id}-container", :loading => "new Effect.Squish('item-#{item.id}-container')", - :url => { :controller => "project", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " + :url => { :controller => "project", :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}');" ) + " " %>
+ +
+ +
+ <%= staleness(item.created) %> <%= due_date( item.due ) %> @@ -28,7 +32,7 @@ <%= link_to( "[C]", { :controller => "context", :action => "show", :name => urlize(item.context.name) }, :title => "View context: #{item.context.name}" ) %> <% end %> <% if item.notes? %> - <%= "" + image_tag( "notes", :width=>"10", :height=>"10", :border=>"0") + "" %> + <%= "" + image_tag( "blank", :width=>"16", :height=>"16", :border=>"0" ) + "" %> <% m_notes = markdown( item.notes ) %> <%= "
" + m_notes + "
" %> <% end %> @@ -58,14 +62,18 @@
- <%= - link_to_remote( image_tag("delete", :title =>"Delete this action"), + link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"), :update => "done-item-#{item.id}-container", :loading => "new Effect.Squish('done-item-#{item.id}-container')", :url => { :controller => "project", :action => "destroy_action", :id => item.id }, :confirm => "Are you sure that you want to delete the action \'#{item.description}\'?" ) + " " %>
+ +
+ +
+
<%= format_date( item.completed ) %> <%= item.description %> @@ -73,7 +81,7 @@ <%= link_to( "[C]", { :controller => "context", :action => "show", :name => urlize(item.context.name) }, :title => "View context: #{item.context.name}" ) %> <% end %> <% if item.notes? %> - <%= "" + image_tag( "notes", :width=>"10", :height=>"10", :border=>"0") + "" %> + <%= "" + image_tag( "blank", :width=>"16", :height=>"16", :border=>"0" ) + "" %> <% m_notes = markdown( item.notes ) %> <%= "
" + m_notes + "
" %> <% end %> diff --git a/tracks/app/views/todo/_action_edit_form.rhtml b/tracks/app/views/todo/_action_edit_form.rhtml index 870dab0e..aba18f36 100644 --- a/tracks/app/views/todo/_action_edit_form.rhtml +++ b/tracks/app/views/todo/_action_edit_form.rhtml @@ -1,7 +1,8 @@ <% @item = action_edit_form %> - +<%= error_messages_for("item") %> + <%= hidden_field( "item", "id" ) %> diff --git a/tracks/app/views/todo/_show_items.rhtml b/tracks/app/views/todo/_show_items.rhtml index d5424f91..da0dd761 100644 --- a/tracks/app/views/todo/_show_items.rhtml +++ b/tracks/app/views/todo/_show_items.rhtml @@ -11,15 +11,19 @@
- <%= - link_to_function(image_tag( "edit", :title => "Edit item", :width=>"10", :height=>"10", :border=>"0"), "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_remote( image_tag("delete", :title =>"Delete this action"), - :update => "item-#{item.id}-container", - :loading => "new Effect.Squish('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_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"), + :update => "item-#{item.id}-container", + :loading => "new Effect.Squish('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}')" ) + " " %>
+
+ +
<%= staleness(item.created) %> <%= due_date( item.due ) %> @@ -28,7 +32,7 @@ <%= link_to( "[P]", { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" ) %> <% end %> <% if item.notes? %> - <%= "" + image_tag( "notes", :width=>"10", :height=>"10", :border=>"0") + "" %> + <%= "" + image_tag( "blank", :width=>"16", :height=>"16", :border=>"0" ) + "" %> <% m_notes = markdown( item.notes ) %> <%= "
" + m_notes + "
" %> <% end %> @@ -58,14 +62,19 @@
- <%= - link_to_remote( image_tag("delete", :title =>"Delete this action"), + link_to_remote( image_tag("blank", :title =>"Delete action", :class=>"delete_item"), :update => "done-item-#{item.id}-container", :loading => "new Effect.Squish('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") %>
+ +
+ +
+
<%= format_date( item.completed ) %> <%= item.description %> @@ -73,7 +82,7 @@ <%= link_to( "[P]", { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" ) %> <% end %> <% if item.notes? %> - <%= "" + image_tag( "notes", :width=>"10", :height=>"10", :border=>"0") + "" %> + <%= "" + image_tag( "blank", :width=>"16", :height=>"16", :border=>"0" ) + "" %> <% m_notes = markdown( item.notes ) %> <%= "
" + m_notes + "
" %> <% end %> diff --git a/tracks/app/views/todo/list.rhtml b/tracks/app/views/todo/list.rhtml index 49e8ef9e..4f7f6272 100644 --- a/tracks/app/views/todo/list.rhtml +++ b/tracks/app/views/todo/list.rhtml @@ -2,7 +2,7 @@ @@ -10,8 +10,8 @@ <% @not_done = Todo.find_all("done=0 AND context_id=#{@shown_place.id}", "due IS NULL, due ASC, created ASC") -%> <% if !@not_done.empty? -%>
-

- <%= link_to( "#{@shown_place.name}", :controller => "context", :action => "show", :name => urlize(@shown_place.name) ) %>

+

<%= image_tag("collapse.png", :name=>"toggle_context_#{@shown_place.id}", :border=>"0") %> + <%= link_to( "#{@shown_place.name}", :controller => "context", :action => "show", :name => urlize(@shown_place.name) ) %>

<% if @not_done.empty? -%> @@ -42,7 +42,7 @@
- + <%= link_to_function( "Add the next action in this context »", "Element.toggle('todo_new_action');Element.toggle('new_actions');Form.focusFirstElement('todo-form-new-action');", {:title => "Add the next action [Alt+n]", :accesskey => "n"}) %> diff --git a/tracks/doc/CHANGENOTES.txt b/tracks/doc/CHANGENOTES.txt index f7fdd956..1faa0859 100644 --- a/tracks/doc/CHANGENOTES.txt +++ b/tracks/doc/CHANGENOTES.txt @@ -32,6 +32,7 @@ marked darker yellow, and those created more than 28 days ago (staleness_starts yellow! 17. Contexts and projects can now be sorted in any order you like. Arrow buttons on the /contexts and /projects pages let you move an item to the top, up, down or to the bottom. For contexts, this affects the order in which they sort on the home page. 18. You can mark projects as completed (by editing the project on /projects). In the 'sidebar' active and completed projects are shown separately, but you can still view the completed project. +19. New images (from eclipse.org) for the edit, delete, notes and up, down, top and bottom buttons. I've made a greyscale version for the default, then the coloured version gets loaded when the mouse is hovering over the button. ## Version 1.02 diff --git a/tracks/lib/math/statistics.rb b/tracks/lib/math/statistics.rb new file mode 100644 index 00000000..f63b2c47 --- /dev/null +++ b/tracks/lib/math/statistics.rb @@ -0,0 +1,253 @@ +=begin += module Math::Statistics + +== SYNOPSIS + + ---- + require "math/statistics" + + class Array + include Math::Statistics + end + + a = [-2,-1,1,2] + p a.sum + p a.avg + p a.var + p a.std + p a.Min + p a.Max + ---- + +produces + + ---- + 0.0 + 0.0 + 2.5 + 1.58113883 + -2 + 2 + ---- + +For hashes, + + ---- + require "math/statistics" + + class Hash + include Math::Statistics + Hash::default_block = lambda{|i,j| j} + end + + h = {'alice'=>-2, 'bob'=>-1, 'cris'=>1, 'diana'=>2} + p h.sum + p h.avg + p h.var + p h.std + p h.Min + p h.Max + ---- + +produces + + ---- + 0.0 + 0.0 + 2.5 + 1.58113883 + -2 + 2 + ---- + +== DESCRIPTION + +(({Math::Statistics})) provides basic statistical methods, i.e., +sum, average, variance, standard deviation, min and max. +This module can be used after including to the target class. +The target class must be Enumerable, more precisely, this module +uses each, size, min, and max. + +== CLASS METHOD + +: default_block= aProc + + Sets default block of the class. This block will be used by the methods. + +: default_block + + Returns default block for class if defined. Otherwise nil will be returnd. + +== METHOD + +: default_block= aProc + + Sets default block of the object. This block will be used by the methods. + Priority of the blocks is in the other: in-place given block, + object's default then class's default. + +: default_block + + Returns default block if defined. Otherwise nil will be returnd. + +: sum +: sum{...} + + Returns sum. When a block is given, summation is taken over the + each result of block evaluation. The role of blocks in the below + are same to this one. + +: average +: average{...} +: avg +: avg{...} + + Returns average. + +: variance +: variance{...} +: var +: var{...} + + Returns variance. + +: standard_deviation +: standard_deviation{...} +: std +: std{...} + + Returns standard deviation. + +: Min +: Min{...} + + Returns minimum. + +: Max +: Max{...} + + Returns maximam. + +== AUTHORS + +Gotoken + +== HISTORY + + 2001-02-28 created (gotoken#notwork.org) + +=end + +module Math + module Statistics + VERSION = "2001_02_18" + + def self.append_features(mod) + unless mod < Enumerable + raise TypeError, + "`#{self}' can't be included non Enumerable (#{mod})" + end + + def mod.default_block= (blk) + self.const_set("STAT_BLOCK", blk) + end + + def mod.default_block + defined?(self::STAT_BLOCK) && self::STAT_BLOCK + end + + super + end + + def default_block + @stat_block || type.default_block + end + + def default_block=(blk) + @stat_block = blk + end + + def sum + sum = 0.0 + if block_given? + each{|i| sum += yield(i)} + elsif default_block + each{|i| sum += default_block[*i]} + else + each{|i| sum += i} + end + sum + end + + def average(&blk) + sum(&blk)/size + end + + def variance(&blk) + sum2 = if block_given? + sum{|i| j=yield(i); j*j} + elsif default_block + sum{|i| j=default_block[*i]; j*j} + else + sum{|i| i**2} + end + sum2/size - average(&blk)**2 + end + + def standard_deviation(&blk) + Math::sqrt(variance(&blk)) + end + + def Min(&blk) + if block_given? + if min = find{|i| i} + min = yield(min) + each{|i| + j = yield(i) + min = j if min > j + } + min + end + elsif default_block + if min = find{|i| i} + min = default_block[*min] + each{|i| + j = default_block[*i] + min = j if min > j + } + min + end + else + min() + end + end + + def Max(&blk) + if block_given? + if max = find{|i| i} + max = yield(max) + each{|i| + j = yield(i) + max = j if max < j + } + max + end + elsif default_block + if max = find{|i| i} + max = default_block[*max] + each{|i| + j = default_block[*i] + max = j if max > j + } + max + end + else + max() + end + end + + alias avg average + alias std standard_deviation + alias var variance + end +end diff --git a/tracks/public/dispatch.cgi b/tracks/public/dispatch.cgi index 9b5ae760..9730473f 100755 --- a/tracks/public/dispatch.cgi +++ b/tracks/public/dispatch.cgi @@ -1,4 +1,4 @@ -#!/usr/local/bin/ruby +#!/usr/bin/ruby require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) diff --git a/tracks/public/dispatch.fcgi b/tracks/public/dispatch.fcgi index 0f1b9b3a..f0be2449 100755 --- a/tracks/public/dispatch.fcgi +++ b/tracks/public/dispatch.fcgi @@ -1,4 +1,4 @@ -#!/usr/local/bin/ruby +#!/usr/bin/ruby require File.dirname(__FILE__) + "/../config/environment" require 'dispatcher' diff --git a/tracks/public/dispatch.rb b/tracks/public/dispatch.rb index 9b5ae760..9730473f 100755 --- a/tracks/public/dispatch.rb +++ b/tracks/public/dispatch.rb @@ -1,4 +1,4 @@ -#!/usr/local/bin/ruby +#!/usr/bin/ruby require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) diff --git a/tracks/public/images/blank.png b/tracks/public/images/blank.png new file mode 100644 index 0000000000000000000000000000000000000000..cbee69c9368ec8839fc2d00a51d7b118010e519a GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl;=8&~`zjDUQ}64!{5 z;QX|b^2DN42FH~Aq*MjZ+{E&s56kuSqc5?CtN-=o4`njxgN@xNAKkX=R literal 0 HcmV?d00001 diff --git a/tracks/public/images/bottom_off.png b/tracks/public/images/bottom_off.png new file mode 100644 index 0000000000000000000000000000000000000000..0b16b28dbeb840126959bf6c5f67e603743f1994 GIT binary patch literal 3752 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 literal 0 HcmV?d00001 diff --git a/tracks/public/images/bottom_on.png b/tracks/public/images/bottom_on.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0de9a2da83790a297a237a03beb7d5d07add32 GIT binary patch literal 3754 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 E2ajwAK>z>% literal 0 HcmV?d00001 diff --git a/tracks/public/images/calendar.gif b/tracks/public/images/calendar.gif deleted file mode 100644 index 49e972baa06411dd41cd202e749f56521115364e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103 zcmZ?wbhEHb3w3+{}*_}^kIB?*=|Ns9Pz(DaQ3nK#q3xf`b2U5$x ztm7eg>Nd*>&rHn+X=}>G8W^}DSb1I~6do$zU9v> C^CGkW diff --git a/tracks/public/images/delete_off.png b/tracks/public/images/delete_off.png new file mode 100644 index 0000000000000000000000000000000000000000..e67fdc2a1dd7c7dc821f2b7e012da607112e89b3 GIT binary patch literal 3797 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 SNb>wuTu;a7X1SJq^nU=DO)3!p literal 0 HcmV?d00001 diff --git a/tracks/public/images/down_off.png b/tracks/public/images/down_off.png new file mode 100644 index 0000000000000000000000000000000000000000..933c50151488e72b8aa598df25498380ffeb718e GIT binary patch literal 3775 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 Nd%F6$taD0e0stedb}9e> delta 408 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 literal 0 HcmV?d00001 diff --git a/tracks/public/images/notes_on.png b/tracks/public/images/notes_on.png new file mode 100644 index 0000000000000000000000000000000000000000..ede65bc092d8fc58cedfa21625ef961196ce86a2 GIT binary patch literal 3844 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 aU`o)w29wm(8@TwW0MJm=d03%h74{!S)N$(o literal 0 HcmV?d00001 diff --git a/tracks/public/images/notes.png b/tracks/public/images/top_off.png similarity index 71% rename from tracks/public/images/notes.png rename to tracks/public/images/top_off.png index 6747154fdbafa4140ab6d274dc0dc7c16c138084..c051ba4106ee0abdd3e84e7c343aad5038b055cd 100644 GIT binary patch delta 1022 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 n@rSdERlw>^Cmhhi15Idl5+ literal 0 HcmV?d00001 diff --git a/tracks/public/images/up_off.png b/tracks/public/images/up_off.png new file mode 100644 index 0000000000000000000000000000000000000000..fdacb0e241efbaf7c4d0ade165bb8faad6a72965 GIT binary patch literal 3759 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 literal 0 HcmV?d00001 diff --git a/tracks/public/images/up_on.png b/tracks/public/images/up_on.png new file mode 100644 index 0000000000000000000000000000000000000000..2b0aa78bba03465936c78d2510faa8587694e17c GIT binary patch literal 3760 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 z