public
Description: Tracks is a GTD(TM) web application, built with Ruby on Rails
Homepage: http://www.rousette.org.uk/projects/
Clone URL: git://github.com/bsag/tracks.git
Click here to lend your support to: tracks and make a donation at www.pledgie.com !
tracks / app / helpers / todos_helper.rb
100644 284 lines (246 sloc) 10.189 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
module TodosHelper
 
  # #require 'users_controller' Counts the number of incomplete items in the
  # specified context
  #
  def count_items(context)
    count = Todo.find_all("done=0 AND context_id=#{context.id}").length
  end
 
  def form_remote_tag_edit_todo( &block )
    form_tag(
      todo_path(@todo), {
        :method => :put,
        :id => dom_id(@todo, 'form'),
        :class => dom_id(@todo, 'form') + " inline-form edit_todo_form" },
      &block )
    apply_behavior 'form.edit_todo_form', make_remote_form(
      :method => :put,
      :before => "this.down('button.positive').startWaiting()",
      :loaded => "this.down('button.positive').stopWaiting()",
      :condition => "!(this.down('button.positive').isWaiting())"),
      :prevent_default => true
  end
  
  def set_behavior_for_delete_icon
    parameters = "_source_view=#{@source_view}"
    parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
    apply_behavior '.item-container a.delete_icon:click', :prevent_default => true do |page|
      page.confirming "'Are you sure that you want to ' + this.title + '?'" do
        page << "itemContainer = this.up('.item-container'); itemContainer.startWaiting();"
        page << remote_to_href(:method => 'delete', :with => "'#{parameters}'", :complete => "itemContainer.stopWaiting();")
      end
    end
  end
  
  def remote_delete_icon
    str = link_to( image_tag_for_delete,
      todo_path(@todo), :id => "delete_icon_"+@todo.id.to_s,
      :class => "icon delete_icon", :title => "delete the action '#{@todo.description}'")
    set_behavior_for_delete_icon
    str
  end
  
  def set_behavior_for_star_icon
    apply_behavior '.item-container a.star_item:click',
      remote_to_href(:method => 'put', :with => "{ _source_view : '#{@source_view}' }"),
      :prevent_default => true
  end
 
  def remote_star_icon
    str = link_to( image_tag_for_star(@todo),
      toggle_star_todo_path(@todo),
      :class => "icon star_item", :title => "star the action '#{@todo.description}'")
    set_behavior_for_star_icon
    str
  end
  
  def set_behavior_for_edit_icon
    parameters = "_source_view=#{@source_view}"
    parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
    apply_behavior '.item-container a.edit_icon:click', :prevent_default => true do |page|
      page << "Effect.Pulsate(this);"
      page << remote_to_href(:method => 'get', :with => "'#{parameters}'")
    end
  end
  
  def remote_edit_icon
    if !@todo.completed?
      str = link_to( image_tag_for_edit(@todo),
        edit_todo_path(@todo),
        :class => "icon edit_icon")
      set_behavior_for_edit_icon
    else
      str = '<a class="icon">' + image_tag("blank.png") + "</a> "
    end
    str
  end
  
  def set_behavior_for_toggle_checkbox
    parameters = "_source_view=#{@source_view}"
    parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
    apply_behavior '.item-container input.item-checkbox:click',
      remote_function(:url => javascript_variable('this.value'), :method => 'put',
      :with => "'#{parameters}'")
  end
  
  def remote_toggle_checkbox
    str = check_box_tag('item_id', toggle_check_todo_path(@todo), @todo.completed?, :class => 'item-checkbox')
    set_behavior_for_toggle_checkbox
    str
  end
  
  def date_span
    if @todo.completed?
      "<span class=\"grey\">#{format_date( @todo.completed_at )}</span>"
    elsif @todo.deferred?
      show_date( @todo.show_from )
    else
      due_date( @todo.due )
    end
  end
  
  def tag_list_text
    @todo.tags.collect{|t| t.name}.join(', ')
  end
  
  def tag_list
    tags_except_starred = @todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
    tag_list = tags_except_starred.collect{|t| "<span class=\"tag #{t.name.gsub(' ','-')}\">" + link_to(t.name, :controller => "todos", :action => "tag", :id => t.name) + "</span>"}.join('')
    "<span class='tags'>#{tag_list}</span>"
  end
  
  def tag_list_mobile
    tags_except_starred = @todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
    # removed the link. TODO: add link to mobile view of tagged actions
    tag_list = tags_except_starred.collect{|t|
      "<span class=\"tag\">" +
        link_to(t.name, {:action => "tag", :controller => "todos", :id => t.name+".m"}) +
      "</span>"}.join('')
    if not tag_list.empty? then "<span class=\"tags\">#{tag_list}</span>" end
  end
  
  def deferred_due_date
    if @todo.deferred? && @todo.due
      "(action due on #{format_date(@todo.due)})"
    end
  end
  
  def project_and_context_links(parent_container_type, opts = {})
    str = ''
    if @todo.completed?
      str += @todo.context.name unless opts[:suppress_context]
      should_suppress_project = opts[:suppress_project] || @todo.project.nil?
      str += ", " unless str.blank? || should_suppress_project
      str += @todo.project.name unless should_suppress_project
      str = "(#{str})" unless str.blank?
    else
      if (['project', 'tag', 'stats', 'search'].include?(parent_container_type))
        str << item_link_to_context( @todo )
      end
      if (['context', 'tickler', 'tag', 'stats', 'search'].include?(parent_container_type)) && @todo.project_id
        str << item_link_to_project( @todo )
      end
    end
    return str
  end
    
  # Uses the 'staleness_starts' value from settings.yml (in days) to colour the
  # background of the action appropriately according to the age of the creation
  # date:
  # * l1: created more than 1 x staleness_starts, but < 2 x staleness_starts
  # * l2: created more than 2 x staleness_starts, but < 3 x staleness_starts
  # * l3: created more than 3 x staleness_starts
  #
  def staleness_class(item)
    if item.due || item.completed?
      return ""
    elsif item.created_at < user_time - (prefs.staleness_starts * 3).days
      return " stale_l3"
    elsif item.created_at < user_time - (prefs.staleness_starts * 2).days
      return " stale_l2"
    elsif item.created_at < user_time - (prefs.staleness_starts).days
      return " stale_l1"
    else
      return ""
    end
  end
 
  # Check show_from date in comparison to today's date Flag up date
  # appropriately with a 'traffic light' colour code
  #
  def show_date(d)
    if d == nil
      return ""
    end
 
    days = days_from_today(d)
       
    case days
      # overdue or due very soon! sound the alarm!
    when -1000..-1
      "<a title=\"" + format_date(d) + "\"><span class=\"red\">Scheduled to show " + (days * -1).to_s + " days ago</span></a> "
    when 0
      "<a title=\"" + format_date(d) + "\"><span class=\"amber\">Show Today</span></a> "
    when 1
      "<a title=\"" + format_date(d) + "\"><span class=\"amber\">Show Tomorrow</span></a> "
      # due 2-7 days away
    when 2..7
      if prefs.due_style == Preference.due_styles[:due_on]
        "<a title=\"" + format_date(d) + "\"><span class=\"orange\">Show on " + d.strftime("%A") + "</span></a> "
      else
        "<a title=\"" + format_date(d) + "\"><span class=\"orange\">Show in " + days.to_s + " days</span></a> "
      end
      # more than a week away - relax
    else
      "<a title=\"" + format_date(d) + "\"><span class=\"green\">Show in " + days.to_s + " days</span></a> "
    end
  end
  
  def calendar_setup( input_field )
    str = "Calendar.setup({ ifFormat:\"#{prefs.date_format}\""
    str << ",firstDay:#{prefs.week_starts},showOthers:true,range:[2004, 2010]"
    str << ",step:1,inputField:\"" + input_field + "\",cache:true,align:\"TR\" })\n"
    javascript_tag str
  end
  
  def item_container_id (todo)
    if source_view_is :project
      return "p#{todo.project_id}" if todo.active?
      return "tickler" if todo.deferred?
    end
    return "c#{todo.context_id}"
  end
 
  def should_show_new_item
 
    if @todo.project.nil? == false
      # do not show new actions that were added to hidden or completed projects
      # on home page and context page
      return false if source_view_is(:todo) && (@todo.project.hidden? || @todo.project.completed?)
      return false if source_view_is(:context) && (@todo.project.hidden? || @todo.project.completed?)
    end
 
    return true if source_view_is(:deferred) && @todo.deferred?
    return true if source_view_is(:project) && @todo.project.hidden? && @todo.project_hidden?
    return true if source_view_is(:project) && @todo.deferred?
    return true if !source_view_is(:deferred) && @todo.active?
    return false
  end
  
  def parent_container_type
    return 'tickler' if source_view_is :deferred
    return 'project' if source_view_is :project
    return 'stats' if source_view_is :stats
    return 'context'
  end
  
  def empty_container_msg_div_id
    return "tickler-empty-nd" if source_view_is(:project) && @todo.deferred?
    return "p#{@todo.project_id}empty-nd" if source_view_is :project
    return "c#{@todo.context_id}empty-nd"
  end
  
  def project_names_for_autocomplete
    array_or_string_for_javascript( ['None'] + @projects.select{ |p| p.active? }.collect{|p| escape_javascript(p.name) } )
  end
  
  def context_names_for_autocomplete
    # #return array_or_string_for_javascript(['Create a new context']) if
    # @contexts.empty?
    array_or_string_for_javascript( @contexts.collect{|c| escape_javascript(c.name) } )
  end
 
  def format_ical_notes(notes)
    split_notes = notes.split(/\n/)
    joined_notes = split_notes.join("\\n")
  end
  
  def formatted_pagination(total)
    s = will_paginate(@todos)
    (s.gsub /(<\/[^<]+>)/, '\1 ').chomp(' ')
  end
 
  def date_field_tag(name, id, value = nil, options = {})
    text_field_tag name, value, {"size" => 12, "id" => id, "class" => "Date", "onfocus" => "Calendar.setup", "autocomplete" => "off"}.update(options.stringify_keys)
  end
    
  private
  
  def image_tag_for_delete
    image_tag("blank.png", :title =>"Delete action", :class=>"delete_item")
  end
  
  def image_tag_for_edit(todo)
    image_tag("blank.png", :title =>"Edit action", :class=>"edit_item", :id=> dom_id(todo, 'edit_icon'))
  end
  
  def image_tag_for_star(todo)
    class_str = todo.starred? ? "starred_todo" : "unstarred_todo"
    image_tag("blank.png", :title =>"Star action", :class => class_str)
  end
  
end