mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-28 18:10:15 +01:00
Merge branch 'master' into clinton/master
* master: (40 commits)
Updated the manual in HTML, PDF and .tex format.
Minor updates to the example db.
set version to 1.7RC
update version in template
update changelog
OpenSearch support.
update the sample sqlite databases and update sample content sql. Solves #802
forgot ot change one version to 1.7
update documentation for 1.7rc release
No point in changing the name of the OpenID identity column in users table. Use the existing one.
show recurrence pattern in :title of a recurring todo. Needed slight refactoring to make it happen
Re-write OpenID code to use new authentication plugin. Tested to work!
Upgraded to open_id_authentication plugin at 00d8bc7f97 and unpacked ruby-openid gem version 2.1.2.
remove old compressed js and css from the asset_packager plugin that we don't use anymore
applied the patches from Jakub to improve the mobile html
adds test for changing context on a todo in tag view. Resolves #762
Add testcase where changing the name of a project should be reflected in the default project name. resolves #756
add testcase for deleting a user. Resolves #734
implement suggestions for search in #787
fix failing tests
...
This commit is contained in:
commit
4fb4a98bc8
1905 changed files with 119478 additions and 58883 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -9,3 +9,5 @@ nbproject
|
||||||
vendor/plugins/query_trace/
|
vendor/plugins/query_trace/
|
||||||
db/schema.rb
|
db/schema.rb
|
||||||
.dotest
|
.dotest
|
||||||
|
public/javascripts/cache
|
||||||
|
public/stylesheets/cache
|
||||||
5
README
5
README
|
|
@ -8,12 +8,10 @@
|
||||||
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
|
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
|
||||||
* Original developer: bsag (http://www.rousette.org.uk/)
|
* Original developer: bsag (http://www.rousette.org.uk/)
|
||||||
* Contributors: http://dev.rousette.org.uk/wiki/Tracks/Contributing/Contributors
|
* Contributors: http://dev.rousette.org.uk/wiki/Tracks/Contributing/Contributors
|
||||||
* Version: 1.6
|
* Version: 1.7RC
|
||||||
* Copyright: (cc) 2004-2008 rousette.org.uk.
|
* Copyright: (cc) 2004-2008 rousette.org.uk.
|
||||||
* License: GNU GPL
|
* License: GNU GPL
|
||||||
|
|
||||||
**An important note for version 1.6: OpenID support is broken in this release. The fix isn't trivial because of changes to the `ruby-openid` gem, so we wanted to get this version out now and fix OpenID for the next release. If you depend on OpenID integration, we recommend waiting until the next release.**
|
|
||||||
|
|
||||||
All the documentation for Tracks can be found within the /doc directory. It contains a manual in HTML (manual.html) or PDF format (manual.pdf), and this includes full instructions for both new installations and upgrades from older installations of Tracks. The instructions might appear long and intimidatingly complex, but that is mostly because of the number of different platforms supported, and the different configurations which can be used (e.g. running Tracks on your local computer or on a remote server). If you choose the appropriate section for your situation (installation vs. upgrade), and use the easiest (recommended) method, you should find the instructions easy to follow. If you encounter problems, try searching the wiki, forum or mailing list (URLs above), and ask a question if you cannot find a solution to your problem.
|
All the documentation for Tracks can be found within the /doc directory. It contains a manual in HTML (manual.html) or PDF format (manual.pdf), and this includes full instructions for both new installations and upgrades from older installations of Tracks. The instructions might appear long and intimidatingly complex, but that is mostly because of the number of different platforms supported, and the different configurations which can be used (e.g. running Tracks on your local computer or on a remote server). If you choose the appropriate section for your situation (installation vs. upgrade), and use the easiest (recommended) method, you should find the instructions easy to follow. If you encounter problems, try searching the wiki, forum or mailing list (URLs above), and ask a question if you cannot find a solution to your problem.
|
||||||
|
|
||||||
For those upgrading, change notes are available in /doc/CHANGELOG. If you are thinking about contributing towards the development of Tracks, please read /doc/README_DEVELOPERS for general information, or /doc/tracks_api_wrapper.rb for information on Tracks' API.
|
For those upgrading, change notes are available in /doc/CHANGELOG. If you are thinking about contributing towards the development of Tracks, please read /doc/README_DEVELOPERS for general information, or /doc/tracks_api_wrapper.rb for information on Tracks' API.
|
||||||
|
|
@ -21,3 +19,4 @@ For those upgrading, change notes are available in /doc/CHANGELOG. If you are th
|
||||||
While fully usable for everyday use, Tracks is still a work in progress. Make sure that you take sensible precautions and back up all your data frequently, taking particular care when you are upgrading.
|
While fully usable for everyday use, Tracks is still a work in progress. Make sure that you take sensible precautions and back up all your data frequently, taking particular care when you are upgrading.
|
||||||
|
|
||||||
Enjoy being productive!
|
Enjoy being productive!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,13 @@ class ApplicationController < ActionController::Base
|
||||||
before_filter :set_time_zone
|
before_filter :set_time_zone
|
||||||
prepend_before_filter :login_required
|
prepend_before_filter :login_required
|
||||||
prepend_before_filter :enable_mobile_content_negotiation
|
prepend_before_filter :enable_mobile_content_negotiation
|
||||||
after_filter :restore_content_type_for_mobile
|
|
||||||
after_filter :set_charset
|
after_filter :set_charset
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
include ActionView::Helpers::SanitizeHelper
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
||||||
helper_method :format_date, :markdown
|
helper_method :format_date, :markdown
|
||||||
|
|
||||||
# By default, sets the charset to UTF-8 if it isn't already set
|
# By default, sets the charset to UTF-8 if it isn't already set
|
||||||
|
|
@ -148,21 +148,15 @@ class ApplicationController < ActionController::Base
|
||||||
# during the processing and then setting it to 'text/html' in an
|
# during the processing and then setting it to 'text/html' in an
|
||||||
# 'after_filter' -LKM 2007-04-01
|
# 'after_filter' -LKM 2007-04-01
|
||||||
def mobile?
|
def mobile?
|
||||||
return params[:format] == 'm' || response.content_type == MOBILE_CONTENT_TYPE
|
return params[:format] == 'm'
|
||||||
end
|
end
|
||||||
|
|
||||||
def enable_mobile_content_negotiation
|
def enable_mobile_content_negotiation
|
||||||
if mobile?
|
if mobile?
|
||||||
request.accepts.unshift(Mime::Type::lookup(MOBILE_CONTENT_TYPE))
|
request.format = :m
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def restore_content_type_for_mobile
|
|
||||||
if mobile?
|
|
||||||
response.content_type = 'text/html'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_todo_from_recurring_todo(rt, date=nil)
|
def create_todo_from_recurring_todo(rt, date=nil)
|
||||||
# create todo and initialize with data from recurring_todo rt
|
# create todo and initialize with data from recurring_todo rt
|
||||||
todo = current_user.todos.build( { :description => rt.description, :notes => rt.notes, :project_id => rt.project_id, :context_id => rt.context_id})
|
todo = current_user.todos.build( { :description => rt.description, :notes => rt.notes, :project_id => rt.project_id, :context_id => rt.context_id})
|
||||||
|
|
@ -234,8 +228,13 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_data_for_sidebar
|
def init_data_for_sidebar
|
||||||
@projects = @projects || current_user.projects.find(:all, :include => [:default_context ])
|
@completed_projects = current_user.projects.completed
|
||||||
@contexts = @contexts || current_user.contexts
|
@hidden_projects = current_user.projects.hidden
|
||||||
|
@active_projects = current_user.projects.active
|
||||||
|
|
||||||
|
@active_contexts = current_user.contexts.active
|
||||||
|
@hidden_contexts = current_user.contexts.hidden
|
||||||
|
|
||||||
init_not_done_counts
|
init_not_done_counts
|
||||||
if prefs.show_hidden_projects_in_sidebar
|
if prefs.show_hidden_projects_in_sidebar
|
||||||
init_project_hidden_todo_counts(['project'])
|
init_project_hidden_todo_counts(['project'])
|
||||||
|
|
@ -244,13 +243,13 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
def init_not_done_counts(parents = ['project','context'])
|
def init_not_done_counts(parents = ['project','context'])
|
||||||
parents.each do |parent|
|
parents.each do |parent|
|
||||||
eval("@#{parent}_not_done_counts = @#{parent}_not_done_counts || Todo.count(:conditions => ['user_id = ? and state = ?', current_user.id, 'active'], :group => :#{parent}_id)")
|
eval("@#{parent}_not_done_counts = @#{parent}_not_done_counts || current_user.todos.active.count(:group => :#{parent}_id)")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_project_hidden_todo_counts(parents = ['project','context'])
|
def init_project_hidden_todo_counts(parents = ['project','context'])
|
||||||
parents.each do |parent|
|
parents.each do |parent|
|
||||||
eval("@#{parent}_project_hidden_todo_counts = @#{parent}_project_hidden_todo_counts || Todo.count(:conditions => ['user_id = ? and (state = ? or state = ?)', current_user.id, 'project_hidden', 'active'], :group => :#{parent}_id)")
|
eval("@#{parent}_project_hidden_todo_counts = @#{parent}_project_hidden_todo_counts || current_user.todos.count(:conditions => ['state = ? or state = ?', 'project_hidden', 'active'], :group => :#{parent}_id)")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,8 +134,8 @@ class ContextsController < ApplicationController
|
||||||
def render_contexts_mobile
|
def render_contexts_mobile
|
||||||
lambda do
|
lambda do
|
||||||
@page_title = "TRACKS::List Contexts"
|
@page_title = "TRACKS::List Contexts"
|
||||||
@active_contexts = @contexts.find(:all, { :conditions => ["hide = ?", false]})
|
@active_contexts = @contexts.active
|
||||||
@hidden_contexts = @contexts.find(:all, { :conditions => ["hide = ?", true]})
|
@hidden_contexts = @contexts.hidden
|
||||||
@down_count = @active_contexts.size + @hidden_contexts.size
|
@down_count = @active_contexts.size + @hidden_contexts.size
|
||||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||||
render :action => 'index_mobile'
|
render :action => 'index_mobile'
|
||||||
|
|
@ -201,6 +201,9 @@ class ContextsController < ApplicationController
|
||||||
:conditions => ['todos.state = ? AND (todos.project_id IS ? OR projects.state = ?)', 'active', nil, 'active'],
|
:conditions => ['todos.state = ? AND (todos.project_id IS ? OR projects.state = ?)', 'active', nil, 'active'],
|
||||||
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
|
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
|
||||||
:include => [:project, :tags])
|
:include => [:project, :tags])
|
||||||
|
|
||||||
|
@projects = current_user.projects
|
||||||
|
|
||||||
@count = @not_done_todos.size
|
@count = @not_done_todos.size
|
||||||
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
|
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ class FeedlistController < ApplicationController
|
||||||
@contexts = current_user.contexts
|
@contexts = current_user.contexts
|
||||||
end
|
end
|
||||||
|
|
||||||
@active_projects = @projects.select{ |p| p.active? }
|
@active_projects = current_user.projects.active
|
||||||
@hidden_projects = @projects.select{ |p| p.hidden? }
|
@hidden_projects = current_user.projects.hidden
|
||||||
@completed_projects = @projects.select{ |p| p.completed? }
|
@completed_projects = current_user.projects.completed
|
||||||
|
|
||||||
@active_contexts = @contexts.select{ |c| !c.hidden? }
|
@active_contexts = current_user.contexts.active
|
||||||
@hidden_contexts = @contexts.select{ |c| c.hidden? }
|
@hidden_contexts = current_user.contexts.hidden
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :layout => 'standard' }
|
format.html { render :layout => 'standard' }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
class IntegrationsController < ApplicationController
|
class IntegrationsController < ApplicationController
|
||||||
|
|
||||||
|
skip_before_filter :login_required, :only => :search_plugin
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@page_title = 'TRACKS::Integrations'
|
@page_title = 'TRACKS::Integrations'
|
||||||
end
|
end
|
||||||
|
|
@ -22,4 +24,12 @@ class IntegrationsController < ApplicationController
|
||||||
context = current_user.contexts.find params[:context_id]
|
context = current_user.contexts.find params[:context_id]
|
||||||
render :partial => 'applescript2', :locals => { :context => context }
|
render :partial => 'applescript2', :locals => { :context => context }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def search_plugin
|
||||||
|
@icon_data = [File.open(RAILS_ROOT + '/public/images/done.png').read].
|
||||||
|
pack('m').gsub(/\n/, '')
|
||||||
|
|
||||||
|
render :layout => false
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,114 +6,45 @@ class LoginController < ApplicationController
|
||||||
skip_before_filter :login_required
|
skip_before_filter :login_required
|
||||||
before_filter :login_optional
|
before_filter :login_optional
|
||||||
before_filter :get_current_user
|
before_filter :get_current_user
|
||||||
open_id_consumer if openid_enabled?
|
|
||||||
|
|
||||||
def login
|
def login
|
||||||
@page_title = "TRACKS::Login"
|
if openid_enabled? && using_open_id?
|
||||||
@openid_url = cookies[:openid_url] if openid_enabled?
|
login_openid
|
||||||
case request.method
|
else
|
||||||
when :post
|
@page_title = "TRACKS::Login"
|
||||||
if @user = User.authenticate(params['user_login'], params['user_password'])
|
case request.method
|
||||||
session['user_id'] = @user.id
|
when :post
|
||||||
# If checkbox on login page checked, we don't expire the session after 1 hour
|
if @user = User.authenticate(params['user_login'], params['user_password'])
|
||||||
# of inactivity and we remember this user for future browser sessions
|
session['user_id'] = @user.id
|
||||||
session['noexpiry'] = params['user_noexpiry']
|
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
# of inactivity and we remember this user for future browser sessions
|
||||||
notify :notice, "Login successful: session #{msg}"
|
session['noexpiry'] = params['user_noexpiry']
|
||||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||||
unless should_expire_sessions?
|
notify :notice, "Login successful: session #{msg}"
|
||||||
@user.remember_me
|
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
||||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
unless should_expire_sessions?
|
||||||
|
@user.remember_me
|
||||||
|
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
||||||
|
end
|
||||||
|
redirect_back_or_home
|
||||||
|
return
|
||||||
|
else
|
||||||
|
@login = params['user_login']
|
||||||
|
notify :warning, "Login unsuccessful"
|
||||||
end
|
end
|
||||||
redirect_back_or_home
|
when :get
|
||||||
return
|
if User.no_users_yet?
|
||||||
else
|
redirect_to :controller => 'users', :action => 'new'
|
||||||
@login = params['user_login']
|
return
|
||||||
notify :warning, "Login unsuccessful"
|
end
|
||||||
end
|
end
|
||||||
when :get
|
respond_to do |format|
|
||||||
if User.no_users_yet?
|
format.html
|
||||||
redirect_to :controller => 'users', :action => 'new'
|
format.m { render :action => 'login_mobile.html.erb', :layout => 'mobile' }
|
||||||
return
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
respond_to do |format|
|
|
||||||
format.html
|
|
||||||
format.m { render :action => 'login_mobile.html.erb', :layout => 'mobile' }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin
|
|
||||||
# If the URL was unusable (either because of network conditions,
|
|
||||||
# a server error, or that the response returned was not an OpenID
|
|
||||||
# identity page), the library will return HTTP_FAILURE or PARSE_ERROR.
|
|
||||||
# Let the user know that the URL is unusable.
|
|
||||||
case open_id_response.status
|
|
||||||
when OpenID::SUCCESS
|
|
||||||
session['openid_url'] = params[:openid_url]
|
|
||||||
session['user_noexpiry'] = params[:user_noexpiry]
|
|
||||||
# The URL was a valid identity URL. Now we just need to send a redirect
|
|
||||||
# to the server using the redirect_url the library created for us.
|
|
||||||
|
|
||||||
# redirect to the server
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), open_id_complete_url) }
|
|
||||||
format.m { redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), formatted_open_id_complete_url(:format => 'm')) }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
notify :warning, "Unable to find openid server for <q>#{openid_url}</q>"
|
|
||||||
redirect_to_login
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def complete
|
|
||||||
openid_url = session['openid_url']
|
|
||||||
if openid_url.blank?
|
|
||||||
notify :error, "expected an openid_url"
|
|
||||||
end
|
|
||||||
|
|
||||||
case open_id_response.status
|
|
||||||
when OpenID::FAILURE
|
|
||||||
# In the case of failure, if info is non-nil, it is the
|
|
||||||
# URL that we were verifying. We include it in the error
|
|
||||||
# message to help the user figure out what happened.
|
|
||||||
if open_id_response.identity_url
|
|
||||||
msg = "Verification of #{openid_url}(#{open_id_response.identity_url}) failed. "
|
|
||||||
else
|
|
||||||
msg = "Verification failed. "
|
|
||||||
end
|
|
||||||
notify :error, open_id_response.msg.to_s + msg
|
|
||||||
|
|
||||||
when OpenID::SUCCESS
|
|
||||||
# Success means that the transaction completed without
|
|
||||||
# error. If info is nil, it means that the user cancelled
|
|
||||||
# the verification.
|
|
||||||
@user = User.find_by_open_id_url(openid_url)
|
|
||||||
unless (@user.nil?)
|
|
||||||
session['user_id'] = @user.id
|
|
||||||
session['noexpiry'] = session['user_noexpiry']
|
|
||||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
|
||||||
notify :notice, "You have successfully verified #{openid_url} as your identity. Login successful: session #{msg}"
|
|
||||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
|
||||||
unless should_expire_sessions?
|
|
||||||
@user.remember_me
|
|
||||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
|
||||||
end
|
|
||||||
cookies[:openid_url] = { :value => openid_url, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
|
||||||
redirect_back_or_home
|
|
||||||
else
|
|
||||||
notify :warning, "You have successfully verified #{openid_url} as your identity, but you do not have a Tracks account. Please ask your administrator to sign you up."
|
|
||||||
end
|
|
||||||
|
|
||||||
when OpenID::CANCEL
|
|
||||||
notify :warning, "Verification cancelled."
|
|
||||||
|
|
||||||
else
|
|
||||||
notify :warning, "Unknown response status: #{open_id_response.status}"
|
|
||||||
end
|
|
||||||
redirect_to_login unless performed?
|
|
||||||
end
|
|
||||||
|
|
||||||
def logout
|
def logout
|
||||||
@user.forget_me if logged_in?
|
@user.forget_me if logged_in?
|
||||||
cookies.delete :auth_token
|
cookies.delete :auth_token
|
||||||
|
|
@ -156,5 +87,31 @@ class LoginController < ApplicationController
|
||||||
def should_expire_sessions?
|
def should_expire_sessions?
|
||||||
session['noexpiry'] != "on"
|
session['noexpiry'] != "on"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def login_openid
|
||||||
|
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||||
|
# of inactivity and we remember this user for future browser sessions
|
||||||
|
session['noexpiry'] ||= params['user_noexpiry']
|
||||||
|
authenticate_with_open_id do |result, identity_url|
|
||||||
|
if result.successful?
|
||||||
|
if @user = User.find_by_open_id_url(identity_url)
|
||||||
|
session['user_id'] = @user.id
|
||||||
|
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||||
|
notify :notice, "Login successful: session #{msg}"
|
||||||
|
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
||||||
|
unless should_expire_sessions?
|
||||||
|
@user.remember_me
|
||||||
|
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
||||||
|
end
|
||||||
|
redirect_back_or_home
|
||||||
|
else
|
||||||
|
notify :warning, "Sorry, no user by that identity URL exists (#{identity_url})"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
notify :warning, result.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class ProjectsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def projects_and_actions
|
def projects_and_actions
|
||||||
@projects = @projects.select { |p| p.active? }
|
@projects = @projects.active
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.text {
|
format.text {
|
||||||
render :action => 'index_text_projects_and_actions', :layout => false, :content_type => Mime::TEXT
|
render :action => 'index_text_projects_and_actions', :layout => false, :content_type => Mime::TEXT
|
||||||
|
|
@ -83,7 +83,7 @@ class ProjectsController < ApplicationController
|
||||||
@go_to_project = params['go_to_project']
|
@go_to_project = params['go_to_project']
|
||||||
@saved = @project.save
|
@saved = @project.save
|
||||||
@project_not_done_counts = { @project.id => 0 }
|
@project_not_done_counts = { @project.id => 0 }
|
||||||
@active_projects_count = current_user.projects.count(:conditions => "state = 'active'")
|
@active_projects_count = current_user.projects.active.count
|
||||||
@contexts = current_user.contexts
|
@contexts = current_user.contexts
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.js { @down_count = current_user.projects.size }
|
format.js { @down_count = current_user.projects.size }
|
||||||
|
|
@ -124,9 +124,9 @@ class ProjectsController < ApplicationController
|
||||||
@project_not_done_counts[@project.id] = @project.reload().not_done_todo_count(:include_project_hidden_todos => true)
|
@project_not_done_counts[@project.id] = @project.reload().not_done_todo_count(:include_project_hidden_todos => true)
|
||||||
end
|
end
|
||||||
@contexts = current_user.contexts
|
@contexts = current_user.contexts
|
||||||
@active_projects_count = current_user.projects.count(:conditions => "state = 'active'")
|
@active_projects_count = current_user.projects.active.count
|
||||||
@hidden_projects_count = current_user.projects.count(:conditions => "state = 'hidden'")
|
@hidden_projects_count = current_user.projects.hidden.count
|
||||||
@completed_projects_count = current_user.projects.count(:conditions => "state = 'completed'")
|
@completed_projects_count = current_user.projects.completed.count
|
||||||
render :template => 'projects/update.js.rjs'
|
render :template => 'projects/update.js.rjs'
|
||||||
return
|
return
|
||||||
elsif boolean_param('update_status')
|
elsif boolean_param('update_status')
|
||||||
|
|
@ -161,9 +161,9 @@ class ProjectsController < ApplicationController
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@project.destroy
|
@project.destroy
|
||||||
@active_projects_count = current_user.projects.count(:conditions => "state = 'active'")
|
@active_projects_count = current_user.projects.active.count
|
||||||
@hidden_projects_count = current_user.projects.count(:conditions => "state = 'hidden'")
|
@hidden_projects_count = current_user.projects.hidden.count
|
||||||
@completed_projects_count = current_user.projects.count(:conditions => "state = 'completed'")
|
@completed_projects_count = current_user.projects.completed.count
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.js { @down_count = current_user.projects.size }
|
format.js { @down_count = current_user.projects.size }
|
||||||
format.xml { render :text => "Deleted project #{@project.name}" }
|
format.xml { render :text => "Deleted project #{@project.name}" }
|
||||||
|
|
@ -199,9 +199,9 @@ class ProjectsController < ApplicationController
|
||||||
lambda do
|
lambda do
|
||||||
@page_title = "TRACKS::List Projects"
|
@page_title = "TRACKS::List Projects"
|
||||||
@count = current_user.projects.size
|
@count = current_user.projects.size
|
||||||
@active_projects = @projects.select{ |p| p.active? }
|
@active_projects = @projects.active
|
||||||
@hidden_projects = @projects.select{ |p| p.hidden? }
|
@hidden_projects = @projects.hidden
|
||||||
@completed_projects = @projects.select{ |p| p.completed? }
|
@completed_projects = @projects.completed
|
||||||
@no_projects = @projects.empty?
|
@no_projects = @projects.empty?
|
||||||
@projects.cache_note_counts
|
@projects.cache_note_counts
|
||||||
@new_project = current_user.projects.build
|
@new_project = current_user.projects.build
|
||||||
|
|
@ -211,9 +211,9 @@ class ProjectsController < ApplicationController
|
||||||
|
|
||||||
def render_projects_mobile
|
def render_projects_mobile
|
||||||
lambda do
|
lambda do
|
||||||
@active_projects = @projects.select{ |p| p.active? }
|
@active_projects = @projects.active
|
||||||
@hidden_projects = @projects.select{ |p| p.hidden? }
|
@hidden_projects = @projects.hidden
|
||||||
@completed_projects = @projects.select{ |p| p.completed? }
|
@completed_projects = @projects.completed
|
||||||
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
|
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
|
||||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||||
render :action => 'index_mobile'
|
render :action => 'index_mobile'
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ class RecurringTodosController < ApplicationController
|
||||||
else
|
else
|
||||||
@message += " / did not create todo"
|
@message += " / did not create todo"
|
||||||
end
|
end
|
||||||
@count = current_user.recurring_todos.count(:all, :conditions => ["state = ?", "active"])
|
@count = current_user.recurring_todos.active.count
|
||||||
else
|
else
|
||||||
@message = "Error saving recurring todo"
|
@message = "Error saving recurring todo"
|
||||||
end
|
end
|
||||||
|
|
@ -140,7 +140,7 @@ class RecurringTodosController < ApplicationController
|
||||||
|
|
||||||
# delete the recurring todo
|
# delete the recurring todo
|
||||||
@saved = @recurring_todo.destroy
|
@saved = @recurring_todo.destroy
|
||||||
@remaining = current_user.recurring_todos.count(:all)
|
@remaining = current_user.recurring_todos.count
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,30 @@
|
||||||
class SearchController < ApplicationController
|
class SearchController < ApplicationController
|
||||||
|
|
||||||
helper :todos, :application, :notes, :projects
|
helper :todos, :application, :notes, :projects
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@source_view = params['_source_view'] || 'search'
|
@source_view = params['_source_view'] || 'search'
|
||||||
@page_title = "TRACKS::Search Results for #{params[:search]}"
|
@page_title = "TRACKS::Search Results for #{params[:search]}"
|
||||||
terms = '%' + params[:search] + '%'
|
terms = '%' + params[:search] + '%'
|
||||||
@found_todos = current_user.todos.find(:all, :conditions => ["todos.description LIKE ? OR todos.notes LIKE ?", terms, terms], :include => [:tags, :project, :context])
|
@found_todos = current_user.todos.find(:all, :conditions => ["todos.description LIKE ? OR todos.notes LIKE ?", terms, terms], :include => [:tags, :project, :context])
|
||||||
@found_projects = current_user.projects.find(:all, :conditions => ["name LIKE ? or description LIKE ?", terms, terms])
|
@found_projects = current_user.projects.find(:all, :conditions => ["name LIKE ? OR description LIKE ?", terms, terms])
|
||||||
@found_notes = current_user.notes.find(:all, :conditions => ["body LIKE ?", terms])
|
@found_notes = current_user.notes.find(:all, :conditions => ["body LIKE ?", terms])
|
||||||
|
@found_contexts = current_user.contexts.find(:all, :conditions => ["name LIKE ?", terms])
|
||||||
@count = @found_todos.size + @found_projects.size + @found_notes.size
|
# TODO: limit search to tags on todos
|
||||||
|
@found_tags = current_user.tags.find(:all, :conditions => ["name LIKE ?", terms])
|
||||||
init_not_done_counts(['project'])
|
|
||||||
init_project_hidden_todo_counts(['project'])
|
@count = @found_todos.size + @found_projects.size + @found_notes.size + @found_contexts.size + @found_tags.size
|
||||||
end
|
|
||||||
|
init_not_done_counts
|
||||||
def index
|
init_project_hidden_todo_counts
|
||||||
@page_title = "TRACKS::Search"
|
end
|
||||||
end
|
|
||||||
|
def index
|
||||||
def init
|
@page_title = "TRACKS::Search"
|
||||||
@source_view = params['_source_view'] || 'search'
|
end
|
||||||
end
|
|
||||||
|
def init
|
||||||
end
|
@source_view = params['_source_view'] || 'search'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ class StatsController < ApplicationController
|
||||||
@page_title = 'TRACKS::Statistics'
|
@page_title = 'TRACKS::Statistics'
|
||||||
|
|
||||||
@unique_tags = @tags.count(:all, {:group=>"tag_id"})
|
@unique_tags = @tags.count(:all, {:group=>"tag_id"})
|
||||||
@hidden_contexts = @contexts.find(:all, {:conditions => ["hide = ? ", true]})
|
@hidden_contexts = @contexts.hidden
|
||||||
@first_action = @actions.find(:first, :order => "created_at ASC")
|
@first_action = @actions.find(:first, :order => "created_at ASC")
|
||||||
|
|
||||||
get_stats_actions
|
get_stats_actions
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class TodosController < ApplicationController
|
||||||
@projects = current_user.projects.find(:all, :include => [:default_context])
|
@projects = current_user.projects.find(:all, :include => [:default_context])
|
||||||
@contexts = current_user.contexts.find(:all)
|
@contexts = current_user.contexts.find(:all)
|
||||||
|
|
||||||
@contexts_to_show = @contexts.reject {|x| x.hide? }
|
@contexts_to_show = current_user.contexts.active
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html &render_todos_html
|
format.html &render_todos_html
|
||||||
|
|
@ -28,7 +28,7 @@ class TodosController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@projects = current_user.projects.select { |p| p.active? }
|
@projects = current_user.projects.active
|
||||||
@contexts = current_user.contexts.find(:all)
|
@contexts = current_user.contexts.find(:all)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.m {
|
format.m {
|
||||||
|
|
@ -76,7 +76,7 @@ class TodosController < ApplicationController
|
||||||
format.m do
|
format.m do
|
||||||
@return_path=cookies[:mobile_url]
|
@return_path=cookies[:mobile_url]
|
||||||
# todo: use function for this fixed path
|
# todo: use function for this fixed path
|
||||||
@return_path='/mobile' if @return_path.nil?
|
@return_path='/m' if @return_path.nil?
|
||||||
if @saved
|
if @saved
|
||||||
redirect_to @return_path
|
redirect_to @return_path
|
||||||
else
|
else
|
||||||
|
|
@ -116,7 +116,7 @@ class TodosController < ApplicationController
|
||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.m do
|
format.m do
|
||||||
@projects = current_user.projects.select { |p| p.active? }
|
@projects = current_user.projects.active
|
||||||
@contexts = current_user.contexts.find(:all)
|
@contexts = current_user.contexts.find(:all)
|
||||||
@edit_mobile = true
|
@edit_mobile = true
|
||||||
@return_path=cookies[:mobile_url]
|
@return_path=cookies[:mobile_url]
|
||||||
|
|
@ -399,8 +399,9 @@ class TodosController < ApplicationController
|
||||||
:limit => max_completed,
|
:limit => max_completed,
|
||||||
:conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'completed'],
|
:conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'completed'],
|
||||||
:order => 'todos.completed_at DESC')
|
:order => 'todos.completed_at DESC')
|
||||||
|
|
||||||
@contexts = current_user.contexts.find(:all)
|
@projects = current_user.projects
|
||||||
|
@contexts = current_user.contexts
|
||||||
@contexts_to_show = @contexts.reject {|x| x.hide? }
|
@contexts_to_show = @contexts.reject {|x| x.hide? }
|
||||||
|
|
||||||
# Set count badge to number of items with this tag
|
# Set count badge to number of items with this tag
|
||||||
|
|
@ -442,33 +443,33 @@ class TodosController < ApplicationController
|
||||||
due_next_week_date = due_this_week_date + 7.days
|
due_next_week_date = due_this_week_date + 7.days
|
||||||
due_this_month_date = Time.zone.now.end_of_month
|
due_this_month_date = Time.zone.now.end_of_month
|
||||||
|
|
||||||
@due_today = current_user.todos.find(:all,
|
@due_today = current_user.todos.not_completed.find(:all,
|
||||||
:include => [:taggings, :tags],
|
:include => [:taggings, :tags],
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due <= ?', 'active', 'deferred', due_today_date],
|
:conditions => ['todos.due <= ?', due_today_date],
|
||||||
:order => "due")
|
:order => "due")
|
||||||
@due_this_week = current_user.todos.find(:all,
|
@due_this_week = current_user.todos.not_completed.find(:all,
|
||||||
:include => [:taggings, :tags],
|
:include => [:taggings, :tags],
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ? AND todos.due <= ?', 'active', 'deferred', due_today_date, due_this_week_date],
|
:conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date],
|
||||||
:order => "due")
|
:order => "due")
|
||||||
@due_next_week = current_user.todos.find(:all,
|
@due_next_week = current_user.todos.not_completed.find(:all,
|
||||||
:include => [:taggings, :tags],
|
:include => [:taggings, :tags],
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ? AND todos.due <= ?', 'active', 'deferred', due_this_week_date, due_next_week_date],
|
:conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date],
|
||||||
:order => "due")
|
:order => "due")
|
||||||
@due_this_month = current_user.todos.find(:all,
|
@due_this_month = current_user.todos.not_completed.find(:all,
|
||||||
:include => [:taggings, :tags],
|
:include => [:taggings, :tags],
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ? AND todos.due <= ?', 'active', 'deferred', due_next_week_date, due_this_month_date],
|
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date],
|
||||||
:order => "due")
|
:order => "due")
|
||||||
@due_after_this_month = current_user.todos.find(:all,
|
@due_after_this_month = current_user.todos.not_completed.find(:all,
|
||||||
:include => [:taggings, :tags],
|
:include => [:taggings, :tags],
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ?', 'active', 'deferred', due_this_month_date],
|
:conditions => ['todos.due > ?', due_this_month_date],
|
||||||
:order => "due")
|
:order => "due")
|
||||||
|
|
||||||
|
@count = current_user.todos.not_completed.are_due.count
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
format.ics {
|
format.ics {
|
||||||
@due_all = current_user.todos.find(:all,
|
@due_all = current_user.todos.not_completed.are_due.find(:all, :order => "due")
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND NOT todos.due IS NULL', 'active', 'deferred'],
|
|
||||||
:order => "due")
|
|
||||||
render :action => 'calendar', :layout => false, :content_type => Mime::ICS
|
render :action => 'calendar', :layout => false, :content_type => Mime::ICS
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
@ -536,13 +537,17 @@ class TodosController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_parent_resource_scope(&block)
|
def with_parent_resource_scope(&block)
|
||||||
|
@feed_title = "Actions "
|
||||||
if (params[:context_id])
|
if (params[:context_id])
|
||||||
@context = current_user.contexts.find_by_params(params)
|
@context = current_user.contexts.find_by_params(params)
|
||||||
|
@feed_title = @feed_title + "in context '#{@context.name}'"
|
||||||
Todo.send :with_scope, :find => {:conditions => ['todos.context_id = ?', @context.id]} do
|
Todo.send :with_scope, :find => {:conditions => ['todos.context_id = ?', @context.id]} do
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
elsif (params[:project_id])
|
elsif (params[:project_id])
|
||||||
@project = current_user.projects.find_by_params(params)
|
@project = current_user.projects.find_by_params(params)
|
||||||
|
@feed_title = @feed_title + "in project '#{@project.name}'"
|
||||||
|
@project_feed = true
|
||||||
Todo.send :with_scope, :find => {:conditions => ['todos.project_id = ?', @project.id]} do
|
Todo.send :with_scope, :find => {:conditions => ['todos.project_id = ?', @project.id]} do
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
|
|
@ -718,7 +723,7 @@ class TodosController < ApplicationController
|
||||||
render_rss_feed_for @todos, :feed => todo_feed_options,
|
render_rss_feed_for @todos, :feed => todo_feed_options,
|
||||||
:item => {
|
:item => {
|
||||||
:title => :description,
|
:title => :description,
|
||||||
:link => lambda { |t| context_url(t.context) },
|
:link => lambda { |t| @project_feed.nil? ? context_url(t.context) : project_url(t.project) },
|
||||||
:guid => lambda { |t| todo_url(t) },
|
:guid => lambda { |t| todo_url(t) },
|
||||||
:description => todo_feed_content
|
:description => todo_feed_content
|
||||||
}
|
}
|
||||||
|
|
@ -726,7 +731,9 @@ class TodosController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def todo_feed_options
|
def todo_feed_options
|
||||||
Todo.feed_options(current_user)
|
options = Todo.feed_options(current_user)
|
||||||
|
options[:title] = @feed_title
|
||||||
|
return options
|
||||||
end
|
end
|
||||||
|
|
||||||
def todo_feed_content
|
def todo_feed_content
|
||||||
|
|
@ -779,8 +786,8 @@ class TodosController < ApplicationController
|
||||||
if todo.from_recurring_todo?
|
if todo.from_recurring_todo?
|
||||||
recurring_todo = todo.recurring_todo
|
recurring_todo = todo.recurring_todo
|
||||||
|
|
||||||
# check if there are active todos belonging to this recurring todo.
|
# check if there are active todos belonging to this recurring todo. only
|
||||||
# only add new one if all active todos are completed
|
# add new one if all active todos are completed
|
||||||
if recurring_todo.todos.active.count == 0
|
if recurring_todo.todos.active.count == 0
|
||||||
|
|
||||||
# check for next todo either from the due date or the show_from date
|
# check for next todo either from the due date or the show_from date
|
||||||
|
|
@ -788,7 +795,7 @@ class TodosController < ApplicationController
|
||||||
|
|
||||||
# if both due and show_from are nil, check for a next todo from now
|
# if both due and show_from are nil, check for a next todo from now
|
||||||
date_to_check = Time.zone.now if date_to_check.nil?
|
date_to_check = Time.zone.now if date_to_check.nil?
|
||||||
|
|
||||||
if recurring_todo.active? && recurring_todo.has_next_todo(date_to_check)
|
if recurring_todo.active? && recurring_todo.has_next_todo(date_to_check)
|
||||||
|
|
||||||
# shift the reference date to yesterday if date_to_check is furher in
|
# shift the reference date to yesterday if date_to_check is furher in
|
||||||
|
|
@ -799,7 +806,7 @@ class TodosController < ApplicationController
|
||||||
# that new todos due for today will be created instead of new todos
|
# that new todos due for today will be created instead of new todos
|
||||||
# for tomorrow.
|
# for tomorrow.
|
||||||
date = date_to_check.at_midnight >= Time.zone.now.at_midnight ? date_to_check : Time.zone.now-1.day
|
date = date_to_check.at_midnight >= Time.zone.now.at_midnight ? date_to_check : Time.zone.now-1.day
|
||||||
|
|
||||||
new_recurring_todo = create_todo_from_recurring_todo(recurring_todo, date)
|
new_recurring_todo = create_todo_from_recurring_todo(recurring_todo, date)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -834,20 +841,20 @@ class TodosController < ApplicationController
|
||||||
due_this_month_date = Time.zone.now.end_of_month
|
due_this_month_date = Time.zone.now.end_of_month
|
||||||
case id
|
case id
|
||||||
when "due_today"
|
when "due_today"
|
||||||
return 0 == current_user.todos.count(:all,
|
return 0 == current_user.todos.not_completed.count(:all,
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due <= ?', 'active', 'deferred', due_today_date])
|
:conditions => ['todos.due <= ?', due_today_date])
|
||||||
when "due_this_week"
|
when "due_this_week"
|
||||||
return 0 == current_user.todos.count(:all,
|
return 0 == current_user.todos.not_completed.count(:all,
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ? AND todos.due <= ?', 'active', 'deferred', due_today_date, due_this_week_date])
|
:conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date])
|
||||||
when "due_next_week"
|
when "due_next_week"
|
||||||
return 0 == current_user.todos.count(:all,
|
return 0 == current_user.todos.not_completed.count(:all,
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ? AND todos.due <= ?', 'active', 'deferred', due_this_week_date, due_next_week_date])
|
:conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date])
|
||||||
when "due_this_month"
|
when "due_this_month"
|
||||||
return 0 == current_user.todos.count(:all,
|
return 0 == current_user.todos.not_completed.count(:all,
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ? AND todos.due <= ?', 'active', 'deferred', due_next_week_date, due_this_month_date])
|
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date])
|
||||||
when "due_after_this_month"
|
when "due_after_this_month"
|
||||||
return 0 == current_user.todos.count(:all,
|
return 0 == current_user.todos.not_completed.count(:all,
|
||||||
:conditions => ['(todos.state = ? OR todos.state = ?) AND todos.due > ?', 'active', 'deferred', due_this_month_date])
|
:conditions => ['todos.due > ?', due_this_month_date])
|
||||||
else
|
else
|
||||||
raise Exception.new, "unknown due id for calendar: '#{id}'"
|
raise Exception.new, "unknown due id for calendar: '#{id}'"
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
|
|
||||||
if openid_enabled?
|
|
||||||
open_id_consumer
|
|
||||||
before_filter :begin_open_id_auth, :only => :update_auth_type
|
|
||||||
end
|
|
||||||
|
|
||||||
before_filter :admin_login_required, :only => [ :index, :show, :destroy ]
|
before_filter :admin_login_required, :only => [ :index, :show, :destroy ]
|
||||||
skip_before_filter :login_required, :only => [ :new, :create ]
|
skip_before_filter :login_required, :only => [ :new, :create ]
|
||||||
prepend_before_filter :login_optional, :only => [ :new, :create ]
|
prepend_before_filter :login_optional, :only => [ :new, :create ]
|
||||||
|
|
@ -153,18 +147,25 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_auth_type
|
def update_auth_type
|
||||||
if (params[:user][:auth_type] == 'open_id') && openid_enabled?
|
if (params[:open_id_complete] || (params[:user][:auth_type] == 'open_id')) && openid_enabled?
|
||||||
case open_id_response.status
|
authenticate_with_open_id do |result, identity_url|
|
||||||
when OpenID::SUCCESS
|
if result.successful?
|
||||||
# The URL was a valid identity URL. Now we just need to send a redirect
|
# Success means that the transaction completed without
|
||||||
# to the server using the redirect_url the library created for us.
|
# error. If info is nil, it means that the user cancelled
|
||||||
session['openid_url'] = params[:openid_url]
|
# the verification.
|
||||||
|
@user.auth_type = 'open_id'
|
||||||
# redirect to the server
|
@user.open_id_url = identity_url
|
||||||
redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), url_for(:action => 'complete'))
|
if @user.save
|
||||||
|
notify :notice, "You have successfully verified #{identity_url} as your identity and set your authentication type to Open ID."
|
||||||
|
else
|
||||||
|
debugger
|
||||||
|
notify :warning, "You have successfully verified #{identity_url} as your identity but there was a problem saving your authentication preferences."
|
||||||
|
end
|
||||||
|
redirect_to preferences_path
|
||||||
else
|
else
|
||||||
notify :warning, "Unable to find openid server for <q>#{openid_url}</q>"
|
notify :warning, result.message
|
||||||
redirect_to :action => 'change_auth_type'
|
redirect_to :action => 'change_auth_type'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -178,47 +179,6 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def complete
|
|
||||||
return unless openid_enabled?
|
|
||||||
openid_url = session['openid_url']
|
|
||||||
if openid_url.blank?
|
|
||||||
notify :error, "expected an openid_url"
|
|
||||||
end
|
|
||||||
case open_id_response.status
|
|
||||||
when OpenID::FAILURE
|
|
||||||
# In the case of failure, if info is non-nil, it is the
|
|
||||||
# URL that we were verifying. We include it in the error
|
|
||||||
# message to help the user figure out what happened.
|
|
||||||
if open_id_response.identity_url
|
|
||||||
msg = "Verification of #{openid_url}(#{open_id_response.identity_url}) failed. "
|
|
||||||
else
|
|
||||||
msg = "Verification failed. "
|
|
||||||
end
|
|
||||||
notify :error, open_id_response.msg.to_s + msg
|
|
||||||
|
|
||||||
when OpenID::SUCCESS
|
|
||||||
# Success means that the transaction completed without
|
|
||||||
# error. If info is nil, it means that the user cancelled
|
|
||||||
# the verification.
|
|
||||||
@user.auth_type = 'open_id'
|
|
||||||
@user.open_id_url = openid_url
|
|
||||||
if @user.save
|
|
||||||
notify :notice, "You have successfully verified #{openid_url} as your identity and set your authentication type to Open ID."
|
|
||||||
else
|
|
||||||
notify :warning, "You have successfully verified #{openid_url} as your identity but there was a problem saving your authentication preferences."
|
|
||||||
end
|
|
||||||
redirect_to preferences_path
|
|
||||||
|
|
||||||
when OpenID::CANCEL
|
|
||||||
notify :warning, "Verification cancelled."
|
|
||||||
|
|
||||||
else
|
|
||||||
notify :warning, "Unknown response status: #{open_id_response.status}"
|
|
||||||
end
|
|
||||||
redirect_to :action => 'change_auth_type' unless performed?
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_token
|
def refresh_token
|
||||||
@user.generate_token
|
@user.generate_token
|
||||||
@user.save!
|
@user.save!
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ module ApplicationHelper
|
||||||
|
|
||||||
# Replicates the link_to method but also checks request.request_uri to find
|
# Replicates the link_to method but also checks request.request_uri to find
|
||||||
# current page. If that matches the url, the link is marked id = "current"
|
# current page. If that matches the url, the link is marked id = "current"
|
||||||
#
|
#
|
||||||
def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference)
|
def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference)
|
||||||
if html_options
|
if html_options
|
||||||
html_options = html_options.stringify_keys
|
html_options = html_options.stringify_keys
|
||||||
|
|
@ -29,7 +29,7 @@ module ApplicationHelper
|
||||||
|
|
||||||
# Check due date in comparison to today's date Flag up date appropriately with
|
# Check due date in comparison to today's date Flag up date appropriately with
|
||||||
# a 'traffic light' colour code
|
# a 'traffic light' colour code
|
||||||
#
|
#
|
||||||
def due_date(due)
|
def due_date(due)
|
||||||
if due == nil
|
if due == nil
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -62,7 +62,7 @@ module ApplicationHelper
|
||||||
|
|
||||||
# Check due date in comparison to today's date Flag up date appropriately with
|
# Check due date in comparison to today's date Flag up date appropriately with
|
||||||
# a 'traffic light' colour code Modified method for mobile screen
|
# a 'traffic light' colour code Modified method for mobile screen
|
||||||
#
|
#
|
||||||
def due_date_mobile(due)
|
def due_date_mobile(due)
|
||||||
if due == nil
|
if due == nil
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -92,7 +92,7 @@ module ApplicationHelper
|
||||||
# Returns a count of next actions in the given context or project. The result
|
# Returns a count of next actions in the given context or project. The result
|
||||||
# is count and a string descriptor, correctly pluralised if there are no
|
# is count and a string descriptor, correctly pluralised if there are no
|
||||||
# actions or multiple actions
|
# actions or multiple actions
|
||||||
#
|
#
|
||||||
def count_undone_todos_phrase(todos_parent, string="actions")
|
def count_undone_todos_phrase(todos_parent, string="actions")
|
||||||
@controller.count_undone_todos_phrase(todos_parent, string)
|
@controller.count_undone_todos_phrase(todos_parent, string)
|
||||||
end
|
end
|
||||||
|
|
@ -143,5 +143,31 @@ module ApplicationHelper
|
||||||
page.replace 'flash', "<h4 id='flash' class='alert #{type}'>#{message}</h4>"
|
page.replace 'flash', "<h4 id='flash' class='alert #{type}'>#{message}</h4>"
|
||||||
page.visual_effect :fade, 'flash', :duration => fade_duration
|
page.visual_effect :fade, 'flash', :duration => fade_duration
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recurrence_time_span(rt)
|
||||||
|
case rt.ends_on
|
||||||
|
when "no_end_date"
|
||||||
|
return rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
||||||
|
when "ends_on_number_of_times"
|
||||||
|
return "for "+rt.number_of_occurences.to_s + " times"
|
||||||
|
when "ends_on_end_date"
|
||||||
|
starts = rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
||||||
|
ends = rt.end_date.nil? ? "" : " until " + format_date(rt.end_date)
|
||||||
|
return starts+ends
|
||||||
|
else
|
||||||
|
raise Exception.new, "unknown recurrence time span selection (#{rt.ends_on})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def recurrence_pattern_as_text(recurring_todo)
|
||||||
|
rt = recurring_todo.recurring_target_as_text
|
||||||
|
rp = recurring_todo.recurrence_pattern
|
||||||
|
# only add space if recurrence_pattern has content
|
||||||
|
rp = " " + rp if !rp.nil?
|
||||||
|
rts = recurrence_time_span(recurring_todo)
|
||||||
|
# only add space if recurrence_time_span has content
|
||||||
|
rts = " " + rts if !(rts == "")
|
||||||
|
return rt+rp+rts
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
module NotesHelper
|
module NotesHelper
|
||||||
def truncated_note(note, characters = 50)
|
def truncated_note(note, characters = 50)
|
||||||
sanitize(textilize_without_paragraph(truncate(note.body, characters, "...")))
|
sanitize(textilize_without_paragraph(truncate(note.body, :length => characters, :omission => "...")))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ module ProjectsHelper
|
||||||
def project_next_prev
|
def project_next_prev
|
||||||
html = ''
|
html = ''
|
||||||
unless @previous_project.nil?
|
unless @previous_project.nil?
|
||||||
project_name = truncate(@previous_project.name, 40, "...")
|
project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
|
||||||
html << link_to_project(@previous_project, "« #{project_name}")
|
html << link_to_project(@previous_project, "« #{project_name}")
|
||||||
end
|
end
|
||||||
html << ' | ' if @previous_project && @next_project
|
html << ' | ' if @previous_project && @next_project
|
||||||
unless @next_project.nil?
|
unless @next_project.nil?
|
||||||
project_name = truncate(@next_project.name, 40, "...")
|
project_name = truncate(@next_project.name, :length => 40, :omission => "...")
|
||||||
html << link_to_project(@next_project, "#{project_name} »")
|
html << link_to_project(@next_project, "#{project_name} »")
|
||||||
end
|
end
|
||||||
html
|
html
|
||||||
|
|
@ -34,12 +34,12 @@ module ProjectsHelper
|
||||||
def project_next_prev_mobile
|
def project_next_prev_mobile
|
||||||
html = ''
|
html = ''
|
||||||
unless @previous_project.nil?
|
unless @previous_project.nil?
|
||||||
project_name = truncate(@previous_project.name, 40, "...")
|
project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
|
||||||
html << link_to_project_mobile(@previous_project, "5", "« 5-#{project_name}")
|
html << link_to_project_mobile(@previous_project, "5", "« 5-#{project_name}")
|
||||||
end
|
end
|
||||||
html << ' | ' if @previous_project && @next_project
|
html << ' | ' if @previous_project && @next_project
|
||||||
unless @next_project.nil?
|
unless @next_project.nil?
|
||||||
project_name = truncate(@next_project.name, 40, "...")
|
project_name = truncate(@next_project.name, :length => 40, :omission => "...")
|
||||||
html << link_to_project_mobile(@next_project, "6", "6-#{project_name} »")
|
html << link_to_project_mobile(@next_project, "6", "6-#{project_name} »")
|
||||||
end
|
end
|
||||||
html
|
html
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,11 @@
|
||||||
module RecurringTodosHelper
|
module RecurringTodosHelper
|
||||||
|
|
||||||
def recurrence_time_span(rt)
|
|
||||||
case rt.ends_on
|
|
||||||
when "no_end_date"
|
|
||||||
return ""
|
|
||||||
when "ends_on_number_of_times"
|
|
||||||
return "for "+rt.number_of_occurences.to_s + " times"
|
|
||||||
when "ends_on_end_date"
|
|
||||||
starts = rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
|
||||||
ends = rt.end_date.nil? ? "" : " until " + format_date(rt.end_date)
|
|
||||||
return starts+ends
|
|
||||||
else
|
|
||||||
raise Exception.new, "unknown recurrence time span selection (#{self.ends_on})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def recurrence_target(rt)
|
|
||||||
case rt.target
|
|
||||||
when 'due_date'
|
|
||||||
return "due"
|
|
||||||
when 'show_from_date'
|
|
||||||
return "show"
|
|
||||||
else
|
|
||||||
return "ERROR"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def recurring_todo_tag_list
|
def recurring_todo_tag_list
|
||||||
tags_except_starred = @recurring_todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
|
tags_except_starred = @recurring_todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
|
||||||
tag_list = tags_except_starred.collect{|t| "<span class=\"tag #{t.name.gsub(' ','-')}\">" +
|
tag_list = tags_except_starred.collect{|t| "<span class=\"tag #{t.name.gsub(' ','-')}\">" +
|
||||||
# link_to(t.name, :controller => "todos", :action => "tag", :id =>
|
# link_to(t.name, :controller => "todos", :action => "tag", :id =>
|
||||||
# t.name) + TODO: tag view for recurring_todos (yet?)
|
# t.name) + TODO: tag view for recurring_todos (yet?)
|
||||||
t.name +
|
t.name +
|
||||||
"</span>"}.join('')
|
"</span>"}.join('')
|
||||||
"<span class='tags'>#{tag_list}</span>"
|
"<span class='tags'>#{tag_list}</span>"
|
||||||
end
|
end
|
||||||
|
|
@ -44,7 +18,7 @@ module RecurringTodosHelper
|
||||||
str
|
str
|
||||||
end
|
end
|
||||||
|
|
||||||
def recurring_todo_remote_star_icon
|
def recurring_todo_remote_star_icon
|
||||||
str = link_to( image_tag_for_star(@recurring_todo),
|
str = link_to( image_tag_for_star(@recurring_todo),
|
||||||
toggle_star_recurring_todo_path(@recurring_todo),
|
toggle_star_recurring_todo_path(@recurring_todo),
|
||||||
:class => "icon star_item", :title => "star the action '#{@recurring_todo.description}'")
|
:class => "icon star_item", :title => "star the action '#{@recurring_todo.description}'")
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ module TodosHelper
|
||||||
"<span class=\"tag\">" +
|
"<span class=\"tag\">" +
|
||||||
link_to(t.name, {:action => "tag", :controller => "todos", :id => t.name+".m"}) +
|
link_to(t.name, {:action => "tag", :controller => "todos", :id => t.name+".m"}) +
|
||||||
"</span>"}.join('')
|
"</span>"}.join('')
|
||||||
"<span class=\"tags\">#{tag_list}</span>"
|
if tag_list.empty? then "" else "<span class=\"tags\">#{tag_list}</span>" end
|
||||||
end
|
end
|
||||||
|
|
||||||
def deferred_due_date
|
def deferred_due_date
|
||||||
|
|
@ -242,13 +242,13 @@ module TodosHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_names_for_autocomplete
|
def project_names_for_autocomplete
|
||||||
array_or_string_for_javascript( ['None'] + @projects.select{ |p| p.active? }.collect{|p| escape_javascript(p.name) } )
|
array_or_string_for_javascript( ['None'] + current_user.projects.active.collect{|p| escape_javascript(p.name) } )
|
||||||
end
|
end
|
||||||
|
|
||||||
def context_names_for_autocomplete
|
def context_names_for_autocomplete
|
||||||
# #return array_or_string_for_javascript(['Create a new context']) if
|
# #return array_or_string_for_javascript(['Create a new context']) if
|
||||||
# @contexts.empty?
|
# @contexts.empty?
|
||||||
array_or_string_for_javascript( @contexts.collect{|c| escape_javascript(c.name) } )
|
array_or_string_for_javascript( current_user.contexts.collect{|c| escape_javascript(c.name) } )
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_ical_notes(notes)
|
def format_ical_notes(notes)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ class Context < ActiveRecord::Base
|
||||||
|
|
||||||
has_many :todos, :dependent => :delete_all, :include => :project, :order => "todos.completed_at DESC"
|
has_many :todos, :dependent => :delete_all, :include => :project, :order => "todos.completed_at DESC"
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
|
named_scope :active, :conditions => { :hide => false }
|
||||||
|
named_scope :hidden, :conditions => { :hide => true }
|
||||||
|
|
||||||
acts_as_list :scope => :user
|
acts_as_list :scope => :user
|
||||||
extend NamePartFinder
|
extend NamePartFinder
|
||||||
include Tracks::TodoList
|
include Tracks::TodoList
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
class MessageGateway < ActionMailer::Base
|
class MessageGateway < ActionMailer::Base
|
||||||
include ActionView::Helpers::SanitizeHelper
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
||||||
|
|
||||||
def receive(email)
|
def receive(email)
|
||||||
user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip])
|
user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip])
|
||||||
if user.nil?
|
if user.nil?
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@ class Project < ActiveRecord::Base
|
||||||
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
||||||
belongs_to :default_context, :class_name => "Context", :foreign_key => "default_context_id"
|
belongs_to :default_context, :class_name => "Context", :foreign_key => "default_context_id"
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
|
named_scope :active, :conditions => { :state => 'active' }
|
||||||
|
named_scope :hidden, :conditions => { :state => 'hidden' }
|
||||||
|
named_scope :completed, :conditions => { :state => 'completed'}
|
||||||
|
|
||||||
validates_presence_of :name, :message => "project must have a name"
|
validates_presence_of :name, :message => "project must have a name"
|
||||||
validates_length_of :name, :maximum => 255, :message => "project name must be less than 256 characters"
|
validates_length_of :name, :maximum => 255, :message => "project name must be less than 256 characters"
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,17 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
def recurring_target=(t)
|
def recurring_target=(t)
|
||||||
self.target = t
|
self.target = t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recurring_target_as_text
|
||||||
|
case self.target
|
||||||
|
when 'due_date'
|
||||||
|
return "due"
|
||||||
|
when 'show_from_date'
|
||||||
|
return "show"
|
||||||
|
else
|
||||||
|
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def recurring_show_days_before=(days)
|
def recurring_show_days_before=(days)
|
||||||
self.show_from_delta=days
|
self.show_from_delta=days
|
||||||
|
|
@ -361,6 +372,8 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
when 'show_from'
|
when 'show_from'
|
||||||
# so leave due date empty
|
# so leave due date empty
|
||||||
return nil
|
return nil
|
||||||
|
else
|
||||||
|
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -471,12 +484,23 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
start = determine_start(previous)
|
start = determine_start(previous)
|
||||||
day = self.every_other1
|
day = self.every_other1
|
||||||
n = self.every_other2
|
n = self.every_other2
|
||||||
|
|
||||||
case self.recurrence_selector
|
case self.recurrence_selector
|
||||||
when 0 # specific day of the month
|
when 0 # specific day of the month
|
||||||
if start.mday >= day
|
if start.mday >= day
|
||||||
# there is no next day n in this month, search in next month
|
# there is no next day n in this month, search in next month
|
||||||
start += n.months
|
#
|
||||||
|
# start += n.months
|
||||||
|
#
|
||||||
|
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
||||||
|
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
||||||
|
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
||||||
|
# local timezone.
|
||||||
|
#
|
||||||
|
# TODO: recheck if future rails versions have this problem too
|
||||||
|
start = Time.utc(start.year, start.month, start.day)+n.months
|
||||||
|
start = Time.zone.local(start.year, start.month, start.day)
|
||||||
|
|
||||||
# go back to day
|
# go back to day
|
||||||
end
|
end
|
||||||
return Time.zone.local(start.year, start.month, day)
|
return Time.zone.local(start.year, start.month, day)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ class Todo < ActiveRecord::Base
|
||||||
|
|
||||||
named_scope :active, :conditions => { :state => 'active' }
|
named_scope :active, :conditions => { :state => 'active' }
|
||||||
named_scope :not_completed, :conditions => ['NOT state = ? ', 'completed']
|
named_scope :not_completed, :conditions => ['NOT state = ? ', 'completed']
|
||||||
|
named_scope :are_due, :conditions => ['NOT todos.due IS NULL']
|
||||||
|
|
||||||
STARRED_TAG_NAME = "starred"
|
STARRED_TAG_NAME = "starred"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
#for will_paginate plugin
|
#for will_paginate plugin
|
||||||
cattr_accessor :per_page
|
cattr_accessor :per_page
|
||||||
@@per_page = 1
|
@@per_page = 5
|
||||||
|
|
||||||
def validate
|
def validate
|
||||||
unless Tracks::Config.auth_schemes.include?(auth_type)
|
unless Tracks::Config.auth_schemes.include?(auth_type)
|
||||||
|
|
@ -145,8 +145,8 @@ class User < ActiveRecord::Base
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_by_open_id_url(raw_open_id_url)
|
def self.find_by_open_id_url(raw_identity_url)
|
||||||
normalized_open_id_url = normalize_open_id_url(raw_open_id_url)
|
normalized_open_id_url = OpenIdAuthentication.normalize_url(raw_identity_url)
|
||||||
find(:first, :conditions => ['open_id_url = ?', normalized_open_id_url])
|
find(:first, :conditions => ['open_id_url = ?', normalized_open_id_url])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -188,7 +188,7 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def at_midnight(date)
|
def at_midnight(date)
|
||||||
return TimeZone[prefs.time_zone].local(date.year, date.month, date.day, 0, 0, 0)
|
return ActiveSupport::TimeZone[prefs.time_zone].local(date.year, date.month, date.day, 0, 0, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_token
|
def generate_token
|
||||||
|
|
@ -237,13 +237,6 @@ protected
|
||||||
|
|
||||||
def normalize_open_id_url
|
def normalize_open_id_url
|
||||||
return if open_id_url.nil?
|
return if open_id_url.nil?
|
||||||
self.open_id_url = self.class.normalize_open_id_url(open_id_url)
|
self.open_id_url = OpenIdAuthentication.normalize_url(open_id_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.normalize_open_id_url(raw_open_id_url)
|
|
||||||
normalized = raw_open_id_url
|
|
||||||
normalized = "http://#{raw_open_id_url}" unless raw_open_id_url =~ /\:\/\//
|
|
||||||
normalized.downcase.chomp('/')
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
<% context = context_listing %>
|
<% context = context_listing
|
||||||
|
suppress_drag_handle ||= false
|
||||||
|
suppress_edit_button ||= false
|
||||||
|
%>
|
||||||
<div id="<%= dom_id(context, "container") %>" class="list">
|
<div id="<%= dom_id(context, "container") %>" class="list">
|
||||||
<div id="<%= dom_id(context) %>" class="context sortable_row" style="display:'';">
|
<div id="<%= dom_id(context) %>" class="context sortable_row" style="display:'';">
|
||||||
<div class="position">
|
<% unless suppress_drag_handle -%>
|
||||||
<span class="handle">DRAG</span>
|
<div class="position">
|
||||||
</div>
|
<span class="handle">DRAG</span>
|
||||||
|
</div>
|
||||||
|
<% end -%>
|
||||||
<div class="data">
|
<div class="data">
|
||||||
<%= link_to_context( context ) %> <%= " (" + count_undone_todos_phrase(context,"actions") + ")" %>
|
<%= link_to_context( context ) %> <%= " (" + count_undone_todos_phrase(context,"actions") + ")" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,5 +37,5 @@
|
||||||
-%>
|
-%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%= render :partial => 'context_form', :object => context %>
|
<%= render :partial => 'contexts/context_form', :object => context %>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -6,9 +6,9 @@ if not @not_done.empty?
|
||||||
# only show a context when there are actions in it
|
# only show a context when there are actions in it
|
||||||
-%>
|
-%>
|
||||||
<h2><%=mobile_context.name%></h2>
|
<h2><%=mobile_context.name%></h2>
|
||||||
<table cellpadding="0" cellspacing="0" border="0">
|
<ul class="c">
|
||||||
<%= render :partial => "todos/mobile_todo",
|
<%= render :partial => "todos/mobile_todo",
|
||||||
:collection => @not_done,
|
:collection => @not_done,
|
||||||
:locals => { :parent_container_type => "context" }-%>
|
:locals => { :parent_container_type => "context" }-%>
|
||||||
</table>
|
</ul>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,5 @@
|
||||||
|
|
||||||
<div id="input_box">
|
<div id="input_box">
|
||||||
<%= render :partial => "shared/add_new_item_form" %>
|
<%= render :partial => "shared/add_new_item_form" %>
|
||||||
<%= render "sidebar/sidebar" %>
|
<%= render :template => "sidebar/sidebar" %>
|
||||||
</div><!-- End of input box -->
|
</div><!-- End of input box -->
|
||||||
|
|
@ -123,7 +123,7 @@
|
||||||
</div><!-- End of display_box -->
|
</div><!-- End of display_box -->
|
||||||
|
|
||||||
<div id="input_box">
|
<div id="input_box">
|
||||||
<%= render "sidebar/sidebar" %>
|
<%= render :template => "sidebar/sidebar" %>
|
||||||
</div><!-- End of input box -->
|
</div><!-- End of input box -->
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
|
||||||
14
app/views/integrations/search_plugin.rxml
Normal file
14
app/views/integrations/search_plugin.rxml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
xml.instruct!
|
||||||
|
|
||||||
|
xml.OpenSearchDescription 'xmlns' => "http://a9.com/-/spec/opensearch/1.1/" do
|
||||||
|
|
||||||
|
xml.ShortName Tracks
|
||||||
|
xml.Description 'Search in Tracks'
|
||||||
|
xml.InputEncoding 'UTF-8'
|
||||||
|
xml.Image("data:image/x-icon;base64," + @icon_data,
|
||||||
|
'width' => '16', 'height' => '16')
|
||||||
|
xml.Url 'type' => 'text/html', 'method' => 'GET',
|
||||||
|
'template' => url_for(:controller => 'search', :action => 'results',
|
||||||
|
:only_path => false) + '?search={searchTerms}'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -2,35 +2,40 @@
|
||||||
new_todo_params = {}
|
new_todo_params = {}
|
||||||
new_todo_params[:from_project] = @mobile_from_project if @mobile_from_project
|
new_todo_params[:from_project] = @mobile_from_project if @mobile_from_project
|
||||||
new_todo_params[:from_context] = @mobile_from_context if @mobile_from_context
|
new_todo_params[:from_context] = @mobile_from_context if @mobile_from_context
|
||||||
-%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
-%><?xml version="1.0"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<meta name="viewport" content="initial-scale = 1.0" />
|
<meta name="viewport" content="initial-scale = 1.0" />
|
||||||
<%= stylesheet_link_tag "mobile"%>
|
<%= stylesheet_link_tag "mobile", :media => 'handheld,all' %>
|
||||||
<title><%= @page_title %></title>
|
<title><%= @page_title %></title>
|
||||||
</head><body>
|
</head><body>
|
||||||
<% if !(@new_mobile || @edit_mobile)
|
<% if !(@new_mobile || @edit_mobile)
|
||||||
if !@prefs.nil? -%>
|
if !@prefs.nil? -%>
|
||||||
<h1><span class="count"><%= @down_count %></span> <%=
|
<h1><span class="count"><%= @down_count %></span> <%=
|
||||||
user_time.strftime(@prefs.title_date_format) -%></h1>
|
user_time.strftime(@prefs.title_date_format) -%></h1>
|
||||||
<%= (link_to("0-Add new action", formatted_new_todo_path(:m, new_todo_params))+" | ") unless @new_mobile -%>
|
<div class="nav">
|
||||||
|
<%= (link_to("0-New action", formatted_new_todo_path(:m, new_todo_params))+" | ") unless @new_mobile -%>
|
||||||
<%= (link_to("1-Home", formatted_todos_path(:m))+" | ") unless @home -%>
|
<%= (link_to("1-Home", formatted_todos_path(:m))+" | ") unless @home -%>
|
||||||
<%= (link_to("2-Contexts", formatted_contexts_path(:m))+" | ") -%>
|
<%= (link_to("2-Contexts", formatted_contexts_path(:m))+" | ") -%>
|
||||||
<%= (link_to("3-Projects", formatted_projects_path(:m))+" | ") -%>
|
<%= (link_to("3-Projects", formatted_projects_path(:m))+" | ") -%>
|
||||||
<%= (link_to("4-Starred", {:action => "tag", :controller => "todos", :id => "starred.m"})) -%>
|
<%= (link_to("4-Starred", {:action => "tag", :controller => "todos", :id => "starred.m"})) -%>
|
||||||
<% end
|
<% end
|
||||||
end -%><%= render_flash -%>
|
end -%><%= render_flash -%>
|
||||||
<hr/><%= yield -%>
|
</div>
|
||||||
|
<%= yield -%>
|
||||||
<hr/><% if !@prefs.nil? -%>
|
<hr/><% if !@prefs.nil? -%>
|
||||||
|
<div class="nav">
|
||||||
<%= (link_to("Logout", formatted_logout_path(:format => 'm')) +" | ") -%>
|
<%= (link_to("Logout", formatted_logout_path(:format => 'm')) +" | ") -%>
|
||||||
<%= (link_to("0-Add new action", formatted_new_todo_path(:m), {:accesskey => "0"})+" | ") unless @new_mobile -%>
|
<%= (link_to("0-New action", formatted_new_todo_path(:m), {:accesskey => "0"})+" | ") unless @new_mobile -%>
|
||||||
<%= (link_to("1-Home", formatted_todos_path(:m), {:accesskey => "1"})+" | ") unless @home -%>
|
<%= (link_to("1-Home", formatted_todos_path(:m), {:accesskey => "1"})+" | ") unless @home -%>
|
||||||
<%= (link_to("2-Contexts", formatted_contexts_path(:m), {:accesskey => "2"})+" | ") -%>
|
<%= (link_to("2-Contexts", formatted_contexts_path(:m), {:accesskey => "2"})+" | ") -%>
|
||||||
<%= (link_to("3-Projects", formatted_projects_path(:m), {:accesskey => "3"})+" | ") -%>
|
<%= (link_to("3-Projects", formatted_projects_path(:m), {:accesskey => "3"})+" | ") -%>
|
||||||
<%= (link_to("4-Starred", {:action => "tag", :controller => "todos", :id => "starred.m"}, {:accesskey => "4"})+" | ") -%>
|
<%= (link_to("4-Starred", {:action => "tag", :controller => "todos", :id => "starred.m"}, {:accesskey => "4"})+" | ") -%>
|
||||||
<%= (link_to("Tickler", {:action => "index", :controller => "tickler.m"})+" | ") -%>
|
<%= (link_to("Tickler", {:action => "index", :controller => "tickler.m"})+" | ") -%>
|
||||||
<%= (link_to("Feeds", {:action => "index", :controller => "feeds.m"})) -%>
|
<%= (link_to("Feeds", {:action => "index", :controller => "feeds.m"})) -%>
|
||||||
|
</div>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<%= render :partial => "shared/mobile_footer" -%>
|
<%= render :partial => "shared/mobile_footer" -%>
|
||||||
</body></html>
|
</body></html>
|
||||||
|
|
@ -5,13 +5,23 @@
|
||||||
<% if @prefs.refresh != 0 -%>
|
<% if @prefs.refresh != 0 -%>
|
||||||
<meta http-equiv="Refresh" content="<%= @prefs["refresh"].to_i*60 %>;url=<%= request.request_uri %>">
|
<meta http-equiv="Refresh" content="<%= @prefs["refresh"].to_i*60 %>;url=<%= request.request_uri %>">
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<%= javascript_include_merged :tracks %>
|
<% bundle do %>
|
||||||
|
<%= javascript_include_tag *%w[
|
||||||
|
prototype effects dragdrop controls application
|
||||||
|
calendar calendar-en calendar-setup
|
||||||
|
accesskey-hints todo-items niftycube
|
||||||
|
protoload flashobject lowpro
|
||||||
|
] %>
|
||||||
|
<%= stylesheet_link_tag *%w[ standard calendar-system niftyCorners] %>
|
||||||
|
<% end %>
|
||||||
<%= javascript_include_tag :unobtrusive %>
|
<%= javascript_include_tag :unobtrusive %>
|
||||||
<%= stylesheet_link_merged :tracks %>
|
|
||||||
<%= stylesheet_link_tag "print", :media => "print" %>
|
<%= stylesheet_link_tag "print", :media => "print" %>
|
||||||
|
|
||||||
<link rel="shortcut icon" href="<%= url_for(:controller => 'favicon.ico') %>" />
|
<link rel="shortcut icon" href="<%= url_for(:controller => 'favicon.ico') %>" />
|
||||||
<%= auto_discovery_link_tag(:rss, {:controller => "todos", :action => "index", :format => 'rss', :token => "#{current_user.token}"}, {:title => "RSS feed of next actions"}) %>
|
<%= auto_discovery_link_tag(:rss, {:controller => "todos", :action => "index", :format => 'rss', :token => "#{current_user.token}"}, {:title => "RSS feed of next actions"}) %>
|
||||||
|
<link rel="search" type="application/opensearchdescription+xml"
|
||||||
|
title="Tracks"
|
||||||
|
href="<%= search_plugin_path %>" />
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.onload=function(){
|
window.onload=function(){
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,19 @@
|
||||||
<% form_tag :action=> 'login' do %>
|
<% form_tag :action=> 'login' do %>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="user_login">Login:</label></td>
|
<td><label for="user_login">Login:</label></td>
|
||||||
<td width="100px"><input type="text" name="user_login" id="user_login" value="" class="login_text" /></td>
|
<td><input type="text" name="user_login" id="user_login" value="" class="login_text" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="user_password">Password:</label></td>
|
<td><label for="user_password">Password:</label></td>
|
||||||
<td width="100px"><input type="password" name="user_password" id="user_password" class="login_text" /></td>
|
<td><input type="password" name="user_password" id="user_password" class="login_text" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="user_noexpiry">Stay logged in:</label></td>
|
<td><label for="user_noexpiry">Stay logged in:</label></td>
|
||||||
<td width="100px"><input type="checkbox" name="user_noexpiry" id="user_noexpiry" checked /></td>
|
<td><input type="checkbox" name="user_noexpiry" id="user_noexpiry" checked /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"></td>
|
<td></td>
|
||||||
<td><input type="submit" name="login" value="Sign In »" class="primary" /></td>
|
<td><input type="submit" name="login" value="Sign In »" class="primary" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -36,18 +36,18 @@
|
||||||
|
|
||||||
<% if show_openid_form %>
|
<% if show_openid_form %>
|
||||||
<div id="openid_auth_form" style="display:none">
|
<div id="openid_auth_form" style="display:none">
|
||||||
<% form_tag :action=> 'login', :action => 'begin' do %>
|
<% form_tag :action=> 'login' do %>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="openid_url">Identity URL:</label></td>
|
<td><label for="openid_url">Identity URL:</label></td>
|
||||||
<td width="100px"><input type="text" name="openid_url" id="openid_url" value="<%= @openid_url %>" class="login_text open_id" /></td>
|
<td><input type="text" name="openid_url" id="openid_url" value="<%= @openid_url %>" class="login_text open_id" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="user_noexpiry">Stay logged in:</label></td>
|
<td><label for="user_noexpiry">Stay logged in:</label></td>
|
||||||
<td width="100px"><input type="checkbox" name="user_noexpiry" id="user_noexpiry" checked /></td>
|
<td><input type="checkbox" name="user_noexpiry" id="user_noexpiry" checked /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"></td>
|
<td></td>
|
||||||
<td><input type="submit" name="login" value="Sign In »" class="primary" /></td>
|
<td><input type="submit" name="login" value="Sign In »" class="primary" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,19 @@
|
||||||
<% form_tag formatted_login_path(:format => 'm') do %>
|
<% form_tag formatted_login_path(:format => 'm') do %>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="user_login">Login:</label></td>
|
<td><label for="user_login">Login:</label></td>
|
||||||
<td width="100px"><input type="text" name="user_login" id="user_login" value="" class="login_text" /></td>
|
<td><input type="text" name="user_login" id="user_login" value="" class="login_text" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="user_password">Password:</label></td>
|
<td><label for="user_password">Password:</label></td>
|
||||||
<td width="100px"><input type="password" name="user_password" id="user_password" class="login_text" /></td>
|
<td><input type="password" name="user_password" id="user_password" class="login_text" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"><label for="user_noexpiry">Stay logged in:</label></td>
|
<td><label for="user_noexpiry">Stay logged in:</label></td>
|
||||||
<td width="100px"><input type="checkbox" name="user_noexpiry" id="user_noexpiry" checked /></td>
|
<td><input type="checkbox" name="user_noexpiry" id="user_noexpiry" checked="checked" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100px"></td>
|
<td> </td>
|
||||||
<td><input type="submit" name="login" value="Sign In »" class="primary" /></td>
|
<td><input type="submit" name="login" value="Sign In »" class="primary" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="page_name_auto_complete" id="default_context_list" style="display:none;z-index:9999"></div>
|
<div class="page_name_auto_complete" id="default_context_list" style="display:none;z-index:9999"></div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
defaultContextAutoCompleter = new Autocompleter.Local('project[default_context_name]', 'default_context_list', <%= context_names_for_autocomplete %>, {choices:100,autoSelect:false});
|
defaultContextAutoCompleter = new Autocompleter.Local('project_default_context_name', 'default_context_list', <%= context_names_for_autocomplete %>, {choices:100,autoSelect:false});
|
||||||
Event.observe($('project[default_context_name]'), "focus", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
|
Event.observe($('project_default_context_name'), "focus", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
|
||||||
Event.observe($('project[default_context_name]'), "click", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
|
Event.observe($('project_default_context_name'), "click", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -74,5 +74,5 @@
|
||||||
|
|
||||||
<div id="input_box">
|
<div id="input_box">
|
||||||
<%= render :partial => "shared/add_new_item_form" %>
|
<%= render :partial => "shared/add_new_item_form" %>
|
||||||
<%= render "sidebar/sidebar" %>
|
<%= render :template => "sidebar/sidebar" %>
|
||||||
</div><!-- End of input box -->
|
</div><!-- End of input box -->
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<span class="todo.descr"><%= sanitize(recurring_todo.description) %></span> <%= recurring_todo_tag_list %>
|
<span class="todo.descr"><%= sanitize(recurring_todo.description) %></span> <%= recurring_todo_tag_list %>
|
||||||
<span class='recurrence_pattern'>
|
<span class='recurrence_pattern'>
|
||||||
[<%=recurrence_target(recurring_todo)%> <%= recurring_todo.recurrence_pattern %> <%= recurrence_time_span(recurring_todo) %>]
|
[<%= recurrence_pattern_as_text(@recurring_todo) %>]
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ if @recurring_saved
|
||||||
page.hide 'recurring-todos-empty-nd'
|
page.hide 'recurring-todos-empty-nd'
|
||||||
page.insert_html :bottom,
|
page.insert_html :bottom,
|
||||||
'recurring_todos_container',
|
'recurring_todos_container',
|
||||||
:partial => 'recurring_todos/recurring_todo'
|
:partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo}
|
||||||
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
||||||
# update badge count
|
# update badge count
|
||||||
page['badge_count'].replace_html @count
|
page['badge_count'].replace_html @count
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ if @saved
|
||||||
|
|
||||||
if @recurring_todo.completed?
|
if @recurring_todo.completed?
|
||||||
# show completed recurring todo
|
# show completed recurring todo
|
||||||
page.insert_html :top, "completed_recurring_todos_container", :partial => 'recurring_todos/recurring_todo'
|
page.insert_html :top, "completed_recurring_todos_container", :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo }
|
||||||
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
||||||
|
|
||||||
# set empty messages
|
# set empty messages
|
||||||
|
|
@ -14,7 +14,7 @@ if @saved
|
||||||
# recurring_todo is activated
|
# recurring_todo is activated
|
||||||
|
|
||||||
# show completed recurring todo
|
# show completed recurring todo
|
||||||
page.insert_html :top, "recurring_todos_container", :partial => 'recurring_todos/recurring_todo'
|
page.insert_html :top, "recurring_todos_container", :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo }
|
||||||
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
||||||
|
|
||||||
# inform user if a new todo has been created because of the activation
|
# inform user if a new todo has been created because of the activation
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ if @saved
|
||||||
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true" if @new_project_created
|
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true" if @new_project_created
|
||||||
|
|
||||||
# replace old recurring todo with updated todo
|
# replace old recurring todo with updated todo
|
||||||
page.replace dom_id(@recurring_todo), :partial => 'recurring_todos/recurring_todo'
|
page.replace dom_id(@recurring_todo), :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo }
|
||||||
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<div id="display_box_search">
|
<div id="display_box_search">
|
||||||
<% form_tag(:action => :results) do %>
|
<% form_tag({:action => :results}, :id => 'search-form') do %>
|
||||||
<%= text_field_tag(:search, params[:search]) %>
|
<%= text_field_tag(:search, params[:search]) %>
|
||||||
<%= submit_tag "Search" %>
|
<%= submit_tag "Search" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
Form.focusFirstElement('search-form')
|
||||||
|
</script>
|
||||||
|
|
@ -1,32 +1,48 @@
|
||||||
<div id="display_box_results">
|
<div id="display_box_results">
|
||||||
<% if @count == 0 -%>
|
<% if @count == 0 -%>
|
||||||
<div class="message"><p>Your search yielded no results.</p></div>
|
<div class="message"><p>Your search yielded no results.</p></div>
|
||||||
<% else -%>
|
<% else -%>
|
||||||
<% source_view_is = :search %>
|
<% unless @found_todos.empty? -%>
|
||||||
<% parent_container_type = 'search' %>
|
<div id="found-todos-container" class="container">
|
||||||
<% if not @found_todos.empty? -%>
|
<h2><span id="found-todos-count" class="badge"><%= @found_todos.size %></span>Todos matching query</h2>
|
||||||
<div id="found-todos-container" class="container">
|
<%= render :partial => "todos/todo", :collection => @found_todos, :locals => { :parent_container_type => 'search', :suppress_context => false, :suppress_project => false, :suppress_edit_button => true } %>
|
||||||
<h2><span id="found-todos-count" class="badge"><%= @found_todos.size %></span>Todos matching query</h2>
|
</div>
|
||||||
<%= render :partial => "todos/todo", :collection => @found_todos, :locals => { :parent_container_type => 'search', :suppress_context => false, :suppress_project => false, :suppress_edit_button => true } %>
|
<% end -%>
|
||||||
</div>
|
|
||||||
<% end -%>
|
<% unless @found_projects.empty? -%>
|
||||||
|
<div id="found-projects-container" class="container">
|
||||||
<% if not @found_projects.empty? -%>
|
<h2><span id="found-projects-count" class="badge"><%= @found_projects.size %></span>Projects matching query</h2>
|
||||||
<div id="found-projects-container" class="container">
|
<%= render :partial => "projects/project_listing", :collection => @found_projects, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %>
|
||||||
<h2><span id="found-projects-count" class="badge"><%= @found_projects.size %></span>Projects matching query</h2>
|
</div>
|
||||||
<%= render :partial => "projects/project_listing", :collection => @found_projects, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %>
|
<% end -%>
|
||||||
</div>
|
|
||||||
<% end -%>
|
<% unless @found_notes.empty? -%>
|
||||||
|
<div id="found-notes-container" class="container">
|
||||||
<% if not @found_notes.empty? -%>
|
<h2><span id="found-notes-count" class="badge"><%= @found_notes.size %></span>Notes matching query</h2>
|
||||||
<div id="found-notes-container" class="container">
|
<% for notes in @found_notes -%>
|
||||||
<h2><span id="found-notes-count" class="badge"><%= @found_notes.size %></span>Notes matching query</h2>
|
<div class="container" id="note-<%= notes.id %>-wrapper">
|
||||||
<% for notes in @found_notes -%>
|
<%= render :partial => "notes/notes_summary", :object => notes %>
|
||||||
<div class="container" id="note-<%= notes.id %>-wrapper">
|
</div>
|
||||||
<%= render :partial => "notes/notes_summary", :object => notes %>
|
<% end -%>
|
||||||
</div>
|
</div>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
</div>
|
|
||||||
<% end -%>
|
<% unless @found_contexts.empty? -%>
|
||||||
<% end -%>
|
<div id="found-contexts-container" class="container">
|
||||||
</div>
|
<h2><span id="found-contexts-count" class="badge"><%= @found_contexts.size %></span>Contexts matching query</h2>
|
||||||
|
<%= render :partial => "contexts/context_listing", :collection => @found_contexts, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %>
|
||||||
|
</div>
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<% unless @found_tags.empty? -%>
|
||||||
|
<div id="found-tags-container" class="container">
|
||||||
|
<h2><span id="found-tags-count" class="badge"><%= @found_tags.size %></span>Tags matching query</h2>
|
||||||
|
<span class="tags"><% @found_tags.each do |tag| -%>
|
||||||
|
<span class="tag"><%= link_to tag.name, {:controller => "todos", :action => "tag", :id => tag.name} -%></span>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end -%>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
@todo = nil
|
@todo = nil
|
||||||
@initial_context_name = @context.name unless @context.nil?
|
@initial_context_name = @context.name unless @context.nil?
|
||||||
@initial_context_name ||= @project.default_context.name unless @project.nil? || @project.default_context.nil?
|
@initial_context_name ||= @project.default_context.name unless @project.nil? || @project.default_context.nil?
|
||||||
@initial_context_name ||= @contexts[0].name unless @contexts[0].nil?
|
@initial_context_name ||= current_user.contexts.first.name unless current_user.contexts.first.nil?
|
||||||
@initial_project_name = @project.name unless @project.nil?
|
@initial_project_name = @project.name unless @project.nil?
|
||||||
%>
|
%>
|
||||||
<div id="todo_new_action_container">
|
<div id="todo_new_action_container">
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,30 @@
|
||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
<% # show active items before hidden / completed items -%>
|
<% # show active items before hidden / completed items -%>
|
||||||
|
|
||||||
<%= render :partial => "sidebar/project_list",
|
<%= render :partial => "sidebar/project_list",
|
||||||
:locals => { :list_name => 'Active Projects',
|
:locals => { :list_name => 'Active Projects',
|
||||||
:projects => @projects.select{|p| p.active? } } -%>
|
:projects => @active_projects } -%>
|
||||||
|
|
||||||
<%= render :partial => "sidebar/context_list",
|
<%= render :partial => "sidebar/context_list",
|
||||||
:locals => { :list_name => 'Active Contexts',
|
:locals => { :list_name => 'Active Contexts',
|
||||||
:contexts => @contexts.reject{|c| c.hide? } } -%>
|
:contexts => @active_contexts } -%>
|
||||||
|
|
||||||
<% if prefs.show_hidden_projects_in_sidebar -%>
|
<% if prefs.show_hidden_projects_in_sidebar -%>
|
||||||
<%= render :partial => "sidebar/project_list",
|
<%= render :partial => "sidebar/project_list",
|
||||||
:locals => { :list_name => 'Hidden Projects',
|
:locals => { :list_name => 'Hidden Projects',
|
||||||
:projects => @projects.select{|p| p.hidden? } } -%>
|
:projects => @hidden_projects } -%>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
|
|
||||||
<% if prefs.show_completed_projects_in_sidebar -%>
|
<% if prefs.show_completed_projects_in_sidebar -%>
|
||||||
<%= render :partial => "sidebar/project_list",
|
<%= render :partial => "sidebar/project_list",
|
||||||
:locals => { :list_name => 'Completed Projects',
|
:locals => { :list_name => 'Completed Projects',
|
||||||
:projects => @projects.select{|p| p.completed? } } -%>
|
:projects => @completed_projects } -%>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
|
|
||||||
<% if prefs.show_hidden_contexts_in_sidebar -%>
|
<% if prefs.show_hidden_contexts_in_sidebar -%>
|
||||||
<%= render :partial => "sidebar/context_list",
|
<%= render :partial => "sidebar/context_list",
|
||||||
:locals => { :list_name => 'Hidden Contexts',
|
:locals => { :list_name => 'Hidden Contexts',
|
||||||
:contexts => @contexts.select{|c| c.hide? } } -%>
|
:contexts => @hidden_contexts } -%>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
|
|
||||||
<div class="integrations-link"><ul>
|
<div class="integrations-link"><ul>
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ end
|
||||||
-%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>&
|
-%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>&
|
||||||
&pie_labels=<%
|
&pie_labels=<%
|
||||||
0.upto @actions_per_context.size()-2 do | i |
|
0.upto @actions_per_context.size()-2 do | i |
|
||||||
%><%=truncate(@actions_per_context[i]['name'],@truncate_chars, '...')%>,<%
|
%><%=truncate(@actions_per_context[i]['name'], :length => @truncate_chars, :omission => '...')%>,<%
|
||||||
end
|
end
|
||||||
-%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'],@truncate_chars,'...') %>&
|
-%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'], :legnth => @truncate_chars, :omission => '...') %>&
|
||||||
&links=<%
|
&links=<%
|
||||||
0.upto @actions_per_context.size()-2 do | i |
|
0.upto @actions_per_context.size()-2 do | i |
|
||||||
%><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<%
|
%><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<%
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ end
|
||||||
-%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>&
|
-%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>&
|
||||||
&pie_labels=<%
|
&pie_labels=<%
|
||||||
0.upto @actions_per_context.size()-2 do | i |
|
0.upto @actions_per_context.size()-2 do | i |
|
||||||
%><%=truncate(@actions_per_context[i]['name'], @truncate_chars, '...') %>,<%
|
%><%=truncate(@actions_per_context[i]['name'], :length => @truncate_chars, :omission => '...') %>,<%
|
||||||
end
|
end
|
||||||
-%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'], @truncate_chars, '...') %>&
|
-%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'], :length => @truncate_chars, :omission => '...') %>&
|
||||||
&links=<%
|
&links=<%
|
||||||
0.upto @actions_per_context.size()-2 do | i |
|
0.upto @actions_per_context.size()-2 do | i |
|
||||||
%><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<%
|
%><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<%
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ if parent_container_type == 'show_mobile' -%>
|
||||||
<h2><label for="todo_description">Description</label></h2>
|
<h2><label for="todo_description">Description</label></h2>
|
||||||
<%= text_field( "todo", "description", "tabindex" => 2) %>
|
<%= text_field( "todo", "description", "tabindex" => 2) %>
|
||||||
<h2><label for="todo_notes">Notes</label></h2>
|
<h2><label for="todo_notes">Notes</label></h2>
|
||||||
<%= text_area( "todo", "notes", "cols" => 30, "rows" => 5, "tabindex" => 3) %>
|
<%= text_area( "todo", "notes", "cols" => 30, "rows" => 2, "tabindex" => 3) %>
|
||||||
<h2><label for="todo_context_id">Context</label></h2>
|
<h2><label for="todo_context_id">Context</label></h2>
|
||||||
<%= unless @mobile_from_context
|
<%= unless @mobile_from_context
|
||||||
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 4} )
|
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 4} )
|
||||||
|
|
@ -22,7 +22,7 @@ end %>
|
||||||
<h2><label for="todo_project_id">Project</label></h2>
|
<h2><label for="todo_project_id">Project</label></h2>
|
||||||
<%= unless @mobile_from_project
|
<%= unless @mobile_from_project
|
||||||
collection_select( "todo", "project_id", @projects, "id", "name",
|
collection_select( "todo", "project_id", @projects, "id", "name",
|
||||||
{:include_blank => true}, {"tabindex" => 5} )
|
{:include_blank => '--No project--'}, {"tabindex" => 5} )
|
||||||
else
|
else
|
||||||
# manually add blank option since :include_blank does not work
|
# manually add blank option since :include_blank does not work
|
||||||
# with options_from_collection_for_select
|
# with options_from_collection_for_select
|
||||||
|
|
@ -34,7 +34,7 @@ end %>
|
||||||
<%= text_field_tag "tag_list", @tag_list_text, :size => 30, :tabindex => 6 %>
|
<%= text_field_tag "tag_list", @tag_list_text, :size => 30, :tabindex => 6 %>
|
||||||
<h2><label for="todo_due">Due</label></h2>
|
<h2><label for="todo_due">Due</label></h2>
|
||||||
<%= date_select("todo", "due", {:order => [:day, :month, :year],
|
<%= date_select("todo", "due", {:order => [:day, :month, :year],
|
||||||
:start_year => this_year, :include_blank => true}, :tabindex => 7) %>
|
:start_year => this_year, :include_blank => '--'}, :tabindex => 7) %>
|
||||||
<h2><label for="todo_show_from">Show from</label></h2>
|
<h2><label for="todo_show_from">Show from</label></h2>
|
||||||
<%= date_select("todo", "show_from", {:order => [:day, :month, :year],
|
<%= date_select("todo", "show_from", {:order => [:day, :month, :year],
|
||||||
:start_year => this_year, :include_blank => true}, :tabindex => 8) %>
|
:start_year => this_year, :include_blank => true}, :tabindex => 8) %>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
<% @todo = mobile_todo
|
<% @todo = mobile_todo
|
||||||
if mobile_todo.starred?
|
if mobile_todo.starred?
|
||||||
bullet = "<span class=star>"+image_tag("menustar_small.gif")+"</span>"
|
li_class = " class=\"star\""
|
||||||
else
|
else
|
||||||
bullet = "<span class=r>» </span>"
|
li_class = ""
|
||||||
end -%>
|
end -%>
|
||||||
<div class="t" id="<%= dom_id(mobile_todo) %>">
|
<li id="<%= dom_id(mobile_todo) %>" <%= li_class %>><%
|
||||||
<tr valign="top"><td><%= bullet %></td><td><%
|
|
||||||
if mobile_todo.completed?
|
if mobile_todo.completed?
|
||||||
-%><span class="m_t_d">
|
-%><span class="m_t_d">
|
||||||
<% else
|
<% else
|
||||||
|
|
@ -23,4 +22,4 @@ end -%>
|
||||||
")</span>" -%>
|
")</span>" -%>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<%= tag_list_mobile -%>
|
<%= tag_list_mobile -%>
|
||||||
</span></td></tr></div>
|
</span></li>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@
|
||||||
<% unless @todo.completed? %><span class="defer-container"><%= defer_link(1) %> <%= defer_link(7) %></span><% end %>
|
<% unless @todo.completed? %><span class="defer-container"><%= defer_link(1) %> <%= defer_link(7) %></span><% end %>
|
||||||
<%= date_span -%>
|
<%= date_span -%>
|
||||||
<span class="todo.descr"><%= h sanitize(todo.description) %></span>
|
<span class="todo.descr"><%= h sanitize(todo.description) %></span>
|
||||||
<%= link_to(image_tag("recurring16x16.png"), {:controller => "recurring_todos", :action => "index"}, :class => "recurring_icon") if @todo.from_recurring_todo? %>
|
<%= link_to(
|
||||||
|
image_tag("recurring16x16.png"),
|
||||||
|
{:controller => "recurring_todos", :action => "index"},
|
||||||
|
:class => "recurring_icon", :title => recurrence_pattern_as_text(@todo.recurring_todo)) if @todo.from_recurring_todo? %>
|
||||||
<%= tag_list %>
|
<%= tag_list %>
|
||||||
<%= deferred_due_date %>
|
<%= deferred_due_date %>
|
||||||
<%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %>
|
<%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ CLASS:PUBLIC
|
||||||
CATEGORIES:Tracks
|
CATEGORIES:Tracks
|
||||||
CREATED:<%= todo.created_at.strftime("%Y%m%dT%H%M%SZ") %>
|
CREATED:<%= todo.created_at.strftime("%Y%m%dT%H%M%SZ") %>
|
||||||
DESCRIPTION:<%= format_ical_notes(todo.notes) %>
|
DESCRIPTION:<%= format_ical_notes(todo.notes) %>
|
||||||
LAST-MODIFIED:<%= due_date.strftime("%Y%m%dT%H%M%SZ") %>
|
LAST-MODIFIED:<%= todo.updated_at.strftime("%Y%m%dT%H%M%SZ") %>
|
||||||
LOCATION:
|
LOCATION:
|
||||||
SEQUENCE:0
|
SEQUENCE:0
|
||||||
STATUS:CONFIRMED
|
STATUS:CONFIRMED
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ if @saved
|
||||||
page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true }
|
page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true }
|
||||||
else
|
else
|
||||||
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred)
|
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred)
|
||||||
page.insert_html :bottom, item_container_id(@todo) + 'items', :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type, :source_view => @source_view }
|
page.insert_html :bottom, item_container_id(@todo) + 'items', :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type, :source_view => @source_view }
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,7 @@
|
||||||
|
|
||||||
<div id="input_box">
|
<div id="input_box">
|
||||||
<%= render :partial => "shared/add_new_item_form" %>
|
<%= render :partial => "shared/add_new_item_form" %>
|
||||||
<%= render "sidebar/sidebar" %>
|
<%- # TODO: this used to be render :template, but somehow it was not
|
||||||
|
#rendered after the rails2.2.2 upgrade -%>
|
||||||
|
<%= render :file => "sidebar/sidebar.html.erb" %>
|
||||||
</div><!-- End of input box -->
|
</div><!-- End of input box -->
|
||||||
|
|
@ -11,5 +11,5 @@
|
||||||
|
|
||||||
<div id="input_box">
|
<div id="input_box">
|
||||||
<%= render :partial => "shared/add_new_item_form" %>
|
<%= render :partial => "shared/add_new_item_form" %>
|
||||||
<%= render "sidebar/sidebar" %>
|
<%= render :template => "sidebar/sidebar" %>
|
||||||
</div><!-- End of input box -->
|
</div><!-- End of input box -->
|
||||||
|
|
@ -23,5 +23,5 @@
|
||||||
|
|
||||||
<div id="input_box">
|
<div id="input_box">
|
||||||
<%= render :partial => "shared/add_new_item_form" %>
|
<%= render :partial => "shared/add_new_item_form" %>
|
||||||
<%= render "sidebar/sidebar" %>
|
<%= render :template => "sidebar/sidebar" %>
|
||||||
</div><!-- End of input box -->
|
</div><!-- End of input box -->
|
||||||
|
|
@ -5,7 +5,7 @@ if @saved
|
||||||
|
|
||||||
# completed todos move from their context to the completed container
|
# completed todos move from their context to the completed container
|
||||||
unless @prefs.hide_completed_actions?
|
unless @prefs.hide_completed_actions?
|
||||||
page.insert_html :top, "completed_containeritems", :partial => 'todos/todo', :locals => { :parent_container_type => "completed" }
|
page.insert_html :top, "completed_containeritems", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => "completed" }
|
||||||
page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"}
|
page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"}
|
||||||
page[empty_container_msg_div_id].show if @down_count == 0 && !empty_container_msg_div_id.nil?
|
page[empty_container_msg_div_id].show if @down_count == 0 && !empty_container_msg_div_id.nil?
|
||||||
page.show 'tickler-empty-nd' if source_view_is(:project) && @deferred_count == 0
|
page.show 'tickler-empty-nd' if source_view_is(:project) && @deferred_count == 0
|
||||||
|
|
@ -33,7 +33,7 @@ if @saved
|
||||||
else
|
else
|
||||||
# todo is activated from completed container
|
# todo is activated from completed container
|
||||||
page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@todo)
|
page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@todo)
|
||||||
page.insert_html :bottom, item_container_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.insert_html :bottom, item_container_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"}
|
page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"}
|
||||||
page.show "empty-d" if @completed_count == 0
|
page.show "empty-d" if @completed_count == 0
|
||||||
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? # If we've checked something as undone, incomplete items can't be empty
|
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? # If we've checked something as undone, incomplete items can't be empty
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ if @saved
|
||||||
page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true
|
page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true
|
||||||
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
||||||
# show all todos in context
|
# show all todos in context
|
||||||
page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
end
|
end
|
||||||
|
|
||||||
# update badge count
|
# update badge count
|
||||||
|
|
@ -52,7 +52,7 @@ if @saved
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
end
|
end
|
||||||
elsif source_view_is :project
|
elsif source_view_is :project
|
||||||
|
|
@ -63,17 +63,17 @@ if @saved
|
||||||
elsif @todo.deferred?
|
elsif @todo.deferred?
|
||||||
page[@todo].remove
|
page[@todo].remove
|
||||||
page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0)
|
page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0)
|
||||||
page.insert_html :bottom, "tickler", :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.insert_html :bottom, "tickler", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page['tickler-empty-nd'].hide
|
page['tickler-empty-nd'].hide
|
||||||
page.replace_html "badge_count", @down_count
|
page.replace_html "badge_count", @down_count
|
||||||
elsif @todo_was_activated_from_deferred_state
|
elsif @todo_was_activated_from_deferred_state
|
||||||
page[@todo].remove
|
page[@todo].remove
|
||||||
page['tickler-empty-nd'].show if (@deferred_count == 0)
|
page['tickler-empty-nd'].show if (@deferred_count == 0)
|
||||||
page.insert_html :bottom, "p#{@todo.project_id}", :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.insert_html :bottom, "p#{@todo.project_id}", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page["p#{@todo.project_id}empty-nd"].hide
|
page["p#{@todo.project_id}empty-nd"].hide
|
||||||
page.replace_html "badge_count", @down_count
|
page.replace_html "badge_count", @down_count
|
||||||
else
|
else
|
||||||
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
end
|
end
|
||||||
elsif source_view_is :deferred
|
elsif source_view_is :deferred
|
||||||
|
|
@ -87,7 +87,7 @@ if @saved
|
||||||
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}"
|
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}"
|
||||||
page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true
|
page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true
|
||||||
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
||||||
page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page.replace_html("badge_count", @down_count)
|
page.replace_html("badge_count", @down_count)
|
||||||
page.delay(0.5) do
|
page.delay(0.5) do
|
||||||
page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items"
|
page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items"
|
||||||
|
|
@ -95,18 +95,18 @@ if @saved
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
end
|
end
|
||||||
elsif source_view_is :stats
|
elsif source_view_is :stats
|
||||||
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
elsif source_view_is :calendar
|
elsif source_view_is :calendar
|
||||||
if @due_date_changed
|
if @due_date_changed
|
||||||
page[@todo].remove
|
page[@todo].remove
|
||||||
page.show "empty_"+@original_item_due_id if @old_due_empty
|
page.show "empty_"+@original_item_due_id if @old_due_empty
|
||||||
page.hide "empty_"+@new_due_id
|
page.hide "empty_"+@new_due_id
|
||||||
page.insert_html :bottom, @new_due_id, :partial => 'todos/todo'
|
page.insert_html :bottom, @new_due_id, :partial => 'todos/todo', :locals => {:todo => @todo}
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
else
|
else
|
||||||
if @todo.due.nil?
|
if @todo.due.nil?
|
||||||
|
|
@ -114,7 +114,7 @@ if @saved
|
||||||
page[@todo].remove
|
page[@todo].remove
|
||||||
page.show "empty_"+@original_item_due_id if @old_due_empty
|
page.show "empty_"+@original_item_due_id if @old_due_empty
|
||||||
else
|
else
|
||||||
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
|
||||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
---
|
|
||||||
javascripts:
|
|
||||||
- tracks:
|
|
||||||
- prototype
|
|
||||||
- effects
|
|
||||||
- dragdrop
|
|
||||||
- controls
|
|
||||||
- application
|
|
||||||
- calendar
|
|
||||||
- calendar-en
|
|
||||||
- calendar-setup
|
|
||||||
- accesskey-hints
|
|
||||||
- todo-items
|
|
||||||
- niftycube
|
|
||||||
- protoload
|
|
||||||
- flashobject
|
|
||||||
- lowpro
|
|
||||||
stylesheets:
|
|
||||||
- tracks:
|
|
||||||
- standard
|
|
||||||
- calendar-system
|
|
||||||
- niftyCorners
|
|
||||||
|
|
@ -67,7 +67,7 @@ module Rails
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def rubygems_version
|
def rubygems_version
|
||||||
Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
|
Gem::RubyGemsVersion rescue nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def gem_version
|
def gem_version
|
||||||
|
|
@ -82,14 +82,14 @@ module Rails
|
||||||
|
|
||||||
def load_rubygems
|
def load_rubygems
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
|
min_version = '1.3.1'
|
||||||
unless rubygems_version >= '0.9.4'
|
unless rubygems_version >= min_version
|
||||||
$stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
$stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ class Rails::Configuration
|
||||||
attr_accessor :action_web_service
|
attr_accessor :action_web_service
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
|
||||||
|
# If you choose ldap, see the additional configuration options further down.
|
||||||
|
AUTHENTICATION_SCHEMES = ['database']
|
||||||
|
|
||||||
Rails::Initializer.run do |config|
|
Rails::Initializer.run do |config|
|
||||||
# Skip frameworks you're not going to use
|
# Skip frameworks you're not going to use
|
||||||
# config.frameworks -= [ :action_web_service, :action_mailer ]
|
# config.frameworks -= [ :action_web_service, :action_mailer ]
|
||||||
|
|
@ -22,6 +26,8 @@ Rails::Initializer.run do |config|
|
||||||
config.action_web_service = Rails::OrderedOptions.new
|
config.action_web_service = Rails::OrderedOptions.new
|
||||||
config.load_paths += %W( #{RAILS_ROOT}/app/apis )
|
config.load_paths += %W( #{RAILS_ROOT}/app/apis )
|
||||||
|
|
||||||
|
config.action_controller.use_accept_header = true
|
||||||
|
|
||||||
# Add additional load paths for your own custom dirs
|
# Add additional load paths for your own custom dirs
|
||||||
# config.load_paths += %W( #{RAILS_ROOT}/app/services )
|
# config.load_paths += %W( #{RAILS_ROOT}/app/services )
|
||||||
|
|
||||||
|
|
@ -33,10 +39,10 @@ Rails::Initializer.run do |config|
|
||||||
# (create the session table with 'rake create_sessions_table')
|
# (create the session table with 'rake create_sessions_table')
|
||||||
config.action_controller.session_store = :active_record_store
|
config.action_controller.session_store = :active_record_store
|
||||||
|
|
||||||
# config.action_controller.session = {
|
config.action_controller.session = {
|
||||||
# :session_key => '_tracks_session_id',
|
:session_key => '_tracks_session_id',
|
||||||
# :secret => SALT * (30.0 / SALT.length).ceil #must be at least 30 characters
|
:secret => SALT * (30.0 / SALT.length).ceil #must be at least 30 characters
|
||||||
# }
|
}
|
||||||
|
|
||||||
# Enable page/fragment caching by setting a file-based store
|
# Enable page/fragment caching by setting a file-based store
|
||||||
# (remember to create the caching directory and make it readable to the application)
|
# (remember to create the caching directory and make it readable to the application)
|
||||||
|
|
@ -70,9 +76,6 @@ end
|
||||||
|
|
||||||
# Include your application configuration below
|
# Include your application configuration below
|
||||||
|
|
||||||
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
|
|
||||||
# If you choose ldap, see the additional configuration options further down.
|
|
||||||
AUTHENTICATION_SCHEMES = ['database']
|
|
||||||
|
|
||||||
require 'name_part_finder'
|
require 'name_part_finder'
|
||||||
require 'tracks/todo_list'
|
require 'tracks/todo_list'
|
||||||
|
|
@ -96,14 +99,11 @@ end
|
||||||
# setting this to true will make the cookies only available over HTTPS
|
# setting this to true will make the cookies only available over HTTPS
|
||||||
TRACKS_COOKIES_SECURE = false
|
TRACKS_COOKIES_SECURE = false
|
||||||
|
|
||||||
MOBILE_CONTENT_TYPE = 'tracks/mobile'
|
tracks_version='1.7RC'
|
||||||
Mime::Type.register(MOBILE_CONTENT_TYPE, :m)
|
|
||||||
|
|
||||||
tracks_version='1.7-devel'
|
|
||||||
|
|
||||||
# comment out next two lines if you do not want (or can not) the date of the
|
# comment out next two lines if you do not want (or can not) the date of the
|
||||||
# last git commit in the footer
|
# last git commit in the footer
|
||||||
info=`git log --pretty=format:"%ai" -1`
|
# info=`git log --pretty=format:"%ai" -1`
|
||||||
tracks_version=tracks_version + ' ('+info+')'
|
# tracks_version=tracks_version + ' ('+info+')'
|
||||||
|
|
||||||
TRACKS_VERSION=tracks_version
|
TRACKS_VERSION=tracks_version
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
# Add new mime types for use in respond_to blocks:
|
# Add new mime types for use in respond_to blocks:
|
||||||
# Mime::Type.register "text/richtext", :rtf
|
# Mime::Type.register "text/richtext", :rtf
|
||||||
# Mime::Type.register "application/x-mobile", :mobile
|
# Mime::Type.register "application/x-mobile", :mobile
|
||||||
|
Mime::Type.register_alias "text/html", :m
|
||||||
|
|
@ -6,10 +6,6 @@ ActionController::Routing::Routes.draw do |map|
|
||||||
login.formatted_login 'login.:format', :action => 'login'
|
login.formatted_login 'login.:format', :action => 'login'
|
||||||
login.logout 'logout', :action => 'logout'
|
login.logout 'logout', :action => 'logout'
|
||||||
login.formatted_logout 'logout.:format', :action => 'logout'
|
login.formatted_logout 'logout.:format', :action => 'logout'
|
||||||
login.open_id_begin 'begin', :action => 'begin'
|
|
||||||
login.formatted_open_id_begin 'begin.:format', :action => 'begin'
|
|
||||||
login.open_id_complete 'complete', :action => 'complete'
|
|
||||||
login.formatted_open_id_complete 'complete.:format', :action => 'complete'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
map.resources :users,
|
map.resources :users,
|
||||||
|
|
@ -57,6 +53,7 @@ ActionController::Routing::Routes.draw do |map|
|
||||||
todos.mobile_abbrev 'm', :action => "index", :format => 'm'
|
todos.mobile_abbrev 'm', :action => "index", :format => 'm'
|
||||||
todos.mobile_abbrev_new 'm/new', :action => "new", :format => 'm'
|
todos.mobile_abbrev_new 'm/new', :action => "new", :format => 'm'
|
||||||
end
|
end
|
||||||
|
map.root :controller => 'todos' # Make OpenID happy because it needs #root_url defined
|
||||||
|
|
||||||
map.resources :notes
|
map.resources :notes
|
||||||
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
||||||
|
|
@ -68,6 +65,7 @@ ActionController::Routing::Routes.draw do |map|
|
||||||
|
|
||||||
map.preferences 'preferences', :controller => 'preferences', :action => 'index'
|
map.preferences 'preferences', :controller => 'preferences', :action => 'index'
|
||||||
map.integrations 'integrations', :controller => 'integrations', :action => 'index'
|
map.integrations 'integrations', :controller => 'integrations', :action => 'index'
|
||||||
|
map.search_plugin '/integrations/search_plugin.xml', :controller => 'integrations', :action => 'search_plugin', :format => 'xml'
|
||||||
|
|
||||||
map.resources :recurring_todos,
|
map.resources :recurring_todos,
|
||||||
:member => {:toggle_check => :put, :toggle_star => :put}
|
:member => {:toggle_check => :put, :toggle_star => :put}
|
||||||
|
|
|
||||||
43
db/migrate/044_upgrade_open_id.rb
Normal file
43
db/migrate/044_upgrade_open_id.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
class UpgradeOpenId < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :open_id_authentication_associations, :force => true do |t|
|
||||||
|
t.integer :issued, :lifetime
|
||||||
|
t.string :handle, :assoc_type
|
||||||
|
t.binary :server_url, :secret
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||||
|
t.integer :timestamp, :null => false
|
||||||
|
t.string :server_url, :null => true
|
||||||
|
t.string :salt, :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
drop_table :open_id_associations
|
||||||
|
drop_table :open_id_nonces
|
||||||
|
drop_table :open_id_settings
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :open_id_authentication_associations
|
||||||
|
drop_table :open_id_authentication_nonces
|
||||||
|
|
||||||
|
create_table "open_id_associations", :force => true do |t|
|
||||||
|
t.binary "server_url"
|
||||||
|
t.string "handle"
|
||||||
|
t.binary "secret"
|
||||||
|
t.integer "issued"
|
||||||
|
t.integer "lifetime"
|
||||||
|
t.string "assoc_type"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "open_id_nonces", :force => true do |t|
|
||||||
|
t.string "nonce"
|
||||||
|
t.integer "created"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "open_id_settings", :force => true do |t|
|
||||||
|
t.string "setting"
|
||||||
|
t.binary "value"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -5,15 +5,15 @@
|
||||||
-- Dump of table contexts
|
-- Dump of table contexts
|
||||||
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
||||||
|
|
||||||
INSERT INTO "contexts" VALUES(1,'agenda',1,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(1,'agenda',1,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(2,'call',2,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(2,'call',2,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(3,'email',3,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(3,'email',3,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(4,'errand',4,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(4,'errand',4,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(5,'lab',5,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(5,'lab',5,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(6,'library',6,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(6,'library',6,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(7,'freetime',7,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(7,'freetime',7,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(8,'office',8,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(8,'office',8,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
INSERT INTO "contexts" VALUES(9,'waiting for',9,'f',1,'2008-02-25 20:21:09','2008-02-25 20:21:09');
|
INSERT INTO "contexts" VALUES(9,'waiting for',9,'f',1,'2008-02-25 20:21:09','2008-03-24 19:23:53');
|
||||||
|
|
||||||
-- Dump of table notes
|
-- Dump of table notes
|
||||||
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
||||||
|
|
@ -25,33 +25,33 @@ INSERT INTO "notes" VALUES(2,1,1,'Should I go for a swirly effect or a whooshy o
|
||||||
-- Dump of table projects
|
-- Dump of table projects
|
||||||
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
||||||
|
|
||||||
INSERT INTO "projects" VALUES(1,'Build a working time machine',1,1,'','active','2008-02-25 20:21:09','2008-02-25 20:21:09',NULL,NULL);
|
INSERT INTO "projects" VALUES(1,'Build a working time machine',1,1,'','active','2008-02-25 20:21:09','2008-03-24 19:23:53',NULL,NULL);
|
||||||
INSERT INTO "projects" VALUES(2,'Make more money than Billy Gates',2,1,'','active','2008-02-25 20:21:09','2008-02-25 20:21:09',NULL,NULL);
|
INSERT INTO "projects" VALUES(2,'Make more money than Billy Gates',2,1,'','active','2008-02-25 20:21:09','2008-03-24 19:23:53',NULL,NULL);
|
||||||
INSERT INTO "projects" VALUES(3,'Evict dinosaurs from the garden',3,1,'','active','2008-02-25 20:21:09','2008-02-25 20:21:09',NULL,NULL);
|
INSERT INTO "projects" VALUES(3,'Evict dinosaurs from the garden',3,1,'','active','2008-02-25 20:21:09','2008-03-24 19:23:53',NULL,NULL);
|
||||||
|
|
||||||
|
|
||||||
-- Dump of table schema_info
|
-- Dump of table schema_info
|
||||||
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
||||||
|
|
||||||
INSERT INTO "schema_info" VALUES(37);
|
INSERT INTO "schema_migrations" VALUES('44');
|
||||||
|
|
||||||
|
|
||||||
-- Dump of table todos
|
-- Dump of table todos
|
||||||
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
||||||
|
|
||||||
INSERT INTO "todos" VALUES(1,1,2,'Call Bill Gates to find out how much he makes per day',NULL,'2006-06-03 14:36:02','2006-06-24',NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(1,1,2,'Call Bill Gates to find out how much he makes per day',NULL,'2006-06-03 14:36:02','2006-06-23 23:00:00',NULL,1,NULL,'active',NULL,'2006-06-03 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(2,2,3,'Call dinosaur exterminator','Ask him if I need to hire a skip for the corpses.','2006-06-10 14:36:02','2006-06-24',NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(2,2,3,'Call dinosaur exterminator','Ask him if I need to hire a skip for the corpses.','2006-06-10 14:36:02','2006-06-23 23:00:00',NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(3,4,NULL,'Buy milk',NULL,'2006-06-10 14:36:02',NULL,NULL,1,NULL,'completed');
|
INSERT INTO "todos" VALUES(3,4,NULL,'Buy milk',NULL,'2006-06-10 14:36:02',NULL,NULL,1,NULL,'completed',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(4,4,NULL,'Buy bread',NULL,'2006-06-10 14:36:02',NULL,NULL,1,NULL,'completed');
|
INSERT INTO "todos" VALUES(4,4,NULL,'Buy bread',NULL,'2006-06-10 14:36:02',NULL,NULL,1,NULL,'completed',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(5,5,1,'Construct time dilation device',NULL,'2006-06-10 14:36:02',NULL,NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(5,5,1,'Construct time dilation device',NULL,'2006-06-10 14:36:02',NULL,NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(6,2,1,'Phone Grandfather to ask about the paradox','Added some _notes_.','2006-06-10 14:36:02','2006-06-03',NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(6,2,1,'Phone Grandfather to ask about the paradox','Added some _notes_.','2006-06-10 14:36:02','2006-06-02 23:00:00',NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(7,6,3,'Get a book out of the library','Dinosaurs''R','2006-06-10 14:36:02',NULL,NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(7,6,3,'Get a book out of the library','Dinosaurs''R','2006-06-10 14:36:02',NULL,NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(8,4,NULL,'Upgrade to Rails 0.9.1',NULL,'2006-06-10 14:36:02','2006-06-10',NULL,1,NULL,'completed');
|
INSERT INTO "todos" VALUES(8,4,NULL,'Upgrade to Rails 0.9.1',NULL,'2006-06-10 14:36:02','2006-06-09 23:00:00',NULL,1,NULL,'completed',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(9,1,NULL,'This should be due today',NULL,'2006-06-10 14:36:02','2006-06-10',NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(9,1,NULL,'This should be due today',NULL,'2006-06-10 14:36:02','2006-06-09 23:00:00',NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(10,1,NULL,'foo',NULL,'2006-06-10 14:36:02','2005-01-05',NULL,1,NULL,'completed');
|
INSERT INTO "todos" VALUES(10,1,NULL,'foo',NULL,'2006-06-10 14:36:02','2005-01-05 00:00:00',NULL,1,NULL,'completed',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(11,1,2,'Buy shares',NULL,'2006-06-10 14:36:02','2005-02-01',NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(11,1,2,'Buy shares',NULL,'2006-06-10 14:36:02','2005-02-01 00:00:00',NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(12,1,3,'Buy stegosaurus bait',NULL,'2006-06-10 14:36:02','2006-06-17',NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(12,1,3,'Buy stegosaurus bait',NULL,'2006-06-10 14:36:02','2006-06-16 23:00:00',NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(13,1,3,'New action in context','Some notes','2006-06-10 14:36:02','2006-06-17',NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(13,1,3,'New action in context','Some notes','2006-06-10 14:36:02','2006-06-16 23:00:00',NULL,1,NULL,'active',NULL,'2006-06-10 14:36:02');
|
||||||
INSERT INTO "todos" VALUES(14,2,2,'Call stock broker','tel: 12345','2006-06-03 14:36:02',NULL,NULL,1,NULL,'active');
|
INSERT INTO "todos" VALUES(14,2,2,'Call stock broker','tel: 12345','2006-06-03 14:36:02',NULL,NULL,1,NULL,'active',NULL,'2006-06-03 14:36:02');
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
= Tracks: a GTD web application, built with Ruby on Rails
|
= Tracks: a GTD web application, built with Ruby on Rails
|
||||||
|
|
||||||
* Homepage: http://www.rousette.org.uk/projects/
|
* Project homepage: http://www.rousette.org.uk/projects/
|
||||||
* Author: bsag (http://www.rousette.org.uk/)
|
* GitHub: http://github.com/bsag/tracks/
|
||||||
* Contributors: Nicholas Lee, Lolindrath, Jim Ray, Arnaud Limbourg, Timothy Martens, Luke Melia, John Leonard, Jim Strupp, Eric Lesh, Damien Cirotteau, Janet Riley, Reinier Balt, Jacqui Maher, James Kebinger, Jeffrey Gipson, Eric Allen
|
* Trac (for bug reports and feature requests): http://dev.rousette.org.uk/report/6
|
||||||
* Version: 1.6
|
* Wiki (community contributed information): http://www.rousette.org.uk/projects/wiki/
|
||||||
* Copyright: (cc) 2004-2008 rousette.org.uk
|
* Forum: http://www.rousette.org.uk/projects/forums/
|
||||||
* License: GNU GPL
|
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
|
||||||
|
* Original developer: bsag (http://www.rousette.org.uk/)
|
||||||
|
* Contributors: http://dev.rousette.org.uk/wiki/Tracks/Contributing/Contributors
|
||||||
|
* Version: 1.7
|
||||||
|
* Copyright: (cc) 2004-2008 rousette.org.uk.
|
||||||
|
* License: GNU GPL
|
||||||
|
|
||||||
Main project site: http://www.rousette.org.uk/projects/
|
== Version 1.7RC
|
||||||
|
|
||||||
Trac (for bug reports and feature requests): http://dev.rousette.org.uk/report/6
|
|
||||||
|
|
||||||
Wiki (deprecated - please use Trac): http://www.rousette.org.uk/projects/wiki/
|
|
||||||
|
|
||||||
== Version 1.7dev
|
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
1. Recurring todos
|
1. Recurring todos
|
||||||
|
|
@ -23,10 +22,12 @@ New features:
|
||||||
5. New buttons to quickly defer an action 1 or 7 days
|
5. New buttons to quickly defer an action 1 or 7 days
|
||||||
6. Calendar view to review due actions, includes iCal feed to use in your calendar app (tested with Google Calendar, Evolution, Outlook 2007)
|
6. Calendar view to review due actions, includes iCal feed to use in your calendar app (tested with Google Calendar, Evolution, Outlook 2007)
|
||||||
7. You can now sort projects on number of active todos
|
7. You can now sort projects on number of active todos
|
||||||
|
8. Support for OpenSearch. This means you can add a Tracks as a search provider in your webbrowser (tested on FF3 and IE7)
|
||||||
|
|
||||||
Under the hood:
|
Under the hood:
|
||||||
1. Move selenium tests to RSpec
|
1. Started to move selenium tests to RSpec
|
||||||
2. Bugfixes
|
2. Upgrade to Rails 2.2.2
|
||||||
|
2. Bugfixes, including fixing OpenID
|
||||||
|
|
||||||
== Version 1.6
|
== Version 1.6
|
||||||
1. upgrade to rails 2.0.2
|
1. upgrade to rails 2.0.2
|
||||||
|
|
|
||||||
|
|
@ -11,27 +11,25 @@
|
||||||
<meta name="Copyright" content="2008 rousette.org.uk
|
<meta name="Copyright" content="2008 rousette.org.uk
|
||||||
This work is licensed under a Creative Commons License.
|
This work is licensed under a Creative Commons License.
|
||||||
http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
<meta name="Date" content="2008-04-07" />
|
<meta name="Date" content="2008-12-14" />
|
||||||
<meta name="Format" content="complete" />
|
<meta name="Format" content="complete" />
|
||||||
<meta name="LaTeXXSLT" content="memoir-twosided-manual.xslt" />
|
<meta name="LaTeXXSLT" content="memoir-twosided-manual.xslt" />
|
||||||
<meta name="Revision" content="$Id: manual.markdown 864 2008-06-03 17:01:00Z bsag $" />
|
<meta name="Revision" content="$Id: manual.markdown 2008-12-14 11:50:00Z bsag $" />
|
||||||
<title>Tracks 1.6 Manual</title>
|
<title>Tracks 1.7 Manual</title>
|
||||||
<meta name="Version" content="1.6" />
|
<meta name="Version" content="1.7" />
|
||||||
<meta name="XMP" content="CCAttributionShareAlike" />
|
<meta name="XMP" content="CCAttributionShareAlike" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<!-- The HTML file manual.html is generated from manual.markdown, so make edits to the *.markdown file -->
|
<!-- The HTML file manual.html is generated from manual.markdown, so make edits to the *.markdown file -->
|
||||||
|
|
||||||
<h2 id="installingtracks1.6">Installing Tracks 1.6</h2>
|
<h2 id="installingtracks1.7">Installing Tracks 1.7</h2>
|
||||||
|
|
||||||
<h3 id="introduction">Introduction</h3>
|
<h3 id="introduction">Introduction</h3>
|
||||||
|
|
||||||
<p><strong>An important note for version 1.6: OpenID support is broken in this release. The fix isn’t trivial because of changes to the <code>ruby-openid</code> gem, so we wanted to get this version out now and fix OpenID for the next release. If you depend on OpenID integration, we recommend waiting until the next release.</strong></p>
|
<p>Tracks 1.7 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it’s only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in <code>doc/CHANGELOG</code>. Full API documentation can be found at <code>doc/app/index.html</code>, once you have run <code>rake appdoc</code></p>
|
||||||
|
|
||||||
<p>Tracks 1.6 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it’s only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in <code>doc/CHANGELOG</code>. Full API documentation can be found at <code>doc/app/index.html</code>, once you have run <code>rake appdoc</code></p>
|
<p>There are two methods of downloading Tracks 1.7:</p>
|
||||||
|
|
||||||
<p>There are two methods of downloading Tracks 1.6:</p>
|
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
|
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
|
||||||
|
|
@ -62,7 +60,7 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Tracks itself</li>
|
<li>Tracks itself</li>
|
||||||
<li>Rails 2.0.2 (installed in the <code>/vendor/rails</code> directory, so you do not need to install Rails yourself)</li>
|
<li>Rails 2.2.2 (installed in the <code>/vendor/rails</code> directory, so you do not need to install Rails yourself)</li>
|
||||||
<li>An empty SQLite3 database, set up with the correct database schema</li>
|
<li>An empty SQLite3 database, set up with the correct database schema</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
|
@ -76,19 +74,19 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
<li><strong>Database</strong>. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the <code>sqlite3-ruby</code> gem, as described in step 2, and the SQLite3 libraries and binary (see <a href="http://sqlite.org/download.html">sqlite.org</a> for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from <a href="http://dev.mysql.com/downloads/mysql/5.0.html">MySQL.com</a>. The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.</li>
|
<li><strong>Database</strong>. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the <code>sqlite3-ruby</code> gem, as described in step 2, and the SQLite3 libraries and binary (see <a href="http://sqlite.org/download.html">sqlite.org</a> for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from <a href="http://dev.mysql.com/downloads/mysql/5.0.html">MySQL.com</a>. The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<p>If you are using Unix, you might find <a href="http://www.cooldown.com.ar/2006/12/16/install-tracks-on-ubuntu-or-debian/">this guide</a> by c00i90wn helpful. It was written for Tracks 1.043, but it should work for Tracks 1.6.</p>
|
<p>You can find several installation howtos for specific setups <a href="http://dev.rousette.org.uk/wiki/Tracks/Install">here</a>. They were contributed by various Tracks users.</p>
|
||||||
|
|
||||||
<h3 id="installation">Installation</h3>
|
<h3 id="installation">Installation</h3>
|
||||||
|
|
||||||
<p>This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see <a href="#upgrading" title="Upgrading to Tracks 1.6">Upgrading to Tracks 1.6</a>.</p>
|
<p>This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see <a href="#upgrading" title="Upgrading to Tracks 1.7">Upgrading to Tracks 1.7</a>.</p>
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li><a href="#unzip_install" title="Unzip Tracks and install">Unzip tracks</a> and install in a directory</li>
|
<li><a href="#unzip_install" title="Unzip Tracks and install">Unzip tracks</a> and install in a directory</li>
|
||||||
<li>Decide on a <a href="#database_install" title="Decide on a database">database</a> to use
|
<li>Decide on a <a href="#database_install" title="Decide on a database">database</a> to use
|
||||||
<ol><li>SQLite3 - change database.yml to point to SQLite3 database</li>
|
<ol><li>SQLite3 - change database.yml to point to SQLite3 database. Make sure you add the complete path to the database</li>
|
||||||
<li>MySQL - create new MySQL db and grant all privileges </li></ol></li>
|
<li>MySQL - create new MySQL db and grant all privileges </li></ol></li>
|
||||||
<li><a href="#config_install" title="Configure variables">Configure some variables</a></li>
|
<li><a href="#config_install" title="Configure variables">Configure some variables</a></li>
|
||||||
<li>Populate the database with the <a href="#rake_install" title="Populate your database with the Tracks 1.6 schema">Tracks 1.6 schema</a></li>
|
<li>Populate the database with the <a href="#rake_install" title="Populate your database with the Tracks 1.7 schema">Tracks 1.7 schema</a></li>
|
||||||
<li><a href="#startserver_install" title="Start the server">Start the server</a></li>
|
<li><a href="#startserver_install" title="Start the server">Start the server</a></li>
|
||||||
<li><a href="#signup_install" title="Visit Tracks in a browser">Visit Tracks in a browser</a></li>
|
<li><a href="#signup_install" title="Visit Tracks in a browser">Visit Tracks in a browser</a></li>
|
||||||
<li><a href="#customise_install" title="Customise Tracks">Customise Tracks</a></li>
|
<li><a href="#customise_install" title="Customise Tracks">Customise Tracks</a></li>
|
||||||
|
|
@ -104,14 +102,14 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li><strong>SQLite3</strong>. All you need to do is make sure that you point Tracks to the included SQLite3 database in <code>/db</code> in the next step, <a href="#config_install" title="Configure variables">Configure variables</a>.</li>
|
<li><strong>SQLite3</strong>. All you need to do is make sure that you point Tracks to the included SQLite3 database in <code>/db</code> in the next step, <a href="#config_install" title="Configure variables">Configure variables</a>.</li>
|
||||||
<li><strong>MySQL</strong>. Once you have MySQL installed, you need to create a database to use with Tracks 1.6. Go into a terminal and issue the following commands:</li>
|
<li><strong>MySQL</strong>. Once you have MySQL installed, you need to create a database and database-user to use with Tracks 1.7. For this, you can use MySQL Administrator or go into a terminal and issue the following commands:</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<code>
|
<code>
|
||||||
mysql -uroot -p
|
mysql -uroot -p
|
||||||
mysql> CREATE DATABASE tracks15;
|
mysql> CREATE DATABASE tracks16;
|
||||||
mysql> GRANT ALL PRIVILEGES ON tracks15.* TO yourmysqluser@localhost \
|
mysql> GRANT ALL PRIVILEGES ON tracks16.* TO yourmysqluser@localhost \
|
||||||
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
|
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -119,23 +117,23 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
<h4 id="config_install">Configure variables</h4>
|
<h4 id="config_install">Configure variables</h4>
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>If you downloaded Tracks 1.6 via Subversion, you need to duplicate the files <code>database.yml.tmpl</code> and <code>environment.yml.tmpl</code> and remove the <code>*.tmpl</code> extension from the duplicates. Similarly, duplicate <code>/log.tmpl</code> and remove the <code>*.tmpl</code> extension, then edit the files as described in steps 2 and 3.</li>
|
<li>If you downloaded Tracks 1.7 via Subversion, you need to duplicate the files <code>database.yml.tmpl</code> and <code>environment.yml.tmpl</code> and remove the <code>*.tmpl</code> extension from the duplicates. Similarly, duplicate <code>/log.tmpl</code> and remove the <code>*.tmpl</code> extension, then edit the files as described in steps 2 and 3.</li>
|
||||||
<li>Open the file <code>/config/database.yml</code> and edit the <code>production:</code> section with the details of your database. If you are using MySQL the <code>adapter:</code> line should read <code>adapter: mysql</code>, <code>host: localhost</code> (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: <code>adapter: sqlite3</code> and <code>database: db/tracks-15-blank.db</code>. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.</li>
|
<li>Open the file <code>/config/database.yml</code> and edit the <code>production:</code> section with the details of your database. If you are using MySQL the <code>adapter:</code> line should read <code>adapter: mysql</code>, <code>host: localhost</code> (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: <code>adapter: sqlite3</code> and <code>database: db/tracks-15-blank.db</code>. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.</li>
|
||||||
<li>Open the file <code>/config/environment.rb</code>, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the <code>SALT = "change-me"</code> line (change the string “change-me” to some other string of your choice), and the time zone setting.</li>
|
<li>Open the file <code>/config/environment.rb</code>, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the <code>SALT = "change-me"</code> line (change the string “change-me” to some other string of your choice), and the time zone setting. For the time zone setting, most people will only want to change the config.time_zone option and leave the timezone to use in your database to :utc</li>
|
||||||
<li>If you are using Windows, you may need to check the ‘shebang’ lines (<code>#!/usr/bin/env ruby</code>) of the <code>/public/dispatch.*</code> files and all the files in the <code>/script</code> directory. They are set to <code>#!/usr/bin/env ruby</code> by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like <code>#c:/ruby/bin/ruby</code> to point to the Ruby binary on your system.</li>
|
<li>If you are using Windows, you may need to check the ‘shebang’ lines (<code>#!/usr/bin/env ruby</code>) of the <code>/public/dispatch.*</code> files and all the files in the <code>/script</code> directory. They are set to <code>#!/usr/bin/env ruby</code> by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like <code>#c:/ruby/bin/ruby</code> to point to the Ruby binary on your system.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h4 id="rake_install">Populate your database with the Tracks 1.6 schema</h4>
|
<h4 id="rake_install">Populate your database with the Tracks 1.7 schema</h4>
|
||||||
|
|
||||||
<p>Open a terminal and change into the root of your Tracks 1.6 directory. Enter the following command:</p>
|
<p>Open a terminal and change into the root of your Tracks 1.7 directory. Enter the following command:</p>
|
||||||
|
|
||||||
<p><code>rake db:migrate RAILS_ENV=production</code></p>
|
<p><code>rake db:migrate RAILS_ENV=production</code></p>
|
||||||
|
|
||||||
<p>This will update your database with the required schema for Tracks 1.6. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).</p>
|
<p>This will update your database with the required schema for Tracks 1.7. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).</p>
|
||||||
|
|
||||||
<h4 id="startserver_install">Start the server</h4>
|
<h4 id="startserver_install">Start the server</h4>
|
||||||
|
|
||||||
<p>While still in the Terminal inside the Tracks 1.6 root directory, issue the following command:</p>
|
<p>While still in the Terminal inside the Tracks 1.7 root directory, issue the following command:</p>
|
||||||
|
|
||||||
<p><code>script/server -e production</code></p>
|
<p><code>script/server -e production</code></p>
|
||||||
|
|
||||||
|
|
@ -149,7 +147,21 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<p>Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!</p>
|
<p>Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!</p>
|
||||||
|
|
||||||
<h2 id="upgrading">Upgrading to Tracks 1.6</h2>
|
<h2 id="upgrading">Upgrading to Tracks 1.7</h2>
|
||||||
|
|
||||||
|
<h3 id="upgrading_1.6">Upgrading from Tracks 1.6</h3>
|
||||||
|
|
||||||
|
<p>You will need to upgrade your <code>config/environment.rb</code> with the new content from <code>config/environment.rb.tmpl</code> included in 1.7, as the format of this file has changed a bit between 1.6 and 1.7. Also, there were some database changes in Tracks 1.7, so you need to migrate to them.</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
|
||||||
|
<li><a href="#install_upgrade" title="Install Tracks 1.7">Install Tracks 1.7</a> in a new directory</li>
|
||||||
|
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.6 directory. If using SQLite3, copy the old database into the new Tracks 1.7 directory.</li>
|
||||||
|
<li>Rebuild your environment.rb from the updated environment.rb.tmpl</li>
|
||||||
|
<li>Run <code>rake db:migrate RAILS_ENV=production</code> to <a href="#rake_upgrade" title="Update your old database to the new format">update your old database</a> to the new schema – you did back up your database didn’t you?</li>
|
||||||
|
<li>Run <code>script/server</code> inside your Tracks 1.7 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.7</a>.</li>
|
||||||
|
<li>Once you are happy that everything is working well, <a href="#cleanup_upgrade" title="Clean up your old installation">delete your old Tracks directory</a>.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<h3 id="upgrading_1.5">Upgrading from Tracks 1.5</h3>
|
<h3 id="upgrading_1.5">Upgrading from Tracks 1.5</h3>
|
||||||
|
|
||||||
|
|
@ -157,8 +169,9 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
|
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
|
||||||
<li><a href="#install_upgrade" title="Install Tracks 1.6">Install Tracks 1.6</a> in a new directory</li>
|
<li><a href="#install_upgrade" title="Install Tracks 1.7">Install Tracks 1.6</a> in a new directory</li>
|
||||||
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory</li>
|
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory</li>
|
||||||
|
<li>Rebuild your environment.rb from the updated environment.rb.tmpl</li>
|
||||||
<li>Run <code>script/server</code> inside your Tracks 1.6 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.6</a>.</li>
|
<li>Run <code>script/server</code> inside your Tracks 1.6 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.6</a>.</li>
|
||||||
<li>Once you are happy that everything is working well, <a href="#cleanup_upgrade" title="Clean up your old installation">delete your old Tracks directory</a>.</li>
|
<li>Once you are happy that everything is working well, <a href="#cleanup_upgrade" title="Clean up your old installation">delete your old Tracks directory</a>.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
@ -169,7 +182,7 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
|
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
|
||||||
<li><a href="#install_upgrade" title="Install Tracks 1.6">Install Tracks 1.6</a> in a new directory</li>
|
<li><a href="#install_upgrade" title="Install Tracks 1.7">Install Tracks 1.6</a> in a new directory</li>
|
||||||
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory</li>
|
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory</li>
|
||||||
<li>Run <code>rake db:migrate RAILS_ENV=production</code> to <a href="#rake_upgrade" title="Update your old database to the new format">update your old database</a> to the new schema – you did back up your database didn’t you?</li>
|
<li>Run <code>rake db:migrate RAILS_ENV=production</code> to <a href="#rake_upgrade" title="Update your old database to the new format">update your old database</a> to the new schema – you did back up your database didn’t you?</li>
|
||||||
<li>Run <code>script/server</code> inside your Tracks 1.6 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.6</a>.</li>
|
<li>Run <code>script/server</code> inside your Tracks 1.6 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.6</a>.</li>
|
||||||
|
|
@ -182,11 +195,11 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<p><code>mysqldump –-user [user name] –-password=[password] [database name] > [dump file]</code></p>
|
<p><code>mysqldump –-user [user name] –-password=[password] [database name] > [dump file]</code></p>
|
||||||
|
|
||||||
<p>Rename your old Tracks installation (e.g. to ‘tracks-old’) so that you can install Tracks 1.6 along side it.</p>
|
<p>Rename your old Tracks installation (e.g. to ‘tracks-old’) so that you can install Tracks 1.7 along side it.</p>
|
||||||
|
|
||||||
<h4 id="install_upgrade">Install Tracks 1.6</h4>
|
<h4 id="install_upgrade">Install Tracks 1.7</h4>
|
||||||
|
|
||||||
<p>There are two methods of downloading Tracks 1.6:</p>
|
<p>There are two methods of downloading Tracks 1.7:</p>
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
|
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
|
||||||
|
|
@ -208,7 +221,7 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Copy <code>/config/database.yml</code> from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.</li>
|
<li>Copy <code>/config/database.yml</code> from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.</li>
|
||||||
<li>Duplicate <code>/config/environment.rb.tmpl</code> in the Tracks 1.6 directory, and rename the file to <code>environment.rb</code>. Open the file and alter the line <code>SALT = "change-me"</code> so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location (<code>ENV['TZ'] = 'US/Eastern'</code>). If you have made any other customisations to <code>environment.rb</code> in the past, copy those over, but the contents of the file have changed quite a lot since 1.043, so check it carefully.</li>
|
<li>Duplicate <code>/config/environment.rb.tmpl</code> in the Tracks 1.7 directory, and rename the file to <code>environment.rb</code>. Open the file and alter the line <code>SALT = "change-me"</code> so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location. If you have made any other customisations to <code>environment.rb</code> in the past, copy those over, but the contents of the file have slightly changed since 1.5, so check it carefully.</li>
|
||||||
<li>Copy your <code>/log</code> directory over from your old installation to the root of the new one, or just rename <code>/log.tmpl</code> to <code>log</code> to start afresh.</li>
|
<li>Copy your <code>/log</code> directory over from your old installation to the root of the new one, or just rename <code>/log.tmpl</code> to <code>log</code> to start afresh.</li>
|
||||||
<li>If you are using SQLite3, copy your database from <code>/db</code> in your old Tracks directory to the same location in the new one.</li>
|
<li>If you are using SQLite3, copy your database from <code>/db</code> in your old Tracks directory to the same location in the new one.</li>
|
||||||
<li>If you are using Windows, you may need to check the ‘shebang’ lines (<code>#!/usr/bin/env ruby</code>)<a href="#fn:env" id="fnref:env" class="footnote">1</a> of the <code>/public/dispatch.*</code> files and all the files in the <code>/script</code> directory. They are set to <code>#!/usr/bin/env ruby</code> by default. Check the format of those lines in your old installation, and change the new ones as necessary.</li>
|
<li>If you are using Windows, you may need to check the ‘shebang’ lines (<code>#!/usr/bin/env ruby</code>)<a href="#fn:env" id="fnref:env" class="footnote">1</a> of the <code>/public/dispatch.*</code> files and all the files in the <code>/script</code> directory. They are set to <code>#!/usr/bin/env ruby</code> by default. Check the format of those lines in your old installation, and change the new ones as necessary.</li>
|
||||||
|
|
@ -216,7 +229,7 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<h4 id="rake_upgrade">Update your old database to the new format</h4>
|
<h4 id="rake_upgrade">Update your old database to the new format</h4>
|
||||||
|
|
||||||
<p>In a terminal, change directories so that you are inside the Tracks 1.6 directory. Then issue the command to update your Tracks 1.043 database to the format required for Tracks 1.6:</p>
|
<p>In a terminal, change directories so that you are inside the Tracks 1.7 directory. Then issue the command to update your Tracks 1.6 database to the format required for Tracks 1.7:</p>
|
||||||
|
|
||||||
<p><code>rake db:migrate RAILS_ENV=production</code></p>
|
<p><code>rake db:migrate RAILS_ENV=production</code></p>
|
||||||
|
|
||||||
|
|
@ -224,7 +237,7 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<h4 id="startserver_upgrade">Start the server</h4>
|
<h4 id="startserver_upgrade">Start the server</h4>
|
||||||
|
|
||||||
<p>If you’re still in the Tracks 1.6 root directory in a terminal, enter the following command to start up Tracks in production mode:</p>
|
<p>If you’re still in the Tracks 1.7 root directory in a terminal, enter the following command to start up Tracks in production mode:</p>
|
||||||
|
|
||||||
<p><code>script/server -e production</code></p>
|
<p><code>script/server -e production</code></p>
|
||||||
|
|
||||||
|
|
@ -233,7 +246,7 @@ http://creativecommons.org/licenses/by-nc-sa/3.0/" />
|
||||||
|
|
||||||
<h4 id="cleanup_upgrade">Clean up your old installation</h4>
|
<h4 id="cleanup_upgrade">Clean up your old installation</h4>
|
||||||
|
|
||||||
<p>Once you’re certain that your new Tracks 1.6 installation is working perfectly, you can delete your old Tracks directory.</p>
|
<p>Once you’re certain that your new Tracks 1.7 installation is working perfectly, you can delete your old Tracks directory.</p>
|
||||||
|
|
||||||
<h3 id="upgradingfromversionspriorto1.043">Upgrading from versions prior to 1.043</h3>
|
<h3 id="upgradingfromversionspriorto1.043">Upgrading from versions prior to 1.043</h3>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
Title: Tracks 1.6 Manual
|
Title: Tracks 1.7 Manual
|
||||||
Author: Tracks Development Team
|
Author: Tracks Development Team
|
||||||
Date: 2008-04-07
|
Date: 2008-12-14
|
||||||
Revision: $Id: manual.markdown 864 2008-06-03 17:01:00Z bsag $
|
Revision: $Id: manual.markdown 2008-12-14 11:50:00Z bsag $
|
||||||
Version: 1.6
|
Version: 1.7
|
||||||
Copyright: 2008 rousette.org.uk
|
Copyright: 2008 rousette.org.uk
|
||||||
This work is licensed under a Creative Commons License.
|
This work is licensed under a Creative Commons License.
|
||||||
http://creativecommons.org/licenses/by-nc-sa/3.0/
|
http://creativecommons.org/licenses/by-nc-sa/3.0/
|
||||||
|
|
@ -14,15 +14,13 @@ CSS: manual.css
|
||||||
|
|
||||||
<!-- The HTML file manual.html is generated from manual.markdown, so make edits to the *.markdown file -->
|
<!-- The HTML file manual.html is generated from manual.markdown, so make edits to the *.markdown file -->
|
||||||
|
|
||||||
# Installing Tracks 1.6 #
|
# Installing Tracks 1.7 #
|
||||||
|
|
||||||
## Introduction ##
|
## Introduction ##
|
||||||
|
|
||||||
**An important note for version 1.6: OpenID support is broken in this release. The fix isn't trivial because of changes to the `ruby-openid` gem, so we wanted to get this version out now and fix OpenID for the next release. If you depend on OpenID integration, we recommend waiting until the next release.**
|
Tracks 1.7 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it's only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in `doc/CHANGELOG`. Full API documentation can be found at `doc/app/index.html`, once you have run `rake appdoc`
|
||||||
|
|
||||||
Tracks 1.6 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it's only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in `doc/CHANGELOG`. Full API documentation can be found at `doc/app/index.html`, once you have run `rake appdoc`
|
There are two methods of downloading Tracks 1.7:
|
||||||
|
|
||||||
There are two methods of downloading Tracks 1.6:
|
|
||||||
|
|
||||||
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
|
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
|
||||||
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
||||||
|
|
@ -50,7 +48,7 @@ If you'd like an easy way to access Tracks from any internet-connected computer,
|
||||||
### What is included with the Tracks package ###
|
### What is included with the Tracks package ###
|
||||||
|
|
||||||
1. Tracks itself
|
1. Tracks itself
|
||||||
2. Rails 2.0.2 (installed in the `/vendor/rails` directory, so you do not need to install Rails yourself)
|
2. Rails 2.2.2 (installed in the `/vendor/rails` directory, so you do not need to install Rails yourself)
|
||||||
3. An empty SQLite3 database, set up with the correct database schema
|
3. An empty SQLite3 database, set up with the correct database schema
|
||||||
|
|
||||||
### What you need to install [whatyouneed] ###
|
### What you need to install [whatyouneed] ###
|
||||||
|
|
@ -61,18 +59,18 @@ If you don't want to (or can't) use one of the all in one installations, you'll
|
||||||
2. **RubyGems**. The gems needed by Rails to interact with the database have to be compiled on the platform on which they will be run, so we cannot include them with the Tracks package, unlike some other gems. So you will need to [download](http://rubyforge.org/frs/?group_id=126) and install RubyGems (run `ruby setup.rb` after extracting the package). Note that once again, Mac OS X Leopard users get an easy life, because RubyGems and the SQLite3 gem is already installed. Once installed you can use RubyGems to install the gems you need for your database. If you are using SQLite3, run `sudo gem install sqlite3-ruby`, then select the appropriate package for your platform (version 1.2.1 recommended). You can use MySQL without installing a gem, but installing the gem can speed things up a bit: `sudo install gem mysql`. If you're using Leopard, there are a few work-arounds necessary, which are explained on [Mac OS Forge](http://trac.macosforge.org/projects/ruby/wiki/Troubleshooting#IcannotbuildrubymysqlonLeopardwithmysql.combinaries). The ruby-mysql bindings can sometimes be a bit troublesome to install, so to be honest, it's probably not worth the bother unless you are trying to wring maximum speed out of your system. If you are using PostgreSQL, then you can install a postgres gem: `gem install postgres`.
|
2. **RubyGems**. The gems needed by Rails to interact with the database have to be compiled on the platform on which they will be run, so we cannot include them with the Tracks package, unlike some other gems. So you will need to [download](http://rubyforge.org/frs/?group_id=126) and install RubyGems (run `ruby setup.rb` after extracting the package). Note that once again, Mac OS X Leopard users get an easy life, because RubyGems and the SQLite3 gem is already installed. Once installed you can use RubyGems to install the gems you need for your database. If you are using SQLite3, run `sudo gem install sqlite3-ruby`, then select the appropriate package for your platform (version 1.2.1 recommended). You can use MySQL without installing a gem, but installing the gem can speed things up a bit: `sudo install gem mysql`. If you're using Leopard, there are a few work-arounds necessary, which are explained on [Mac OS Forge](http://trac.macosforge.org/projects/ruby/wiki/Troubleshooting#IcannotbuildrubymysqlonLeopardwithmysql.combinaries). The ruby-mysql bindings can sometimes be a bit troublesome to install, so to be honest, it's probably not worth the bother unless you are trying to wring maximum speed out of your system. If you are using PostgreSQL, then you can install a postgres gem: `gem install postgres`.
|
||||||
3. **Database**. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the `sqlite3-ruby` gem, as described in step 2, and the SQLite3 libraries and binary (see [sqlite.org](http://sqlite.org/download.html) for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from [MySQL.com](http://dev.mysql.com/downloads/mysql/5.0.html). The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.
|
3. **Database**. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the `sqlite3-ruby` gem, as described in step 2, and the SQLite3 libraries and binary (see [sqlite.org](http://sqlite.org/download.html) for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from [MySQL.com](http://dev.mysql.com/downloads/mysql/5.0.html). The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.
|
||||||
|
|
||||||
If you are using Unix, you might find [this guide](http://www.cooldown.com.ar/2006/12/16/install-tracks-on-ubuntu-or-debian/) by c00i90wn helpful. It was written for Tracks 1.043, but it should work for Tracks 1.6.
|
You can find several installation howtos for specific setups [here](http://dev.rousette.org.uk/wiki/Tracks/Install). They were contributed by various Tracks users.
|
||||||
|
|
||||||
## Installation ##
|
## Installation ##
|
||||||
|
|
||||||
This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see [Upgrading to Tracks 1.6][upgrading].
|
This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see [Upgrading to Tracks 1.7][upgrading].
|
||||||
|
|
||||||
1. [Unzip tracks][unzip_install] and install in a directory
|
1. [Unzip tracks][unzip_install] and install in a directory
|
||||||
2. Decide on a [database][database_install] to use
|
2. Decide on a [database][database_install] to use
|
||||||
1. SQLite3 - change database.yml to point to SQLite3 database
|
1. SQLite3 - change database.yml to point to SQLite3 database. Make sure you add the complete path to the database
|
||||||
2. MySQL - create new MySQL db and grant all privileges
|
2. MySQL - create new MySQL db and grant all privileges
|
||||||
3. [Configure some variables][config_install]
|
3. [Configure some variables][config_install]
|
||||||
4. Populate the database with the [Tracks 1.6 schema][rake_install]
|
4. Populate the database with the [Tracks 1.7 schema][rake_install]
|
||||||
5. [Start the server][startserver_install]
|
5. [Start the server][startserver_install]
|
||||||
6. [Visit Tracks in a browser][signup_install]
|
6. [Visit Tracks in a browser][signup_install]
|
||||||
7. [Customise Tracks][customise_install]
|
7. [Customise Tracks][customise_install]
|
||||||
|
|
@ -86,34 +84,34 @@ Unzip the package and move Tracks into the directory you want to run it from. Fo
|
||||||
Before you go any further, you need to decide which database you will use. See the [What you need to install][whatyouneed] section for details on installing the required components for you choice of database.
|
Before you go any further, you need to decide which database you will use. See the [What you need to install][whatyouneed] section for details on installing the required components for you choice of database.
|
||||||
|
|
||||||
1. **SQLite3**. All you need to do is make sure that you point Tracks to the included SQLite3 database in `/db` in the next step, [Configure variables][config_install].
|
1. **SQLite3**. All you need to do is make sure that you point Tracks to the included SQLite3 database in `/db` in the next step, [Configure variables][config_install].
|
||||||
2. **MySQL**. Once you have MySQL installed, you need to create a database to use with Tracks 1.6. Go into a terminal and issue the following commands:
|
2. **MySQL**. Once you have MySQL installed, you need to create a database and database-user to use with Tracks 1.7. For this, you can use MySQL Administrator or go into a terminal and issue the following commands:
|
||||||
<pre>
|
<pre>
|
||||||
<code>
|
<code>
|
||||||
mysql -uroot -p
|
mysql -uroot -p
|
||||||
mysql> CREATE DATABASE tracks15;
|
mysql> CREATE DATABASE tracks16;
|
||||||
mysql> GRANT ALL PRIVILEGES ON tracks15.* TO yourmysqluser@localhost \
|
mysql> GRANT ALL PRIVILEGES ON tracks16.* TO yourmysqluser@localhost \
|
||||||
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
|
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### Configure variables [config_install] ###
|
### Configure variables [config_install] ###
|
||||||
|
|
||||||
1. If you downloaded Tracks 1.6 via Subversion, you need to duplicate the files `database.yml.tmpl` and `environment.yml.tmpl` and remove the `*.tmpl` extension from the duplicates. Similarly, duplicate `/log.tmpl` and remove the `*.tmpl` extension, then edit the files as described in steps 2 and 3.
|
1. If you downloaded Tracks 1.7 via Subversion, you need to duplicate the files `database.yml.tmpl` and `environment.yml.tmpl` and remove the `*.tmpl` extension from the duplicates. Similarly, duplicate `/log.tmpl` and remove the `*.tmpl` extension, then edit the files as described in steps 2 and 3.
|
||||||
2. Open the file `/config/database.yml` and edit the `production:` section with the details of your database. If you are using MySQL the `adapter:` line should read `adapter: mysql`, `host: localhost` (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: `adapter: sqlite3` and `database: db/tracks-15-blank.db`. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.
|
2. Open the file `/config/database.yml` and edit the `production:` section with the details of your database. If you are using MySQL the `adapter:` line should read `adapter: mysql`, `host: localhost` (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: `adapter: sqlite3` and `database: db/tracks-15-blank.db`. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.
|
||||||
3. Open the file `/config/environment.rb`, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the `SALT = "change-me"` line (change the string "change-me" to some other string of your choice), and the time zone setting.
|
3. Open the file `/config/environment.rb`, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the `SALT = "change-me"` line (change the string "change-me" to some other string of your choice), and the time zone setting. For the time zone setting, most people will only want to change the config.time_zone option and leave the timezone to use in your database to :utc
|
||||||
4. If you are using Windows, you may need to check the 'shebang' lines (`#!/usr/bin/env ruby`) of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like `#c:/ruby/bin/ruby` to point to the Ruby binary on your system.
|
4. If you are using Windows, you may need to check the 'shebang' lines (`#!/usr/bin/env ruby`) of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like `#c:/ruby/bin/ruby` to point to the Ruby binary on your system.
|
||||||
|
|
||||||
### Populate your database with the Tracks 1.6 schema [rake_install] ###
|
### Populate your database with the Tracks 1.7 schema [rake_install] ###
|
||||||
|
|
||||||
Open a terminal and change into the root of your Tracks 1.6 directory. Enter the following command:
|
Open a terminal and change into the root of your Tracks 1.7 directory. Enter the following command:
|
||||||
|
|
||||||
`rake db:migrate RAILS_ENV=production`
|
`rake db:migrate RAILS_ENV=production`
|
||||||
|
|
||||||
This will update your database with the required schema for Tracks 1.6. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).
|
This will update your database with the required schema for Tracks 1.7. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).
|
||||||
|
|
||||||
### Start the server [startserver_install] ###
|
### Start the server [startserver_install] ###
|
||||||
|
|
||||||
While still in the Terminal inside the Tracks 1.6 root directory, issue the following command:
|
While still in the Terminal inside the Tracks 1.7 root directory, issue the following command:
|
||||||
|
|
||||||
`script/server -e production`
|
`script/server -e production`
|
||||||
|
|
||||||
|
|
@ -127,7 +125,19 @@ Visit `http://0.0.0.0:3000/signup` in a browser (or whatever URL and port was re
|
||||||
|
|
||||||
Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!
|
Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!
|
||||||
|
|
||||||
# Upgrading to Tracks 1.6 [upgrading] #
|
# Upgrading to Tracks 1.7 [upgrading] #
|
||||||
|
|
||||||
|
## Upgrading from Tracks 1.6 [upgrading_1.6] ##
|
||||||
|
|
||||||
|
You will need to upgrade your `config/environment.rb` with the new content from `config/environment.rb.tmpl` included in 1.7, as the format of this file has changed a bit between 1.6 and 1.7. Also, there were some database changes in Tracks 1.7, so you need to migrate to them.
|
||||||
|
|
||||||
|
1. [Back up][backup_upgrade] your existing database and installation of Tracks
|
||||||
|
2. [Install Tracks 1.7][install_upgrade] in a new directory
|
||||||
|
3. [Copy over][config_upgrade] a few configuration files from your Tracks 1.6 directory. If using SQLite3, copy the old database into the new Tracks 1.7 directory.
|
||||||
|
4. Rebuild your environment.rb from the updated environment.rb.tmpl
|
||||||
|
5. Run `rake db:migrate RAILS_ENV=production` to [update your old database][rake_upgrade] to the new schema -- you did back up your database didn't you?
|
||||||
|
6. Run `script/server` inside your Tracks 1.7 directory to [start up Tracks 1.7][startserver_upgrade].
|
||||||
|
7. Once you are happy that everything is working well, [delete your old Tracks directory][cleanup_upgrade].
|
||||||
|
|
||||||
## Upgrading from Tracks 1.5 [upgrading_1.5] ##
|
## Upgrading from Tracks 1.5 [upgrading_1.5] ##
|
||||||
|
|
||||||
|
|
@ -136,9 +146,9 @@ There are no changes to the database between 1.5 and 1.6, but you will need to u
|
||||||
1. [Back up][backup_upgrade] your existing database and installation of Tracks
|
1. [Back up][backup_upgrade] your existing database and installation of Tracks
|
||||||
2. [Install Tracks 1.6][install_upgrade] in a new directory
|
2. [Install Tracks 1.6][install_upgrade] in a new directory
|
||||||
3. [Copy over][config_upgrade] a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
|
3. [Copy over][config_upgrade] a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
|
||||||
4. Run `script/server` inside your Tracks 1.6 directory to [start up Tracks 1.6][startserver_upgrade].
|
4. Rebuild your environment.rb from the updated environment.rb.tmpl
|
||||||
5. Once you are happy that everything is working well, [delete your old Tracks directory][cleanup_upgrade].
|
5. Run `script/server` inside your Tracks 1.6 directory to [start up Tracks 1.6][startserver_upgrade].
|
||||||
|
6. Once you are happy that everything is working well, [delete your old Tracks directory][cleanup_upgrade].
|
||||||
|
|
||||||
## Upgrading from Tracks 1.043 [upgrading_1043] ##
|
## Upgrading from Tracks 1.043 [upgrading_1043] ##
|
||||||
|
|
||||||
|
|
@ -157,11 +167,11 @@ It's very important that you **back up your database** before you start the upgr
|
||||||
|
|
||||||
`mysqldump –-user [user name] –-password=[password] [database name] > [dump file]`
|
`mysqldump –-user [user name] –-password=[password] [database name] > [dump file]`
|
||||||
|
|
||||||
Rename your old Tracks installation (e.g. to 'tracks-old') so that you can install Tracks 1.6 along side it.
|
Rename your old Tracks installation (e.g. to 'tracks-old') so that you can install Tracks 1.7 along side it.
|
||||||
|
|
||||||
### Install Tracks 1.6 [install_upgrade] ###
|
### Install Tracks 1.7 [install_upgrade] ###
|
||||||
|
|
||||||
There are two methods of downloading Tracks 1.6:
|
There are two methods of downloading Tracks 1.7:
|
||||||
|
|
||||||
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
|
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
|
||||||
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
|
||||||
|
|
@ -179,14 +189,14 @@ There are two methods of downloading Tracks 1.6:
|
||||||
There are a few files you need to copy over from your old installation. If you copy them over rather than moving them, you can still run your old version of Tracks if anything goes awry with the installation process.
|
There are a few files you need to copy over from your old installation. If you copy them over rather than moving them, you can still run your old version of Tracks if anything goes awry with the installation process.
|
||||||
|
|
||||||
1. Copy `/config/database.yml` from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.
|
1. Copy `/config/database.yml` from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.
|
||||||
2. Duplicate `/config/environment.rb.tmpl` in the Tracks 1.6 directory, and rename the file to `environment.rb`. Open the file and alter the line `SALT = "change-me"` so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location (`ENV['TZ'] = 'US/Eastern'`). If you have made any other customisations to `environment.rb` in the past, copy those over, but the contents of the file have changed quite a lot since 1.043, so check it carefully.
|
2. Duplicate `/config/environment.rb.tmpl` in the Tracks 1.7 directory, and rename the file to `environment.rb`. Open the file and alter the line `SALT = "change-me"` so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location. If you have made any other customisations to `environment.rb` in the past, copy those over, but the contents of the file have slightly changed since 1.5, so check it carefully.
|
||||||
3. Copy your `/log` directory over from your old installation to the root of the new one, or just rename `/log.tmpl` to `log` to start afresh.
|
3. Copy your `/log` directory over from your old installation to the root of the new one, or just rename `/log.tmpl` to `log` to start afresh.
|
||||||
4. If you are using SQLite3, copy your database from `/db` in your old Tracks directory to the same location in the new one.
|
4. If you are using SQLite3, copy your database from `/db` in your old Tracks directory to the same location in the new one.
|
||||||
5. If you are using Windows, you may need to check the 'shebang' lines (`#!/usr/bin/env ruby`)[^env] of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. Check the format of those lines in your old installation, and change the new ones as necessary.
|
5. If you are using Windows, you may need to check the 'shebang' lines (`#!/usr/bin/env ruby`)[^env] of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. Check the format of those lines in your old installation, and change the new ones as necessary.
|
||||||
|
|
||||||
### Update your old database to the new format [rake_upgrade] ###
|
### Update your old database to the new format [rake_upgrade] ###
|
||||||
|
|
||||||
In a terminal, change directories so that you are inside the Tracks 1.6 directory. Then issue the command to update your Tracks 1.043 database to the format required for Tracks 1.6:
|
In a terminal, change directories so that you are inside the Tracks 1.7 directory. Then issue the command to update your Tracks 1.6 database to the format required for Tracks 1.7:
|
||||||
|
|
||||||
`rake db:migrate RAILS_ENV=production`
|
`rake db:migrate RAILS_ENV=production`
|
||||||
|
|
||||||
|
|
@ -194,7 +204,7 @@ Watch the output carefully for errors, but it should report at the end of the pr
|
||||||
|
|
||||||
### Start the server [startserver_upgrade] ###
|
### Start the server [startserver_upgrade] ###
|
||||||
|
|
||||||
If you're still in the Tracks 1.6 root directory in a terminal, enter the following command to start up Tracks in production mode:
|
If you're still in the Tracks 1.7 root directory in a terminal, enter the following command to start up Tracks in production mode:
|
||||||
|
|
||||||
`script/server -e production`
|
`script/server -e production`
|
||||||
|
|
||||||
|
|
@ -203,7 +213,7 @@ Visit the URL indicated by the output (e.g. `** Mongrel available at 0.0.0.0:300
|
||||||
|
|
||||||
### Clean up your old installation [cleanup_upgrade] ###
|
### Clean up your old installation [cleanup_upgrade] ###
|
||||||
|
|
||||||
Once you're certain that your new Tracks 1.6 installation is working perfectly, you can delete your old Tracks directory.
|
Once you're certain that your new Tracks 1.7 installation is working perfectly, you can delete your old Tracks directory.
|
||||||
|
|
||||||
[^env]: The `env` binary helps to locate other binaries, regardless of their location. If you don't have `env` installed, you'll need to change this line to point to the location of your Ruby binary.
|
[^env]: The `env` binary helps to locate other binaries, regardless of their location. If you don't have `env` installed, you'll need to change this line to point to the location of your Ruby binary.
|
||||||
|
|
||||||
|
|
|
||||||
BIN
doc/manual.pdf
BIN
doc/manual.pdf
Binary file not shown.
105
doc/manual.tex
105
doc/manual.tex
|
|
@ -112,12 +112,12 @@
|
||||||
\usepackage{booktabs} % Better tables
|
\usepackage{booktabs} % Better tables
|
||||||
\usepackage{tabulary} % Support longer table cells
|
\usepackage{tabulary} % Support longer table cells
|
||||||
\usepackage[utf8]{inputenc} % For UTF-8 support
|
\usepackage[utf8]{inputenc} % For UTF-8 support
|
||||||
% \usepackage{xcolor} % Allow for color (annotations)
|
%\usepackage{xcolor} % Allow for color (annotations)
|
||||||
%
|
|
||||||
% %\geometry{landscape} % Activate for rotated page geometry
|
%\geometry{landscape} % Activate for rotated page geometry
|
||||||
%
|
|
||||||
% \usepackage[parfill]{parskip} % Activate to begin paragraphs with an empty
|
%\usepackage[parfill]{parskip} % Activate to begin paragraphs with an empty
|
||||||
% % line rather than an indent
|
% line rather than an indent
|
||||||
|
|
||||||
|
|
||||||
\def\myauthor{Author} % In case these were not included in metadata
|
\def\myauthor{Author} % In case these were not included in metadata
|
||||||
|
|
@ -130,14 +130,14 @@
|
||||||
\def\myauthor{Tracks Development Team}
|
\def\myauthor{Tracks Development Team}
|
||||||
\def\baseheaderlevel{2}
|
\def\baseheaderlevel{2}
|
||||||
\def\mycopyright{2008 rousette.org.uk \\ This work is licensed under a Creative Commons License. \\ http://creativecommons.org/licenses/by-nc-sa/3.0/}
|
\def\mycopyright{2008 rousette.org.uk \\ This work is licensed under a Creative Commons License. \\ http://creativecommons.org/licenses/by-nc-sa/3.0/}
|
||||||
\date{2008-04-07}
|
\date{2008-12-14}
|
||||||
\def\format{complete}
|
\def\format{complete}
|
||||||
\def\latexxslt{memoir-twosided-manual.xslt}
|
\def\latexxslt{memoir-twosided-manual.xslt}
|
||||||
\def\revision{Revision: \$Id: manual.markdown 864 2008-06-03 17:01:00Z bsag \$}
|
\def\revision{Revision: \$Id: manual.markdown 2008-12-14 11:50:00Z bsag \$}
|
||||||
\def\mytitle{Tracks 1.6 Manual}
|
\def\mytitle{Tracks 1.7 Manual}
|
||||||
\def\version{1.6}
|
\def\version{1.7}
|
||||||
% \usepackage{xmpincl}
|
\usepackage{xmpincl}
|
||||||
% \includexmp{CCAttributionShareAlike}
|
\includexmp{CCAttributionShareAlike}
|
||||||
|
|
||||||
|
|
||||||
%
|
%
|
||||||
|
|
@ -254,19 +254,16 @@
|
||||||
\setlength{\parskip}{\baselineskip/2}
|
\setlength{\parskip}{\baselineskip/2}
|
||||||
|
|
||||||
\mainmatter
|
\mainmatter
|
||||||
\chapter{Installing Tracks 1.6}
|
\chapter{Installing Tracks 1.7}
|
||||||
\label{installingtracks1.6}
|
\label{installingtracks1.7}
|
||||||
|
|
||||||
\section{Introduction}
|
\section{Introduction}
|
||||||
\label{introduction}
|
\label{introduction}
|
||||||
|
|
||||||
\textbf{An important note for version 1.6: OpenID support is broken in this release. The fix isn't trivial because of changes to the \texttt{ruby-openid} gem, so we wanted to get this version out now and fix OpenID for the next release. If you depend on OpenID integration, we recommend waiting until the next release.}
|
Tracks 1.7 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it's only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in \texttt{doc/CHANGELOG}. Full API documentation can be found at \texttt{doc/app/index.html}, once you have run \texttt{rake appdoc}
|
||||||
|
|
||||||
|
|
||||||
Tracks 1.6 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it's only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in \texttt{doc/CHANGELOG}. Full API documentation can be found at \texttt{doc/app/index.html}, once you have run \texttt{rake appdoc}
|
There are two methods of downloading Tracks 1.7:
|
||||||
|
|
||||||
|
|
||||||
There are two methods of downloading Tracks 1.6:
|
|
||||||
|
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
|
@ -315,7 +312,7 @@ If you'd like an easy way to access Tracks from any internet-connected computer,
|
||||||
|
|
||||||
\item Tracks itself
|
\item Tracks itself
|
||||||
|
|
||||||
\item Rails 2.0.2 (installed in the \texttt{/vendor/rails} directory, so you do not need to install Rails yourself)
|
\item Rails 2.2.2 (installed in the \texttt{/vendor/rails} directory, so you do not need to install Rails yourself)
|
||||||
|
|
||||||
\item An empty SQLite3 database, set up with the correct database schema
|
\item An empty SQLite3 database, set up with the correct database schema
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
@ -336,13 +333,13 @@ If you don't want to (or can't) use one of the all in one installations, you'll
|
||||||
\item \textbf{Database}. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the \texttt{sqlite3-ruby} gem, as described in step 2, and the SQLite3 libraries and binary (see \href{http://sqlite.org/download.html}{sqlite.org} for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from \href{http://dev.mysql.com/downloads/mysql/5.0.html}{MySQL.com}. The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.
|
\item \textbf{Database}. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the \texttt{sqlite3-ruby} gem, as described in step 2, and the SQLite3 libraries and binary (see \href{http://sqlite.org/download.html}{sqlite.org} for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from \href{http://dev.mysql.com/downloads/mysql/5.0.html}{MySQL.com}. The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
If you are using Unix, you might find \href{http://www.cooldown.com.ar/2006/12/16/install-tracks-on-ubuntu-or-debian/}{this guide} by c00i90wn helpful. It was written for Tracks 1.043, but it should work for Tracks 1.6.
|
You can find several installation howtos for specific setups \href{http://dev.rousette.org.uk/wiki/Tracks/Install}{here}. They were contributed by various Tracks users.
|
||||||
|
|
||||||
|
|
||||||
\section{Installation}
|
\section{Installation}
|
||||||
\label{installation}
|
\label{installation}
|
||||||
|
|
||||||
This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see Upgrading to Tracks 1.6 (\autoref{upgrading}).
|
This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see Upgrading to Tracks 1.7 (\autoref{upgrading}).
|
||||||
|
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
|
@ -354,7 +351,7 @@ This description is intended for people installing Tracks from scratch. If you w
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
|
||||||
|
|
||||||
\item SQLite3 - change database.yml to point to SQLite3 database
|
\item SQLite3 - change database.yml to point to SQLite3 database. Make sure you add the complete path to the database
|
||||||
|
|
||||||
\item MySQL - create new MySQL db and grant all privileges
|
\item MySQL - create new MySQL db and grant all privileges
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
@ -363,7 +360,7 @@ This description is intended for people installing Tracks from scratch. If you w
|
||||||
|
|
||||||
\item Configure some variables (\autoref{config_install})
|
\item Configure some variables (\autoref{config_install})
|
||||||
|
|
||||||
\item Populate the database with the Tracks 1.6 schema (\autoref{rake_install})
|
\item Populate the database with the Tracks 1.7 schema (\autoref{rake_install})
|
||||||
|
|
||||||
\item Start the server (\autoref{startserver_install})
|
\item Start the server (\autoref{startserver_install})
|
||||||
|
|
||||||
|
|
@ -389,7 +386,7 @@ Before you go any further, you need to decide which database you will use. See t
|
||||||
|
|
||||||
\item \textbf{SQLite3}. All you need to do is make sure that you point Tracks to the included SQLite3 database in \texttt{/db} in the next step, Configure variables (\autoref{config_install}).
|
\item \textbf{SQLite3}. All you need to do is make sure that you point Tracks to the included SQLite3 database in \texttt{/db} in the next step, Configure variables (\autoref{config_install}).
|
||||||
|
|
||||||
\item \textbf{MySQL}. Once you have MySQL installed, you need to create a database to use with Tracks 1.6. Go into a terminal and issue the following commands:
|
\item \textbf{MySQL}. Once you have MySQL installed, you need to create a database and database-user to use with Tracks 1.7. For this, you can use MySQL Administrator or go into a terminal and issue the following commands:
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
\begin{adjustwidth}{2.5em}{2.5em}
|
\begin{adjustwidth}{2.5em}{2.5em}
|
||||||
|
|
@ -397,8 +394,8 @@ Before you go any further, you need to decide which database you will use. See t
|
||||||
|
|
||||||
|
|
||||||
mysql -uroot -p
|
mysql -uroot -p
|
||||||
mysql> CREATE DATABASE tracks15;
|
mysql> CREATE DATABASE tracks16;
|
||||||
mysql> GRANT ALL PRIVILEGES ON tracks15.* TO yourmysqluser@localhost \
|
mysql> GRANT ALL PRIVILEGES ON tracks16.* TO yourmysqluser@localhost \
|
||||||
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
|
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -411,31 +408,31 @@ Before you go any further, you need to decide which database you will use. See t
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
|
||||||
|
|
||||||
\item If you downloaded Tracks 1.6 via Subversion, you need to duplicate the files \texttt{database.yml.tmpl} and \texttt{environment.yml.tmpl} and remove the \texttt{*.tmpl} extension from the duplicates. Similarly, duplicate \texttt{/log.tmpl} and remove the \texttt{*.tmpl} extension, then edit the files as described in steps 2 and 3.
|
\item If you downloaded Tracks 1.7 via Subversion, you need to duplicate the files \texttt{database.yml.tmpl} and \texttt{environment.yml.tmpl} and remove the \texttt{*.tmpl} extension from the duplicates. Similarly, duplicate \texttt{/log.tmpl} and remove the \texttt{*.tmpl} extension, then edit the files as described in steps 2 and 3.
|
||||||
|
|
||||||
\item Open the file \texttt{/config/database.yml} and edit the \texttt{production:} section with the details of your database. If you are using MySQL the \texttt{adapter:} line should read \texttt{adapter: mysql}, \texttt{host: localhost} (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: \texttt{adapter: sqlite3} and \texttt{database: db/tracks-15-blank.db}. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.
|
\item Open the file \texttt{/config/database.yml} and edit the \texttt{production:} section with the details of your database. If you are using MySQL the \texttt{adapter:} line should read \texttt{adapter: mysql}, \texttt{host: localhost} (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: \texttt{adapter: sqlite3} and \texttt{database: db/tracks-15-blank.db}. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.
|
||||||
|
|
||||||
\item Open the file \texttt{/config/environment.rb}, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the \texttt{SALT = "change-me"} line (change the string ``change-me" to some other string of your choice), and the time zone setting.
|
\item Open the file \texttt{/config/environment.rb}, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the \texttt{SALT = "change-me"} line (change the string ``change-me" to some other string of your choice), and the time zone setting. For the time zone setting, most people will only want to change the config.time\_zone option and leave the timezone to use in your database to :utc
|
||||||
|
|
||||||
\item If you are using Windows, you may need to check the `shebang' lines (\texttt{\#!/usr/bin/env ruby}) of the \texttt{/public/dispatch.*} files and all the files in the \texttt{/script} directory. They are set to \texttt{\#!/usr/bin/env ruby} by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like \texttt{\#c:/ruby/bin/ruby} to point to the Ruby binary on your system.
|
\item If you are using Windows, you may need to check the `shebang' lines (\texttt{\#!/usr/bin/env ruby}) of the \texttt{/public/dispatch.*} files and all the files in the \texttt{/script} directory. They are set to \texttt{\#!/usr/bin/env ruby} by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like \texttt{\#c:/ruby/bin/ruby} to point to the Ruby binary on your system.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
\subsection{Populate your database with the Tracks 1.6 schema}
|
\subsection{Populate your database with the Tracks 1.7 schema}
|
||||||
\label{rake_install}
|
\label{rake_install}
|
||||||
|
|
||||||
Open a terminal and change into the root of your Tracks 1.6 directory. Enter the following command:
|
Open a terminal and change into the root of your Tracks 1.7 directory. Enter the following command:
|
||||||
|
|
||||||
|
|
||||||
\texttt{rake db:migrate RAILS\_ENV=production}
|
\texttt{rake db:migrate RAILS\_ENV=production}
|
||||||
|
|
||||||
|
|
||||||
This will update your database with the required schema for Tracks 1.6. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).
|
This will update your database with the required schema for Tracks 1.7. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).
|
||||||
|
|
||||||
|
|
||||||
\subsection{Start the server}
|
\subsection{Start the server}
|
||||||
\label{startserver_install}
|
\label{startserver_install}
|
||||||
|
|
||||||
While still in the Terminal inside the Tracks 1.6 root directory, issue the following command:
|
While still in the Terminal inside the Tracks 1.7 root directory, issue the following command:
|
||||||
|
|
||||||
|
|
||||||
\texttt{script/server -e production}
|
\texttt{script/server -e production}
|
||||||
|
|
@ -456,9 +453,33 @@ Visit \texttt{http://0.0.0.0:3000/signup} in a browser (or whatever URL and port
|
||||||
Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!
|
Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!
|
||||||
|
|
||||||
|
|
||||||
\chapter{Upgrading to Tracks 1.6}
|
\chapter{Upgrading to Tracks 1.7}
|
||||||
\label{upgrading}
|
\label{upgrading}
|
||||||
|
|
||||||
|
\section{Upgrading from Tracks 1.6}
|
||||||
|
\label{upgrading_1.6}
|
||||||
|
|
||||||
|
You will need to upgrade your \texttt{config/environment.rb} with the new content from \texttt{config/environment.rb.tmpl} included in 1.7, as the format of this file has changed a bit between 1.6 and 1.7. Also, there were some database changes in Tracks 1.7, so you need to migrate to them.
|
||||||
|
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
|
||||||
|
|
||||||
|
\item Back up (\autoref{backup_upgrade}) your existing database and installation of Tracks
|
||||||
|
|
||||||
|
\item Install Tracks 1.7 (\autoref{install_upgrade}) in a new directory
|
||||||
|
|
||||||
|
\item Copy over (\autoref{config_upgrade}) a few configuration files from your Tracks 1.6 directory. If using SQLite3, copy the old database into the new Tracks 1.7 directory.
|
||||||
|
|
||||||
|
\item Rebuild your environment.rb from the updated environment.rb.tmpl
|
||||||
|
|
||||||
|
\item Run \texttt{rake db:migrate RAILS\_ENV=production} to update your old database (\autoref{rake_upgrade}) to the new schema -- you did back up your database didn't you?
|
||||||
|
|
||||||
|
\item Run \texttt{script/server} inside your Tracks 1.7 directory to start up Tracks 1.7 (\autoref{startserver_upgrade}).
|
||||||
|
|
||||||
|
\item Once you are happy that everything is working well, delete your old Tracks directory (\autoref{cleanup_upgrade}).
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
\section{Upgrading from Tracks 1.5}
|
\section{Upgrading from Tracks 1.5}
|
||||||
\label{upgrading_1.5}
|
\label{upgrading_1.5}
|
||||||
|
|
||||||
|
|
@ -474,6 +495,8 @@ There are no changes to the database between 1.5 and 1.6, but you will need to u
|
||||||
|
|
||||||
\item Copy over (\autoref{config_upgrade}) a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
|
\item Copy over (\autoref{config_upgrade}) a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
|
||||||
|
|
||||||
|
\item Rebuild your environment.rb from the updated environment.rb.tmpl
|
||||||
|
|
||||||
\item Run \texttt{script/server} inside your Tracks 1.6 directory to start up Tracks 1.6 (\autoref{startserver_upgrade}).
|
\item Run \texttt{script/server} inside your Tracks 1.6 directory to start up Tracks 1.6 (\autoref{startserver_upgrade}).
|
||||||
|
|
||||||
\item Once you are happy that everything is working well, delete your old Tracks directory (\autoref{cleanup_upgrade}).
|
\item Once you are happy that everything is working well, delete your old Tracks directory (\autoref{cleanup_upgrade}).
|
||||||
|
|
@ -510,13 +533,13 @@ It's very important that you \textbf{back up your database} before you start the
|
||||||
\texttt{mysqldump ---user [user name] ---password=[password] [database name] $>$ [dump file]}
|
\texttt{mysqldump ---user [user name] ---password=[password] [database name] $>$ [dump file]}
|
||||||
|
|
||||||
|
|
||||||
Rename your old Tracks installation (e.g.\ to `tracks-old') so that you can install Tracks 1.6 along side it.
|
Rename your old Tracks installation (e.g.\ to `tracks-old') so that you can install Tracks 1.7 along side it.
|
||||||
|
|
||||||
|
|
||||||
\subsection{Install Tracks 1.6}
|
\subsection{Install Tracks 1.7}
|
||||||
\label{install_upgrade}
|
\label{install_upgrade}
|
||||||
|
|
||||||
There are two methods of downloading Tracks 1.6:
|
There are two methods of downloading Tracks 1.7:
|
||||||
|
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
|
@ -550,7 +573,7 @@ There are a few files you need to copy over from your old installation. If you c
|
||||||
|
|
||||||
\item Copy \texttt{/config/database.yml} from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.
|
\item Copy \texttt{/config/database.yml} from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.
|
||||||
|
|
||||||
\item Duplicate \texttt{/config/environment.rb.tmpl} in the Tracks 1.6 directory, and rename the file to \texttt{environment.rb}. Open the file and alter the line \texttt{SALT = "change-me"} so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location (\texttt{ENV['TZ'] = 'US/Eastern'}). If you have made any other customisations to \texttt{environment.rb} in the past, copy those over, but the contents of the file have changed quite a lot since 1.043, so check it carefully.
|
\item Duplicate \texttt{/config/environment.rb.tmpl} in the Tracks 1.7 directory, and rename the file to \texttt{environment.rb}. Open the file and alter the line \texttt{SALT = "change-me"} so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location. If you have made any other customisations to \texttt{environment.rb} in the past, copy those over, but the contents of the file have slightly changed since 1.5, so check it carefully.
|
||||||
|
|
||||||
\item Copy your \texttt{/log} directory over from your old installation to the root of the new one, or just rename \texttt{/log.tmpl} to \texttt{log} to start afresh.
|
\item Copy your \texttt{/log} directory over from your old installation to the root of the new one, or just rename \texttt{/log.tmpl} to \texttt{log} to start afresh.
|
||||||
|
|
||||||
|
|
@ -562,7 +585,7 @@ There are a few files you need to copy over from your old installation. If you c
|
||||||
\subsection{Update your old database to the new format}
|
\subsection{Update your old database to the new format}
|
||||||
\label{rake_upgrade}
|
\label{rake_upgrade}
|
||||||
|
|
||||||
In a terminal, change directories so that you are inside the Tracks 1.6 directory. Then issue the command to update your Tracks 1.043 database to the format required for Tracks 1.6:
|
In a terminal, change directories so that you are inside the Tracks 1.7 directory. Then issue the command to update your Tracks 1.6 database to the format required for Tracks 1.7:
|
||||||
|
|
||||||
|
|
||||||
\texttt{rake db:migrate RAILS\_ENV=production}
|
\texttt{rake db:migrate RAILS\_ENV=production}
|
||||||
|
|
@ -574,7 +597,7 @@ Watch the output carefully for errors, but it should report at the end of the pr
|
||||||
\subsection{Start the server}
|
\subsection{Start the server}
|
||||||
\label{startserver_upgrade}
|
\label{startserver_upgrade}
|
||||||
|
|
||||||
If you're still in the Tracks 1.6 root directory in a terminal, enter the following command to start up Tracks in production mode:
|
If you're still in the Tracks 1.7 root directory in a terminal, enter the following command to start up Tracks in production mode:
|
||||||
|
|
||||||
|
|
||||||
\texttt{script/server -e production}
|
\texttt{script/server -e production}
|
||||||
|
|
@ -587,7 +610,7 @@ Visit the URL indicated by the output (e.g.\ \texttt{** Mongrel available at 0.0
|
||||||
\subsection{Clean up your old installation}
|
\subsection{Clean up your old installation}
|
||||||
\label{cleanup_upgrade}
|
\label{cleanup_upgrade}
|
||||||
|
|
||||||
Once you're certain that your new Tracks 1.6 installation is working perfectly, you can delete your old Tracks directory.
|
Once you're certain that your new Tracks 1.7 installation is working perfectly, you can delete your old Tracks directory.
|
||||||
|
|
||||||
|
|
||||||
\section{Upgrading from versions prior to 1.043}
|
\section{Upgrading from versions prior to 1.043}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ namespace :tracks do
|
||||||
desc 'Replace the password of USER with a new one.'
|
desc 'Replace the password of USER with a new one.'
|
||||||
task :password => :environment do
|
task :password => :environment do
|
||||||
|
|
||||||
Dependencies.load_paths.unshift(File.dirname(__FILE__) + "/..../vendor/gems/highline-1.4.0/lib")
|
Dependencies.load_paths.unshift(File.dirname(__FILE__) + "/../../vendor/gems/highline-1.5.0/lib")
|
||||||
require "highline/import"
|
require "highline/import"
|
||||||
|
|
||||||
user = User.find_by_login(ENV['USER'])
|
user = User.find_by_login(ENV['USER'])
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
|
raise "To avoid rake task loading problems: run 'rake clobber' in vendor/plugins/rspec" if File.directory?(File.join(File.dirname(__FILE__), *%w[.. .. vendor plugins rspec pkg]))
|
||||||
|
raise "To avoid rake task loading problems: run 'rake clobber' in vendor/plugins/rspec-rails" if File.directory?(File.join(File.dirname(__FILE__), *%w[.. .. vendor plugins rspec-rails pkg]))
|
||||||
|
|
||||||
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
||||||
# Check to see if the rspec plugin is installed first and require
|
# Check to see if the rspec plugin is installed first and require
|
||||||
# it if it is. If not, use the gem version.
|
# it if it is. If not, use the gem version.
|
||||||
rspec_base = File.expand_path(File.dirname(__FILE__) + '/../../rspec/lib')
|
rspec_base = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec/lib')
|
||||||
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
||||||
require 'spec/rake/spectask'
|
require 'spec/rake/spectask'
|
||||||
require 'spec/translator'
|
|
||||||
|
|
||||||
spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop
|
spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop
|
||||||
task :noop do
|
task :noop do
|
||||||
|
|
@ -64,13 +66,6 @@ namespace :spec do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Translate/upgrade specs using the built-in translator"
|
|
||||||
task :translate do
|
|
||||||
translator = ::Spec::Translator.new
|
|
||||||
dir = RAILS_ROOT + '/spec'
|
|
||||||
translator.translate(dir, dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Setup specs for stats
|
# Setup specs for stats
|
||||||
task :statsetup do
|
task :statsetup do
|
||||||
require 'code_statistics'
|
require 'code_statistics'
|
||||||
|
|
@ -39,4 +39,4 @@ RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
|
||||||
# Example:
|
# Example:
|
||||||
# ErrorDocument 500 /500.html
|
# ErrorDocument 500 /500.html
|
||||||
|
|
||||||
ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
|
ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
|
||||||
|
|
|
||||||
146
public/javascripts/controls.js
vendored
146
public/javascripts/controls.js
vendored
|
|
@ -1,22 +1,22 @@
|
||||||
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||||
// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
||||||
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
|
// (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
|
||||||
// Contributors:
|
// Contributors:
|
||||||
// Richard Livsey
|
// Richard Livsey
|
||||||
// Rahul Bhargava
|
// Rahul Bhargava
|
||||||
// Rob Wills
|
// Rob Wills
|
||||||
//
|
//
|
||||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||||
|
|
||||||
// Autocompleter.Base handles all the autocompletion functionality
|
// Autocompleter.Base handles all the autocompletion functionality
|
||||||
// that's independent of the data source for autocompletion. This
|
// that's independent of the data source for autocompletion. This
|
||||||
// includes drawing the autocompletion menu, observing keyboard
|
// includes drawing the autocompletion menu, observing keyboard
|
||||||
// and mouse events, and similar.
|
// and mouse events, and similar.
|
||||||
//
|
//
|
||||||
// Specific autocompleters need to provide, at the very least,
|
// Specific autocompleters need to provide, at the very least,
|
||||||
// a getUpdatedChoices function that will be invoked every time
|
// a getUpdatedChoices function that will be invoked every time
|
||||||
// the text inside the monitored textbox changes. This method
|
// the text inside the monitored textbox changes. This method
|
||||||
// should get the text for which to provide autocompletion by
|
// should get the text for which to provide autocompletion by
|
||||||
// invoking this.getToken(), NOT by directly accessing
|
// invoking this.getToken(), NOT by directly accessing
|
||||||
// this.element.value. This is to allow incremental tokenized
|
// this.element.value. This is to allow incremental tokenized
|
||||||
|
|
@ -30,23 +30,23 @@
|
||||||
// will incrementally autocomplete with a comma as the token.
|
// will incrementally autocomplete with a comma as the token.
|
||||||
// Additionally, ',' in the above example can be replaced with
|
// Additionally, ',' in the above example can be replaced with
|
||||||
// a token array, e.g. { tokens: [',', '\n'] } which
|
// a token array, e.g. { tokens: [',', '\n'] } which
|
||||||
// enables autocompletion on multiple tokens. This is most
|
// enables autocompletion on multiple tokens. This is most
|
||||||
// useful when one of the tokens is \n (a newline), as it
|
// useful when one of the tokens is \n (a newline), as it
|
||||||
// allows smart autocompletion after linebreaks.
|
// allows smart autocompletion after linebreaks.
|
||||||
|
|
||||||
if(typeof Effect == 'undefined')
|
if(typeof Effect == 'undefined')
|
||||||
throw("controls.js requires including script.aculo.us' effects.js library");
|
throw("controls.js requires including script.aculo.us' effects.js library");
|
||||||
|
|
||||||
var Autocompleter = { }
|
var Autocompleter = { };
|
||||||
Autocompleter.Base = Class.create({
|
Autocompleter.Base = Class.create({
|
||||||
baseInitialize: function(element, update, options) {
|
baseInitialize: function(element, update, options) {
|
||||||
element = $(element)
|
element = $(element);
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.update = $(update);
|
this.update = $(update);
|
||||||
this.hasFocus = false;
|
this.hasFocus = false;
|
||||||
this.changed = false;
|
this.changed = false;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.entryCount = 0;
|
this.entryCount = 0;
|
||||||
this.oldElementValue = this.element.value;
|
this.oldElementValue = this.element.value;
|
||||||
|
|
||||||
|
|
@ -59,28 +59,28 @@ Autocompleter.Base = Class.create({
|
||||||
this.options.tokens = this.options.tokens || [];
|
this.options.tokens = this.options.tokens || [];
|
||||||
this.options.frequency = this.options.frequency || 0.4;
|
this.options.frequency = this.options.frequency || 0.4;
|
||||||
this.options.minChars = this.options.minChars || 1;
|
this.options.minChars = this.options.minChars || 1;
|
||||||
this.options.onShow = this.options.onShow ||
|
this.options.onShow = this.options.onShow ||
|
||||||
function(element, update){
|
function(element, update){
|
||||||
if(!update.style.position || update.style.position=='absolute') {
|
if(!update.style.position || update.style.position=='absolute') {
|
||||||
update.style.position = 'absolute';
|
update.style.position = 'absolute';
|
||||||
Position.clone(element, update, {
|
Position.clone(element, update, {
|
||||||
setHeight: false,
|
setHeight: false,
|
||||||
offsetTop: element.offsetHeight
|
offsetTop: element.offsetHeight
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Effect.Appear(update,{duration:0.15});
|
Effect.Appear(update,{duration:0.15});
|
||||||
};
|
};
|
||||||
this.options.onHide = this.options.onHide ||
|
this.options.onHide = this.options.onHide ||
|
||||||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
|
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
|
||||||
|
|
||||||
if(typeof(this.options.tokens) == 'string')
|
if(typeof(this.options.tokens) == 'string')
|
||||||
this.options.tokens = new Array(this.options.tokens);
|
this.options.tokens = new Array(this.options.tokens);
|
||||||
// Force carriage returns as token delimiters anyway
|
// Force carriage returns as token delimiters anyway
|
||||||
if (!this.options.tokens.include('\n'))
|
if (!this.options.tokens.include('\n'))
|
||||||
this.options.tokens.push('\n');
|
this.options.tokens.push('\n');
|
||||||
|
|
||||||
this.observer = null;
|
this.observer = null;
|
||||||
|
|
||||||
this.element.setAttribute('autocomplete','off');
|
this.element.setAttribute('autocomplete','off');
|
||||||
|
|
||||||
Element.hide(this.update);
|
Element.hide(this.update);
|
||||||
|
|
@ -91,10 +91,10 @@ Autocompleter.Base = Class.create({
|
||||||
|
|
||||||
show: function() {
|
show: function() {
|
||||||
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
||||||
if(!this.iefix &&
|
if(!this.iefix &&
|
||||||
(Prototype.Browser.IE) &&
|
(Prototype.Browser.IE) &&
|
||||||
(Element.getStyle(this.update, 'position')=='absolute')) {
|
(Element.getStyle(this.update, 'position')=='absolute')) {
|
||||||
new Insertion.After(this.update,
|
new Insertion.After(this.update,
|
||||||
'<iframe id="' + this.update.id + '_iefix" '+
|
'<iframe id="' + this.update.id + '_iefix" '+
|
||||||
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
||||||
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
|
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
|
||||||
|
|
@ -102,7 +102,7 @@ Autocompleter.Base = Class.create({
|
||||||
}
|
}
|
||||||
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
|
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
|
||||||
},
|
},
|
||||||
|
|
||||||
fixIEOverlapping: function() {
|
fixIEOverlapping: function() {
|
||||||
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
|
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
|
||||||
this.iefix.style.zIndex = 1;
|
this.iefix.style.zIndex = 1;
|
||||||
|
|
@ -150,15 +150,15 @@ Autocompleter.Base = Class.create({
|
||||||
Event.stop(event);
|
Event.stop(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
|
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
|
||||||
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
|
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
|
||||||
|
|
||||||
this.changed = true;
|
this.changed = true;
|
||||||
this.hasFocus = true;
|
this.hasFocus = true;
|
||||||
|
|
||||||
if(this.observer) clearTimeout(this.observer);
|
if(this.observer) clearTimeout(this.observer);
|
||||||
this.observer =
|
this.observer =
|
||||||
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
|
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -170,35 +170,35 @@ Autocompleter.Base = Class.create({
|
||||||
|
|
||||||
onHover: function(event) {
|
onHover: function(event) {
|
||||||
var element = Event.findElement(event, 'LI');
|
var element = Event.findElement(event, 'LI');
|
||||||
if(this.index != element.autocompleteIndex)
|
if(this.index != element.autocompleteIndex)
|
||||||
{
|
{
|
||||||
this.index = element.autocompleteIndex;
|
this.index = element.autocompleteIndex;
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
Event.stop(event);
|
Event.stop(event);
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function(event) {
|
onClick: function(event) {
|
||||||
var element = Event.findElement(event, 'LI');
|
var element = Event.findElement(event, 'LI');
|
||||||
this.index = element.autocompleteIndex;
|
this.index = element.autocompleteIndex;
|
||||||
this.selectEntry();
|
this.selectEntry();
|
||||||
this.hide();
|
this.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
onBlur: function(event) {
|
onBlur: function(event) {
|
||||||
// needed to make click events working
|
// needed to make click events working
|
||||||
setTimeout(this.hide.bind(this), 250);
|
setTimeout(this.hide.bind(this), 250);
|
||||||
this.hasFocus = false;
|
this.hasFocus = false;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
if(this.entryCount > 0) {
|
if(this.entryCount > 0) {
|
||||||
for (var i = 0; i < this.entryCount; i++)
|
for (var i = 0; i < this.entryCount; i++)
|
||||||
this.index==i ?
|
this.index==i ?
|
||||||
Element.addClassName(this.getEntry(i),"selected") :
|
Element.addClassName(this.getEntry(i),"selected") :
|
||||||
Element.removeClassName(this.getEntry(i),"selected");
|
Element.removeClassName(this.getEntry(i),"selected");
|
||||||
if(this.hasFocus) {
|
if(this.hasFocus) {
|
||||||
this.show();
|
this.show();
|
||||||
this.active = true;
|
this.active = true;
|
||||||
}
|
}
|
||||||
|
|
@ -207,27 +207,27 @@ Autocompleter.Base = Class.create({
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
markPrevious: function() {
|
markPrevious: function() {
|
||||||
if(this.index > 0) this.index--
|
if(this.index > 0) this.index--;
|
||||||
else this.index = this.entryCount-1;
|
else this.index = this.entryCount-1;
|
||||||
this.getEntry(this.index).scrollIntoView(false);
|
this.getEntry(this.index).scrollIntoView(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
markNext: function() {
|
markNext: function() {
|
||||||
if(this.index < this.entryCount-1) this.index++
|
if(this.index < this.entryCount-1) this.index++;
|
||||||
else this.index = 0;
|
else this.index = 0;
|
||||||
this.getEntry(this.index).scrollIntoView(false);
|
this.getEntry(this.index).scrollIntoView(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
getEntry: function(index) {
|
getEntry: function(index) {
|
||||||
return this.update.firstChild.childNodes[index];
|
return this.update.firstChild.childNodes[index];
|
||||||
},
|
},
|
||||||
|
|
||||||
getCurrentEntry: function() {
|
getCurrentEntry: function() {
|
||||||
return this.getEntry(this.index);
|
return this.getEntry(this.index);
|
||||||
},
|
},
|
||||||
|
|
||||||
selectEntry: function() {
|
selectEntry: function() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.updateElement(this.getCurrentEntry());
|
this.updateElement(this.getCurrentEntry());
|
||||||
|
|
@ -244,7 +244,7 @@ Autocompleter.Base = Class.create({
|
||||||
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
|
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
|
||||||
} else
|
} else
|
||||||
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
|
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
|
||||||
|
|
||||||
var bounds = this.getTokenBounds();
|
var bounds = this.getTokenBounds();
|
||||||
if (bounds[0] != -1) {
|
if (bounds[0] != -1) {
|
||||||
var newValue = this.element.value.substr(0, bounds[0]);
|
var newValue = this.element.value.substr(0, bounds[0]);
|
||||||
|
|
@ -257,7 +257,7 @@ Autocompleter.Base = Class.create({
|
||||||
}
|
}
|
||||||
this.oldElementValue = this.element.value;
|
this.oldElementValue = this.element.value;
|
||||||
this.element.focus();
|
this.element.focus();
|
||||||
|
|
||||||
if (this.options.afterUpdateElement)
|
if (this.options.afterUpdateElement)
|
||||||
this.options.afterUpdateElement(this.element, selectedElement);
|
this.options.afterUpdateElement(this.element, selectedElement);
|
||||||
},
|
},
|
||||||
|
|
@ -269,20 +269,20 @@ Autocompleter.Base = Class.create({
|
||||||
Element.cleanWhitespace(this.update.down());
|
Element.cleanWhitespace(this.update.down());
|
||||||
|
|
||||||
if(this.update.firstChild && this.update.down().childNodes) {
|
if(this.update.firstChild && this.update.down().childNodes) {
|
||||||
this.entryCount =
|
this.entryCount =
|
||||||
this.update.down().childNodes.length;
|
this.update.down().childNodes.length;
|
||||||
for (var i = 0; i < this.entryCount; i++) {
|
for (var i = 0; i < this.entryCount; i++) {
|
||||||
var entry = this.getEntry(i);
|
var entry = this.getEntry(i);
|
||||||
entry.autocompleteIndex = i;
|
entry.autocompleteIndex = i;
|
||||||
this.addObservers(entry);
|
this.addObservers(entry);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.entryCount = 0;
|
this.entryCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stopIndicator();
|
this.stopIndicator();
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
|
|
||||||
if(this.entryCount==1 && this.options.autoSelect) {
|
if(this.entryCount==1 && this.options.autoSelect) {
|
||||||
this.selectEntry();
|
this.selectEntry();
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
@ -298,7 +298,7 @@ Autocompleter.Base = Class.create({
|
||||||
},
|
},
|
||||||
|
|
||||||
onObserverEvent: function() {
|
onObserverEvent: function() {
|
||||||
this.changed = false;
|
this.changed = false;
|
||||||
this.tokenBounds = null;
|
this.tokenBounds = null;
|
||||||
if(this.getToken().length>=this.options.minChars) {
|
if(this.getToken().length>=this.options.minChars) {
|
||||||
this.getUpdatedChoices();
|
this.getUpdatedChoices();
|
||||||
|
|
@ -351,16 +351,16 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
|
||||||
|
|
||||||
getUpdatedChoices: function() {
|
getUpdatedChoices: function() {
|
||||||
this.startIndicator();
|
this.startIndicator();
|
||||||
|
|
||||||
var entry = encodeURIComponent(this.options.paramName) + '=' +
|
var entry = encodeURIComponent(this.options.paramName) + '=' +
|
||||||
encodeURIComponent(this.getToken());
|
encodeURIComponent(this.getToken());
|
||||||
|
|
||||||
this.options.parameters = this.options.callback ?
|
this.options.parameters = this.options.callback ?
|
||||||
this.options.callback(this.element, entry) : entry;
|
this.options.callback(this.element, entry) : entry;
|
||||||
|
|
||||||
if(this.options.defaultParams)
|
if(this.options.defaultParams)
|
||||||
this.options.parameters += '&' + this.options.defaultParams;
|
this.options.parameters += '&' + this.options.defaultParams;
|
||||||
|
|
||||||
new Ajax.Request(this.url, this.options);
|
new Ajax.Request(this.url, this.options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -382,7 +382,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
|
||||||
// - choices - How many autocompletion choices to offer
|
// - choices - How many autocompletion choices to offer
|
||||||
//
|
//
|
||||||
// - partialSearch - If false, the autocompleter will match entered
|
// - partialSearch - If false, the autocompleter will match entered
|
||||||
// text only at the beginning of strings in the
|
// text only at the beginning of strings in the
|
||||||
// autocomplete array. Defaults to true, which will
|
// autocomplete array. Defaults to true, which will
|
||||||
// match text at the beginning of any *word* in the
|
// match text at the beginning of any *word* in the
|
||||||
// strings in the autocomplete array. If you want to
|
// strings in the autocomplete array. If you want to
|
||||||
|
|
@ -399,7 +399,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
|
||||||
// - ignoreCase - Whether to ignore case when autocompleting.
|
// - ignoreCase - Whether to ignore case when autocompleting.
|
||||||
// Defaults to true.
|
// Defaults to true.
|
||||||
//
|
//
|
||||||
// It's possible to pass in a custom function as the 'selector'
|
// It's possible to pass in a custom function as the 'selector'
|
||||||
// option, if you prefer to write your own autocompletion logic.
|
// option, if you prefer to write your own autocompletion logic.
|
||||||
// In that case, the other options above will not apply unless
|
// In that case, the other options above will not apply unless
|
||||||
// you support them.
|
// you support them.
|
||||||
|
|
@ -427,20 +427,20 @@ Autocompleter.Local = Class.create(Autocompleter.Base, {
|
||||||
var entry = instance.getToken();
|
var entry = instance.getToken();
|
||||||
var count = 0;
|
var count = 0;
|
||||||
|
|
||||||
for (var i = 0; i < instance.options.array.length &&
|
for (var i = 0; i < instance.options.array.length &&
|
||||||
ret.length < instance.options.choices ; i++) {
|
ret.length < instance.options.choices ; i++) {
|
||||||
|
|
||||||
var elem = instance.options.array[i];
|
var elem = instance.options.array[i];
|
||||||
var foundPos = instance.options.ignoreCase ?
|
var foundPos = instance.options.ignoreCase ?
|
||||||
elem.toLowerCase().indexOf(entry.toLowerCase()) :
|
elem.toLowerCase().indexOf(entry.toLowerCase()) :
|
||||||
elem.indexOf(entry);
|
elem.indexOf(entry);
|
||||||
|
|
||||||
while (foundPos != -1) {
|
while (foundPos != -1) {
|
||||||
if (foundPos == 0 && elem.length != entry.length) {
|
if (foundPos == 0 && elem.length != entry.length) {
|
||||||
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
|
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
|
||||||
elem.substr(entry.length) + "</li>");
|
elem.substr(entry.length) + "</li>");
|
||||||
break;
|
break;
|
||||||
} else if (entry.length >= instance.options.partialChars &&
|
} else if (entry.length >= instance.options.partialChars &&
|
||||||
instance.options.partialSearch && foundPos != -1) {
|
instance.options.partialSearch && foundPos != -1) {
|
||||||
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
|
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
|
||||||
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
|
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
|
||||||
|
|
@ -450,14 +450,14 @@ Autocompleter.Local = Class.create(Autocompleter.Base, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foundPos = instance.options.ignoreCase ?
|
foundPos = instance.options.ignoreCase ?
|
||||||
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
|
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
|
||||||
elem.indexOf(entry, foundPos + 1);
|
elem.indexOf(entry, foundPos + 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (partial.length)
|
if (partial.length)
|
||||||
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
|
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
|
||||||
return "<ul>" + ret.join('') + "</ul>";
|
return "<ul>" + ret.join('') + "</ul>";
|
||||||
}
|
}
|
||||||
}, options || { });
|
}, options || { });
|
||||||
|
|
@ -474,7 +474,7 @@ Field.scrollFreeActivate = function(field) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
Field.activate(field);
|
Field.activate(field);
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
};
|
||||||
|
|
||||||
Ajax.InPlaceEditor = Class.create({
|
Ajax.InPlaceEditor = Class.create({
|
||||||
initialize: function(element, url, options) {
|
initialize: function(element, url, options) {
|
||||||
|
|
@ -604,7 +604,7 @@ Ajax.InPlaceEditor = Class.create({
|
||||||
this.triggerCallback('onEnterHover');
|
this.triggerCallback('onEnterHover');
|
||||||
},
|
},
|
||||||
getText: function() {
|
getText: function() {
|
||||||
return this.element.innerHTML;
|
return this.element.innerHTML.unescapeHTML();
|
||||||
},
|
},
|
||||||
handleAJAXFailure: function(transport) {
|
handleAJAXFailure: function(transport) {
|
||||||
this.triggerCallback('onFailure', transport);
|
this.triggerCallback('onFailure', transport);
|
||||||
|
|
@ -780,7 +780,7 @@ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
|
||||||
onSuccess: function(transport) {
|
onSuccess: function(transport) {
|
||||||
var js = transport.responseText.strip();
|
var js = transport.responseText.strip();
|
||||||
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
|
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
|
||||||
throw 'Server returned an invalid collection representation.';
|
throw('Server returned an invalid collection representation.');
|
||||||
this._collection = eval(js);
|
this._collection = eval(js);
|
||||||
this.checkForExternalText();
|
this.checkForExternalText();
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
|
|
@ -937,7 +937,7 @@ Ajax.InPlaceCollectionEditor.DefaultOptions = {
|
||||||
loadingCollectionText: 'Loading options...'
|
loadingCollectionText: 'Loading options...'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Delayed observer, like Form.Element.Observer,
|
// Delayed observer, like Form.Element.Observer,
|
||||||
// but waits for delay after last key input
|
// but waits for delay after last key input
|
||||||
// Ideal for live-search fields
|
// Ideal for live-search fields
|
||||||
|
|
||||||
|
|
@ -947,7 +947,7 @@ Form.Element.DelayedObserver = Class.create({
|
||||||
this.element = $(element);
|
this.element = $(element);
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
this.lastValue = $F(this.element);
|
this.lastValue = $F(this.element);
|
||||||
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
||||||
},
|
},
|
||||||
delayedListener: function(event) {
|
delayedListener: function(event) {
|
||||||
|
|
@ -960,4 +960,4 @@ Form.Element.DelayedObserver = Class.create({
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
this.callback(this.element, $F(this.element));
|
this.callback(this.element, $F(this.element));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
329
public/javascripts/dragdrop.js
vendored
329
public/javascripts/dragdrop.js
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||||
// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
|
// (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
|
||||||
//
|
//
|
||||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ var Droppables = {
|
||||||
options._containers.push($(containment));
|
options._containers.push($(containment));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.accept) options.accept = [options.accept].flatten();
|
if(options.accept) options.accept = [options.accept].flatten();
|
||||||
|
|
||||||
Element.makePositioned(element); // fix IE
|
Element.makePositioned(element); // fix IE
|
||||||
|
|
@ -40,34 +40,34 @@ var Droppables = {
|
||||||
|
|
||||||
this.drops.push(options);
|
this.drops.push(options);
|
||||||
},
|
},
|
||||||
|
|
||||||
findDeepestChild: function(drops) {
|
findDeepestChild: function(drops) {
|
||||||
deepest = drops[0];
|
deepest = drops[0];
|
||||||
|
|
||||||
for (i = 1; i < drops.length; ++i)
|
for (i = 1; i < drops.length; ++i)
|
||||||
if (Element.isParent(drops[i].element, deepest.element))
|
if (Element.isParent(drops[i].element, deepest.element))
|
||||||
deepest = drops[i];
|
deepest = drops[i];
|
||||||
|
|
||||||
return deepest;
|
return deepest;
|
||||||
},
|
},
|
||||||
|
|
||||||
isContained: function(element, drop) {
|
isContained: function(element, drop) {
|
||||||
var containmentNode;
|
var containmentNode;
|
||||||
if(drop.tree) {
|
if(drop.tree) {
|
||||||
containmentNode = element.treeNode;
|
containmentNode = element.treeNode;
|
||||||
} else {
|
} else {
|
||||||
containmentNode = element.parentNode;
|
containmentNode = element.parentNode;
|
||||||
}
|
}
|
||||||
return drop._containers.detect(function(c) { return containmentNode == c });
|
return drop._containers.detect(function(c) { return containmentNode == c });
|
||||||
},
|
},
|
||||||
|
|
||||||
isAffected: function(point, element, drop) {
|
isAffected: function(point, element, drop) {
|
||||||
return (
|
return (
|
||||||
(drop.element!=element) &&
|
(drop.element!=element) &&
|
||||||
((!drop._containers) ||
|
((!drop._containers) ||
|
||||||
this.isContained(element, drop)) &&
|
this.isContained(element, drop)) &&
|
||||||
((!drop.accept) ||
|
((!drop.accept) ||
|
||||||
(Element.classNames(element).detect(
|
(Element.classNames(element).detect(
|
||||||
function(v) { return drop.accept.include(v) } ) )) &&
|
function(v) { return drop.accept.include(v) } ) )) &&
|
||||||
Position.within(drop.element, point[0], point[1]) );
|
Position.within(drop.element, point[0], point[1]) );
|
||||||
},
|
},
|
||||||
|
|
@ -87,12 +87,12 @@ var Droppables = {
|
||||||
show: function(point, element) {
|
show: function(point, element) {
|
||||||
if(!this.drops.length) return;
|
if(!this.drops.length) return;
|
||||||
var drop, affected = [];
|
var drop, affected = [];
|
||||||
|
|
||||||
this.drops.each( function(drop) {
|
this.drops.each( function(drop) {
|
||||||
if(Droppables.isAffected(point, element, drop))
|
if(Droppables.isAffected(point, element, drop))
|
||||||
affected.push(drop);
|
affected.push(drop);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(affected.length>0)
|
if(affected.length>0)
|
||||||
drop = Droppables.findDeepestChild(affected);
|
drop = Droppables.findDeepestChild(affected);
|
||||||
|
|
||||||
|
|
@ -101,7 +101,7 @@ var Droppables = {
|
||||||
Position.within(drop.element, point[0], point[1]);
|
Position.within(drop.element, point[0], point[1]);
|
||||||
if(drop.onHover)
|
if(drop.onHover)
|
||||||
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
||||||
|
|
||||||
if (drop != this.last_active) Droppables.activate(drop);
|
if (drop != this.last_active) Droppables.activate(drop);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -112,8 +112,8 @@ var Droppables = {
|
||||||
|
|
||||||
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
|
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
|
||||||
if (this.last_active.onDrop) {
|
if (this.last_active.onDrop) {
|
||||||
this.last_active.onDrop(element, this.last_active.element, event);
|
this.last_active.onDrop(element, this.last_active.element, event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -121,25 +121,25 @@ var Droppables = {
|
||||||
if(this.last_active)
|
if(this.last_active)
|
||||||
this.deactivate(this.last_active);
|
this.deactivate(this.last_active);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
var Draggables = {
|
var Draggables = {
|
||||||
drags: [],
|
drags: [],
|
||||||
observers: [],
|
observers: [],
|
||||||
|
|
||||||
register: function(draggable) {
|
register: function(draggable) {
|
||||||
if(this.drags.length == 0) {
|
if(this.drags.length == 0) {
|
||||||
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
||||||
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
|
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
|
||||||
this.eventKeypress = this.keyPress.bindAsEventListener(this);
|
this.eventKeypress = this.keyPress.bindAsEventListener(this);
|
||||||
|
|
||||||
Event.observe(document, "mouseup", this.eventMouseUp);
|
Event.observe(document, "mouseup", this.eventMouseUp);
|
||||||
Event.observe(document, "mousemove", this.eventMouseMove);
|
Event.observe(document, "mousemove", this.eventMouseMove);
|
||||||
Event.observe(document, "keypress", this.eventKeypress);
|
Event.observe(document, "keypress", this.eventKeypress);
|
||||||
}
|
}
|
||||||
this.drags.push(draggable);
|
this.drags.push(draggable);
|
||||||
},
|
},
|
||||||
|
|
||||||
unregister: function(draggable) {
|
unregister: function(draggable) {
|
||||||
this.drags = this.drags.reject(function(d) { return d==draggable });
|
this.drags = this.drags.reject(function(d) { return d==draggable });
|
||||||
if(this.drags.length == 0) {
|
if(this.drags.length == 0) {
|
||||||
|
|
@ -148,24 +148,24 @@ var Draggables = {
|
||||||
Event.stopObserving(document, "keypress", this.eventKeypress);
|
Event.stopObserving(document, "keypress", this.eventKeypress);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
activate: function(draggable) {
|
activate: function(draggable) {
|
||||||
if(draggable.options.delay) {
|
if(draggable.options.delay) {
|
||||||
this._timeout = setTimeout(function() {
|
this._timeout = setTimeout(function() {
|
||||||
Draggables._timeout = null;
|
Draggables._timeout = null;
|
||||||
window.focus();
|
window.focus();
|
||||||
Draggables.activeDraggable = draggable;
|
Draggables.activeDraggable = draggable;
|
||||||
}.bind(this), draggable.options.delay);
|
}.bind(this), draggable.options.delay);
|
||||||
} else {
|
} else {
|
||||||
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
|
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
|
||||||
this.activeDraggable = draggable;
|
this.activeDraggable = draggable;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate: function() {
|
deactivate: function() {
|
||||||
this.activeDraggable = null;
|
this.activeDraggable = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateDrag: function(event) {
|
updateDrag: function(event) {
|
||||||
if(!this.activeDraggable) return;
|
if(!this.activeDraggable) return;
|
||||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||||
|
|
@ -173,36 +173,36 @@ var Draggables = {
|
||||||
// the same coordinates, prevent needless redrawing (moz bug?)
|
// the same coordinates, prevent needless redrawing (moz bug?)
|
||||||
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
|
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
|
||||||
this._lastPointer = pointer;
|
this._lastPointer = pointer;
|
||||||
|
|
||||||
this.activeDraggable.updateDrag(event, pointer);
|
this.activeDraggable.updateDrag(event, pointer);
|
||||||
},
|
},
|
||||||
|
|
||||||
endDrag: function(event) {
|
endDrag: function(event) {
|
||||||
if(this._timeout) {
|
if(this._timeout) {
|
||||||
clearTimeout(this._timeout);
|
clearTimeout(this._timeout);
|
||||||
this._timeout = null;
|
this._timeout = null;
|
||||||
}
|
}
|
||||||
if(!this.activeDraggable) return;
|
if(!this.activeDraggable) return;
|
||||||
this._lastPointer = null;
|
this._lastPointer = null;
|
||||||
this.activeDraggable.endDrag(event);
|
this.activeDraggable.endDrag(event);
|
||||||
this.activeDraggable = null;
|
this.activeDraggable = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
keyPress: function(event) {
|
keyPress: function(event) {
|
||||||
if(this.activeDraggable)
|
if(this.activeDraggable)
|
||||||
this.activeDraggable.keyPress(event);
|
this.activeDraggable.keyPress(event);
|
||||||
},
|
},
|
||||||
|
|
||||||
addObserver: function(observer) {
|
addObserver: function(observer) {
|
||||||
this.observers.push(observer);
|
this.observers.push(observer);
|
||||||
this._cacheObserverCallbacks();
|
this._cacheObserverCallbacks();
|
||||||
},
|
},
|
||||||
|
|
||||||
removeObserver: function(element) { // element instead of observer fixes mem leaks
|
removeObserver: function(element) { // element instead of observer fixes mem leaks
|
||||||
this.observers = this.observers.reject( function(o) { return o.element==element });
|
this.observers = this.observers.reject( function(o) { return o.element==element });
|
||||||
this._cacheObserverCallbacks();
|
this._cacheObserverCallbacks();
|
||||||
},
|
},
|
||||||
|
|
||||||
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
|
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
|
||||||
if(this[eventName+'Count'] > 0)
|
if(this[eventName+'Count'] > 0)
|
||||||
this.observers.each( function(o) {
|
this.observers.each( function(o) {
|
||||||
|
|
@ -210,7 +210,7 @@ var Draggables = {
|
||||||
});
|
});
|
||||||
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
|
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
|
||||||
},
|
},
|
||||||
|
|
||||||
_cacheObserverCallbacks: function() {
|
_cacheObserverCallbacks: function() {
|
||||||
['onStart','onEnd','onDrag'].each( function(eventName) {
|
['onStart','onEnd','onDrag'].each( function(eventName) {
|
||||||
Draggables[eventName+'Count'] = Draggables.observers.select(
|
Draggables[eventName+'Count'] = Draggables.observers.select(
|
||||||
|
|
@ -218,7 +218,7 @@ var Draggables = {
|
||||||
).length;
|
).length;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
@ -234,12 +234,12 @@ var Draggable = Class.create({
|
||||||
},
|
},
|
||||||
endeffect: function(element) {
|
endeffect: function(element) {
|
||||||
var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
|
var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
|
||||||
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
|
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
|
||||||
queue: {scope:'_draggable', position:'end'},
|
queue: {scope:'_draggable', position:'end'},
|
||||||
afterFinish: function(){
|
afterFinish: function(){
|
||||||
Draggable._dragging[element] = false
|
Draggable._dragging[element] = false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
zindex: 1000,
|
zindex: 1000,
|
||||||
revert: false,
|
revert: false,
|
||||||
|
|
@ -250,57 +250,57 @@ var Draggable = Class.create({
|
||||||
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
|
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
|
||||||
delay: 0
|
delay: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
|
if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
|
||||||
Object.extend(defaults, {
|
Object.extend(defaults, {
|
||||||
starteffect: function(element) {
|
starteffect: function(element) {
|
||||||
element._opacity = Element.getOpacity(element);
|
element._opacity = Element.getOpacity(element);
|
||||||
Draggable._dragging[element] = true;
|
Draggable._dragging[element] = true;
|
||||||
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
|
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var options = Object.extend(defaults, arguments[1] || { });
|
var options = Object.extend(defaults, arguments[1] || { });
|
||||||
|
|
||||||
this.element = $(element);
|
this.element = $(element);
|
||||||
|
|
||||||
if(options.handle && Object.isString(options.handle))
|
if(options.handle && Object.isString(options.handle))
|
||||||
this.handle = this.element.down('.'+options.handle, 0);
|
this.handle = this.element.down('.'+options.handle, 0);
|
||||||
|
|
||||||
if(!this.handle) this.handle = $(options.handle);
|
if(!this.handle) this.handle = $(options.handle);
|
||||||
if(!this.handle) this.handle = this.element;
|
if(!this.handle) this.handle = this.element;
|
||||||
|
|
||||||
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
|
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
|
||||||
options.scroll = $(options.scroll);
|
options.scroll = $(options.scroll);
|
||||||
this._isScrollChild = Element.childOf(this.element, options.scroll);
|
this._isScrollChild = Element.childOf(this.element, options.scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
Element.makePositioned(this.element); // fix IE
|
Element.makePositioned(this.element); // fix IE
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
|
|
||||||
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
|
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
|
||||||
Event.observe(this.handle, "mousedown", this.eventMouseDown);
|
Event.observe(this.handle, "mousedown", this.eventMouseDown);
|
||||||
|
|
||||||
Draggables.register(this);
|
Draggables.register(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
|
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
|
||||||
Draggables.unregister(this);
|
Draggables.unregister(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
currentDelta: function() {
|
currentDelta: function() {
|
||||||
return([
|
return([
|
||||||
parseInt(Element.getStyle(this.element,'left') || '0'),
|
parseInt(Element.getStyle(this.element,'left') || '0'),
|
||||||
parseInt(Element.getStyle(this.element,'top') || '0')]);
|
parseInt(Element.getStyle(this.element,'top') || '0')]);
|
||||||
},
|
},
|
||||||
|
|
||||||
initDrag: function(event) {
|
initDrag: function(event) {
|
||||||
if(!Object.isUndefined(Draggable._dragging[this.element]) &&
|
if(!Object.isUndefined(Draggable._dragging[this.element]) &&
|
||||||
Draggable._dragging[this.element]) return;
|
Draggable._dragging[this.element]) return;
|
||||||
if(Event.isLeftClick(event)) {
|
if(Event.isLeftClick(event)) {
|
||||||
// abort on form elements, fixes a Firefox issue
|
// abort on form elements, fixes a Firefox issue
|
||||||
var src = Event.element(event);
|
var src = Event.element(event);
|
||||||
if((tag_name = src.tagName.toUpperCase()) && (
|
if((tag_name = src.tagName.toUpperCase()) && (
|
||||||
|
|
@ -309,34 +309,34 @@ var Draggable = Class.create({
|
||||||
tag_name=='OPTION' ||
|
tag_name=='OPTION' ||
|
||||||
tag_name=='BUTTON' ||
|
tag_name=='BUTTON' ||
|
||||||
tag_name=='TEXTAREA')) return;
|
tag_name=='TEXTAREA')) return;
|
||||||
|
|
||||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||||
var pos = Position.cumulativeOffset(this.element);
|
var pos = Position.cumulativeOffset(this.element);
|
||||||
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
|
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
|
||||||
|
|
||||||
Draggables.activate(this);
|
Draggables.activate(this);
|
||||||
Event.stop(event);
|
Event.stop(event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
startDrag: function(event) {
|
startDrag: function(event) {
|
||||||
this.dragging = true;
|
this.dragging = true;
|
||||||
if(!this.delta)
|
if(!this.delta)
|
||||||
this.delta = this.currentDelta();
|
this.delta = this.currentDelta();
|
||||||
|
|
||||||
if(this.options.zindex) {
|
if(this.options.zindex) {
|
||||||
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
||||||
this.element.style.zIndex = this.options.zindex;
|
this.element.style.zIndex = this.options.zindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.options.ghosting) {
|
if(this.options.ghosting) {
|
||||||
this._clone = this.element.cloneNode(true);
|
this._clone = this.element.cloneNode(true);
|
||||||
this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
|
this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
|
||||||
if (!this.element._originallyAbsolute)
|
if (!this._originallyAbsolute)
|
||||||
Position.absolutize(this.element);
|
Position.absolutize(this.element);
|
||||||
this.element.parentNode.insertBefore(this._clone, this.element);
|
this.element.parentNode.insertBefore(this._clone, this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.options.scroll) {
|
if(this.options.scroll) {
|
||||||
if (this.options.scroll == window) {
|
if (this.options.scroll == window) {
|
||||||
var where = this._getWindowScroll(this.options.scroll);
|
var where = this._getWindowScroll(this.options.scroll);
|
||||||
|
|
@ -347,28 +347,28 @@ var Draggable = Class.create({
|
||||||
this.originalScrollTop = this.options.scroll.scrollTop;
|
this.originalScrollTop = this.options.scroll.scrollTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Draggables.notify('onStart', this, event);
|
Draggables.notify('onStart', this, event);
|
||||||
|
|
||||||
if(this.options.starteffect) this.options.starteffect(this.element);
|
if(this.options.starteffect) this.options.starteffect(this.element);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateDrag: function(event, pointer) {
|
updateDrag: function(event, pointer) {
|
||||||
if(!this.dragging) this.startDrag(event);
|
if(!this.dragging) this.startDrag(event);
|
||||||
|
|
||||||
if(!this.options.quiet){
|
if(!this.options.quiet){
|
||||||
Position.prepare();
|
Position.prepare();
|
||||||
Droppables.show(pointer, this.element);
|
Droppables.show(pointer, this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
Draggables.notify('onDrag', this, event);
|
Draggables.notify('onDrag', this, event);
|
||||||
|
|
||||||
this.draw(pointer);
|
this.draw(pointer);
|
||||||
if(this.options.change) this.options.change(this);
|
if(this.options.change) this.options.change(this);
|
||||||
|
|
||||||
if(this.options.scroll) {
|
if(this.options.scroll) {
|
||||||
this.stopScrolling();
|
this.stopScrolling();
|
||||||
|
|
||||||
var p;
|
var p;
|
||||||
if (this.options.scroll == window) {
|
if (this.options.scroll == window) {
|
||||||
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
|
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
|
||||||
|
|
@ -386,16 +386,16 @@ var Draggable = Class.create({
|
||||||
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
|
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
|
||||||
this.startScrolling(speed);
|
this.startScrolling(speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix AppleWebKit rendering
|
// fix AppleWebKit rendering
|
||||||
if(Prototype.Browser.WebKit) window.scrollBy(0,0);
|
if(Prototype.Browser.WebKit) window.scrollBy(0,0);
|
||||||
|
|
||||||
Event.stop(event);
|
Event.stop(event);
|
||||||
},
|
},
|
||||||
|
|
||||||
finishDrag: function(event, success) {
|
finishDrag: function(event, success) {
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
|
|
||||||
if(this.options.quiet){
|
if(this.options.quiet){
|
||||||
Position.prepare();
|
Position.prepare();
|
||||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||||
|
|
@ -403,24 +403,24 @@ var Draggable = Class.create({
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.options.ghosting) {
|
if(this.options.ghosting) {
|
||||||
if (!this.element._originallyAbsolute)
|
if (!this._originallyAbsolute)
|
||||||
Position.relativize(this.element);
|
Position.relativize(this.element);
|
||||||
delete this.element._originallyAbsolute;
|
delete this._originallyAbsolute;
|
||||||
Element.remove(this._clone);
|
Element.remove(this._clone);
|
||||||
this._clone = null;
|
this._clone = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dropped = false;
|
var dropped = false;
|
||||||
if(success) {
|
if(success) {
|
||||||
dropped = Droppables.fire(event, this.element);
|
dropped = Droppables.fire(event, this.element);
|
||||||
if (!dropped) dropped = false;
|
if (!dropped) dropped = false;
|
||||||
}
|
}
|
||||||
if(dropped && this.options.onDropped) this.options.onDropped(this.element);
|
if(dropped && this.options.onDropped) this.options.onDropped(this.element);
|
||||||
Draggables.notify('onEnd', this, event);
|
Draggables.notify('onEnd', this, event);
|
||||||
|
|
||||||
var revert = this.options.revert;
|
var revert = this.options.revert;
|
||||||
if(revert && Object.isFunction(revert)) revert = revert(this.element);
|
if(revert && Object.isFunction(revert)) revert = revert(this.element);
|
||||||
|
|
||||||
var d = this.currentDelta();
|
var d = this.currentDelta();
|
||||||
if(revert && this.options.reverteffect) {
|
if(revert && this.options.reverteffect) {
|
||||||
if (dropped == 0 || revert != 'failure')
|
if (dropped == 0 || revert != 'failure')
|
||||||
|
|
@ -433,67 +433,67 @@ var Draggable = Class.create({
|
||||||
if(this.options.zindex)
|
if(this.options.zindex)
|
||||||
this.element.style.zIndex = this.originalZ;
|
this.element.style.zIndex = this.originalZ;
|
||||||
|
|
||||||
if(this.options.endeffect)
|
if(this.options.endeffect)
|
||||||
this.options.endeffect(this.element);
|
this.options.endeffect(this.element);
|
||||||
|
|
||||||
Draggables.deactivate(this);
|
Draggables.deactivate(this);
|
||||||
Droppables.reset();
|
Droppables.reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
keyPress: function(event) {
|
keyPress: function(event) {
|
||||||
if(event.keyCode!=Event.KEY_ESC) return;
|
if(event.keyCode!=Event.KEY_ESC) return;
|
||||||
this.finishDrag(event, false);
|
this.finishDrag(event, false);
|
||||||
Event.stop(event);
|
Event.stop(event);
|
||||||
},
|
},
|
||||||
|
|
||||||
endDrag: function(event) {
|
endDrag: function(event) {
|
||||||
if(!this.dragging) return;
|
if(!this.dragging) return;
|
||||||
this.stopScrolling();
|
this.stopScrolling();
|
||||||
this.finishDrag(event, true);
|
this.finishDrag(event, true);
|
||||||
Event.stop(event);
|
Event.stop(event);
|
||||||
},
|
},
|
||||||
|
|
||||||
draw: function(point) {
|
draw: function(point) {
|
||||||
var pos = Position.cumulativeOffset(this.element);
|
var pos = Position.cumulativeOffset(this.element);
|
||||||
if(this.options.ghosting) {
|
if(this.options.ghosting) {
|
||||||
var r = Position.realOffset(this.element);
|
var r = Position.realOffset(this.element);
|
||||||
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
|
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
|
||||||
}
|
}
|
||||||
|
|
||||||
var d = this.currentDelta();
|
var d = this.currentDelta();
|
||||||
pos[0] -= d[0]; pos[1] -= d[1];
|
pos[0] -= d[0]; pos[1] -= d[1];
|
||||||
|
|
||||||
if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
|
if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
|
||||||
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
|
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
|
||||||
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
|
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
var p = [0,1].map(function(i){
|
var p = [0,1].map(function(i){
|
||||||
return (point[i]-pos[i]-this.offset[i])
|
return (point[i]-pos[i]-this.offset[i])
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
if(this.options.snap) {
|
if(this.options.snap) {
|
||||||
if(Object.isFunction(this.options.snap)) {
|
if(Object.isFunction(this.options.snap)) {
|
||||||
p = this.options.snap(p[0],p[1],this);
|
p = this.options.snap(p[0],p[1],this);
|
||||||
} else {
|
} else {
|
||||||
if(Object.isArray(this.options.snap)) {
|
if(Object.isArray(this.options.snap)) {
|
||||||
p = p.map( function(v, i) {
|
p = p.map( function(v, i) {
|
||||||
return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
|
return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
|
||||||
} else {
|
} else {
|
||||||
p = p.map( function(v) {
|
p = p.map( function(v) {
|
||||||
return (v/this.options.snap).round()*this.options.snap }.bind(this))
|
return (v/this.options.snap).round()*this.options.snap }.bind(this));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
var style = this.element.style;
|
var style = this.element.style;
|
||||||
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
|
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
|
||||||
style.left = p[0] + "px";
|
style.left = p[0] + "px";
|
||||||
if((!this.options.constraint) || (this.options.constraint=='vertical'))
|
if((!this.options.constraint) || (this.options.constraint=='vertical'))
|
||||||
style.top = p[1] + "px";
|
style.top = p[1] + "px";
|
||||||
|
|
||||||
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
|
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
|
||||||
},
|
},
|
||||||
|
|
||||||
stopScrolling: function() {
|
stopScrolling: function() {
|
||||||
if(this.scrollInterval) {
|
if(this.scrollInterval) {
|
||||||
clearInterval(this.scrollInterval);
|
clearInterval(this.scrollInterval);
|
||||||
|
|
@ -501,14 +501,14 @@ var Draggable = Class.create({
|
||||||
Draggables._lastScrollPointer = null;
|
Draggables._lastScrollPointer = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
startScrolling: function(speed) {
|
startScrolling: function(speed) {
|
||||||
if(!(speed[0] || speed[1])) return;
|
if(!(speed[0] || speed[1])) return;
|
||||||
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
|
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
|
||||||
this.lastScrolled = new Date();
|
this.lastScrolled = new Date();
|
||||||
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
|
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
|
||||||
},
|
},
|
||||||
|
|
||||||
scroll: function() {
|
scroll: function() {
|
||||||
var current = new Date();
|
var current = new Date();
|
||||||
var delta = current - this.lastScrolled;
|
var delta = current - this.lastScrolled;
|
||||||
|
|
@ -524,7 +524,7 @@ var Draggable = Class.create({
|
||||||
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
|
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
|
||||||
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
|
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Position.prepare();
|
Position.prepare();
|
||||||
Droppables.show(Draggables._lastPointer, this.element);
|
Droppables.show(Draggables._lastPointer, this.element);
|
||||||
Draggables.notify('onDrag', this);
|
Draggables.notify('onDrag', this);
|
||||||
|
|
@ -538,10 +538,10 @@ var Draggable = Class.create({
|
||||||
Draggables._lastScrollPointer[1] = 0;
|
Draggables._lastScrollPointer[1] = 0;
|
||||||
this.draw(Draggables._lastScrollPointer);
|
this.draw(Draggables._lastScrollPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.options.change) this.options.change(this);
|
if(this.options.change) this.options.change(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getWindowScroll: function(w) {
|
_getWindowScroll: function(w) {
|
||||||
var T, L, W, H;
|
var T, L, W, H;
|
||||||
with (w.document) {
|
with (w.document) {
|
||||||
|
|
@ -560,7 +560,7 @@ var Draggable = Class.create({
|
||||||
H = documentElement.clientHeight;
|
H = documentElement.clientHeight;
|
||||||
} else {
|
} else {
|
||||||
W = body.offsetWidth;
|
W = body.offsetWidth;
|
||||||
H = body.offsetHeight
|
H = body.offsetHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { top: T, left: L, width: W, height: H };
|
return { top: T, left: L, width: W, height: H };
|
||||||
|
|
@ -577,11 +577,11 @@ var SortableObserver = Class.create({
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
this.lastValue = Sortable.serialize(this.element);
|
this.lastValue = Sortable.serialize(this.element);
|
||||||
},
|
},
|
||||||
|
|
||||||
onStart: function() {
|
onStart: function() {
|
||||||
this.lastValue = Sortable.serialize(this.element);
|
this.lastValue = Sortable.serialize(this.element);
|
||||||
},
|
},
|
||||||
|
|
||||||
onEnd: function() {
|
onEnd: function() {
|
||||||
Sortable.unmark();
|
Sortable.unmark();
|
||||||
if(this.lastValue != Sortable.serialize(this.element))
|
if(this.lastValue != Sortable.serialize(this.element))
|
||||||
|
|
@ -591,11 +591,11 @@ var SortableObserver = Class.create({
|
||||||
|
|
||||||
var Sortable = {
|
var Sortable = {
|
||||||
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
|
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
|
||||||
|
|
||||||
sortables: { },
|
sortables: { },
|
||||||
|
|
||||||
_findRootElement: function(element) {
|
_findRootElement: function(element) {
|
||||||
while (element.tagName.toUpperCase() != "BODY") {
|
while (element.tagName.toUpperCase() != "BODY") {
|
||||||
if(element.id && Sortable.sortables[element.id]) return element;
|
if(element.id && Sortable.sortables[element.id]) return element;
|
||||||
element = element.parentNode;
|
element = element.parentNode;
|
||||||
}
|
}
|
||||||
|
|
@ -606,22 +606,23 @@ var Sortable = {
|
||||||
if(!element) return;
|
if(!element) return;
|
||||||
return Sortable.sortables[element.id];
|
return Sortable.sortables[element.id];
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function(element){
|
destroy: function(element){
|
||||||
var s = Sortable.options(element);
|
element = $(element);
|
||||||
|
var s = Sortable.sortables[element.id];
|
||||||
|
|
||||||
if(s) {
|
if(s) {
|
||||||
Draggables.removeObserver(s.element);
|
Draggables.removeObserver(s.element);
|
||||||
s.droppables.each(function(d){ Droppables.remove(d) });
|
s.droppables.each(function(d){ Droppables.remove(d) });
|
||||||
s.draggables.invoke('destroy');
|
s.draggables.invoke('destroy');
|
||||||
|
|
||||||
delete Sortable.sortables[s.element.id];
|
delete Sortable.sortables[s.element.id];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
create: function(element) {
|
create: function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var options = Object.extend({
|
var options = Object.extend({
|
||||||
element: element,
|
element: element,
|
||||||
tag: 'li', // assumes li children, override with tag: 'tagname'
|
tag: 'li', // assumes li children, override with tag: 'tagname'
|
||||||
dropOnEmpty: false,
|
dropOnEmpty: false,
|
||||||
|
|
@ -635,17 +636,17 @@ var Sortable = {
|
||||||
delay: 0,
|
delay: 0,
|
||||||
hoverclass: null,
|
hoverclass: null,
|
||||||
ghosting: false,
|
ghosting: false,
|
||||||
quiet: false,
|
quiet: false,
|
||||||
scroll: false,
|
scroll: false,
|
||||||
scrollSensitivity: 20,
|
scrollSensitivity: 20,
|
||||||
scrollSpeed: 15,
|
scrollSpeed: 15,
|
||||||
format: this.SERIALIZE_RULE,
|
format: this.SERIALIZE_RULE,
|
||||||
|
|
||||||
// these take arrays of elements or ids and can be
|
// these take arrays of elements or ids and can be
|
||||||
// used for better initialization performance
|
// used for better initialization performance
|
||||||
elements: false,
|
elements: false,
|
||||||
handles: false,
|
handles: false,
|
||||||
|
|
||||||
onChange: Prototype.emptyFunction,
|
onChange: Prototype.emptyFunction,
|
||||||
onUpdate: Prototype.emptyFunction
|
onUpdate: Prototype.emptyFunction
|
||||||
}, arguments[1] || { });
|
}, arguments[1] || { });
|
||||||
|
|
@ -682,24 +683,24 @@ var Sortable = {
|
||||||
if(options.zindex)
|
if(options.zindex)
|
||||||
options_for_draggable.zindex = options.zindex;
|
options_for_draggable.zindex = options.zindex;
|
||||||
|
|
||||||
// build options for the droppables
|
// build options for the droppables
|
||||||
var options_for_droppable = {
|
var options_for_droppable = {
|
||||||
overlap: options.overlap,
|
overlap: options.overlap,
|
||||||
containment: options.containment,
|
containment: options.containment,
|
||||||
tree: options.tree,
|
tree: options.tree,
|
||||||
hoverclass: options.hoverclass,
|
hoverclass: options.hoverclass,
|
||||||
onHover: Sortable.onHover
|
onHover: Sortable.onHover
|
||||||
}
|
};
|
||||||
|
|
||||||
var options_for_tree = {
|
var options_for_tree = {
|
||||||
onHover: Sortable.onEmptyHover,
|
onHover: Sortable.onEmptyHover,
|
||||||
overlap: options.overlap,
|
overlap: options.overlap,
|
||||||
containment: options.containment,
|
containment: options.containment,
|
||||||
hoverclass: options.hoverclass
|
hoverclass: options.hoverclass
|
||||||
}
|
};
|
||||||
|
|
||||||
// fix for gecko engine
|
// fix for gecko engine
|
||||||
Element.cleanWhitespace(element);
|
Element.cleanWhitespace(element);
|
||||||
|
|
||||||
options.draggables = [];
|
options.draggables = [];
|
||||||
options.droppables = [];
|
options.droppables = [];
|
||||||
|
|
@ -712,14 +713,14 @@ var Sortable = {
|
||||||
|
|
||||||
(options.elements || this.findElements(element, options) || []).each( function(e,i) {
|
(options.elements || this.findElements(element, options) || []).each( function(e,i) {
|
||||||
var handle = options.handles ? $(options.handles[i]) :
|
var handle = options.handles ? $(options.handles[i]) :
|
||||||
(options.handle ? $(e).select('.' + options.handle)[0] : e);
|
(options.handle ? $(e).select('.' + options.handle)[0] : e);
|
||||||
options.draggables.push(
|
options.draggables.push(
|
||||||
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
||||||
Droppables.add(e, options_for_droppable);
|
Droppables.add(e, options_for_droppable);
|
||||||
if(options.tree) e.treeNode = element;
|
if(options.tree) e.treeNode = element;
|
||||||
options.droppables.push(e);
|
options.droppables.push(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(options.tree) {
|
if(options.tree) {
|
||||||
(Sortable.findTreeElements(element, options) || []).each( function(e) {
|
(Sortable.findTreeElements(element, options) || []).each( function(e) {
|
||||||
Droppables.add(e, options_for_tree);
|
Droppables.add(e, options_for_tree);
|
||||||
|
|
@ -741,7 +742,7 @@ var Sortable = {
|
||||||
return Element.findChildren(
|
return Element.findChildren(
|
||||||
element, options.only, options.tree ? true : false, options.tag);
|
element, options.only, options.tree ? true : false, options.tag);
|
||||||
},
|
},
|
||||||
|
|
||||||
findTreeElements: function(element, options) {
|
findTreeElements: function(element, options) {
|
||||||
return Element.findChildren(
|
return Element.findChildren(
|
||||||
element, options.only, options.tree ? true : false, options.treeTag);
|
element, options.only, options.tree ? true : false, options.treeTag);
|
||||||
|
|
@ -758,7 +759,7 @@ var Sortable = {
|
||||||
var oldParentNode = element.parentNode;
|
var oldParentNode = element.parentNode;
|
||||||
element.style.visibility = "hidden"; // fix gecko rendering
|
element.style.visibility = "hidden"; // fix gecko rendering
|
||||||
dropon.parentNode.insertBefore(element, dropon);
|
dropon.parentNode.insertBefore(element, dropon);
|
||||||
if(dropon.parentNode!=oldParentNode)
|
if(dropon.parentNode!=oldParentNode)
|
||||||
Sortable.options(oldParentNode).onChange(element);
|
Sortable.options(oldParentNode).onChange(element);
|
||||||
Sortable.options(dropon.parentNode).onChange(element);
|
Sortable.options(dropon.parentNode).onChange(element);
|
||||||
}
|
}
|
||||||
|
|
@ -769,26 +770,26 @@ var Sortable = {
|
||||||
var oldParentNode = element.parentNode;
|
var oldParentNode = element.parentNode;
|
||||||
element.style.visibility = "hidden"; // fix gecko rendering
|
element.style.visibility = "hidden"; // fix gecko rendering
|
||||||
dropon.parentNode.insertBefore(element, nextElement);
|
dropon.parentNode.insertBefore(element, nextElement);
|
||||||
if(dropon.parentNode!=oldParentNode)
|
if(dropon.parentNode!=oldParentNode)
|
||||||
Sortable.options(oldParentNode).onChange(element);
|
Sortable.options(oldParentNode).onChange(element);
|
||||||
Sortable.options(dropon.parentNode).onChange(element);
|
Sortable.options(dropon.parentNode).onChange(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onEmptyHover: function(element, dropon, overlap) {
|
onEmptyHover: function(element, dropon, overlap) {
|
||||||
var oldParentNode = element.parentNode;
|
var oldParentNode = element.parentNode;
|
||||||
var droponOptions = Sortable.options(dropon);
|
var droponOptions = Sortable.options(dropon);
|
||||||
|
|
||||||
if(!Element.isParent(dropon, element)) {
|
if(!Element.isParent(dropon, element)) {
|
||||||
var index;
|
var index;
|
||||||
|
|
||||||
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
|
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
|
||||||
var child = null;
|
var child = null;
|
||||||
|
|
||||||
if(children) {
|
if(children) {
|
||||||
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
|
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
|
||||||
|
|
||||||
for (index = 0; index < children.length; index += 1) {
|
for (index = 0; index < children.length; index += 1) {
|
||||||
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
|
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
|
||||||
offset -= Element.offsetSize (children[index], droponOptions.overlap);
|
offset -= Element.offsetSize (children[index], droponOptions.overlap);
|
||||||
|
|
@ -801,9 +802,9 @@ var Sortable = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dropon.insertBefore(element, child);
|
dropon.insertBefore(element, child);
|
||||||
|
|
||||||
Sortable.options(oldParentNode).onChange(element);
|
Sortable.options(oldParentNode).onChange(element);
|
||||||
droponOptions.onChange(element);
|
droponOptions.onChange(element);
|
||||||
}
|
}
|
||||||
|
|
@ -816,34 +817,34 @@ var Sortable = {
|
||||||
mark: function(dropon, position) {
|
mark: function(dropon, position) {
|
||||||
// mark on ghosting only
|
// mark on ghosting only
|
||||||
var sortable = Sortable.options(dropon.parentNode);
|
var sortable = Sortable.options(dropon.parentNode);
|
||||||
if(sortable && !sortable.ghosting) return;
|
if(sortable && !sortable.ghosting) return;
|
||||||
|
|
||||||
if(!Sortable._marker) {
|
if(!Sortable._marker) {
|
||||||
Sortable._marker =
|
Sortable._marker =
|
||||||
($('dropmarker') || Element.extend(document.createElement('DIV'))).
|
($('dropmarker') || Element.extend(document.createElement('DIV'))).
|
||||||
hide().addClassName('dropmarker').setStyle({position:'absolute'});
|
hide().addClassName('dropmarker').setStyle({position:'absolute'});
|
||||||
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
|
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
|
||||||
}
|
}
|
||||||
var offsets = Position.cumulativeOffset(dropon);
|
var offsets = Position.cumulativeOffset(dropon);
|
||||||
Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
|
Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
|
||||||
|
|
||||||
if(position=='after')
|
if(position=='after')
|
||||||
if(sortable.overlap == 'horizontal')
|
if(sortable.overlap == 'horizontal')
|
||||||
Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
|
Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
|
||||||
else
|
else
|
||||||
Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
|
Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
|
||||||
|
|
||||||
Sortable._marker.show();
|
Sortable._marker.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
_tree: function(element, options, parent) {
|
_tree: function(element, options, parent) {
|
||||||
var children = Sortable.findElements(element, options) || [];
|
var children = Sortable.findElements(element, options) || [];
|
||||||
|
|
||||||
for (var i = 0; i < children.length; ++i) {
|
for (var i = 0; i < children.length; ++i) {
|
||||||
var match = children[i].id.match(options.format);
|
var match = children[i].id.match(options.format);
|
||||||
|
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
|
|
||||||
var child = {
|
var child = {
|
||||||
id: encodeURIComponent(match ? match[1] : null),
|
id: encodeURIComponent(match ? match[1] : null),
|
||||||
element: element,
|
element: element,
|
||||||
|
|
@ -851,16 +852,16 @@ var Sortable = {
|
||||||
children: [],
|
children: [],
|
||||||
position: parent.children.length,
|
position: parent.children.length,
|
||||||
container: $(children[i]).down(options.treeTag)
|
container: $(children[i]).down(options.treeTag)
|
||||||
}
|
};
|
||||||
|
|
||||||
/* Get the element containing the children and recurse over it */
|
/* Get the element containing the children and recurse over it */
|
||||||
if (child.container)
|
if (child.container)
|
||||||
this._tree(child.container, options, child)
|
this._tree(child.container, options, child);
|
||||||
|
|
||||||
parent.children.push (child);
|
parent.children.push (child);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent;
|
return parent;
|
||||||
},
|
},
|
||||||
|
|
||||||
tree: function(element) {
|
tree: function(element) {
|
||||||
|
|
@ -873,15 +874,15 @@ var Sortable = {
|
||||||
name: element.id,
|
name: element.id,
|
||||||
format: sortableOptions.format
|
format: sortableOptions.format
|
||||||
}, arguments[1] || { });
|
}, arguments[1] || { });
|
||||||
|
|
||||||
var root = {
|
var root = {
|
||||||
id: null,
|
id: null,
|
||||||
parent: null,
|
parent: null,
|
||||||
children: [],
|
children: [],
|
||||||
container: element,
|
container: element,
|
||||||
position: 0
|
position: 0
|
||||||
}
|
};
|
||||||
|
|
||||||
return Sortable._tree(element, options, root);
|
return Sortable._tree(element, options, root);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -897,7 +898,7 @@ var Sortable = {
|
||||||
sequence: function(element) {
|
sequence: function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var options = Object.extend(this.options(element), arguments[1] || { });
|
var options = Object.extend(this.options(element), arguments[1] || { });
|
||||||
|
|
||||||
return $(this.findElements(element, options) || []).map( function(item) {
|
return $(this.findElements(element, options) || []).map( function(item) {
|
||||||
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
|
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
|
||||||
});
|
});
|
||||||
|
|
@ -906,14 +907,14 @@ var Sortable = {
|
||||||
setSequence: function(element, new_sequence) {
|
setSequence: function(element, new_sequence) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var options = Object.extend(this.options(element), arguments[2] || { });
|
var options = Object.extend(this.options(element), arguments[2] || { });
|
||||||
|
|
||||||
var nodeMap = { };
|
var nodeMap = { };
|
||||||
this.findElements(element, options).each( function(n) {
|
this.findElements(element, options).each( function(n) {
|
||||||
if (n.id.match(options.format))
|
if (n.id.match(options.format))
|
||||||
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
|
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
|
||||||
n.parentNode.removeChild(n);
|
n.parentNode.removeChild(n);
|
||||||
});
|
});
|
||||||
|
|
||||||
new_sequence.each(function(ident) {
|
new_sequence.each(function(ident) {
|
||||||
var n = nodeMap[ident];
|
var n = nodeMap[ident];
|
||||||
if (n) {
|
if (n) {
|
||||||
|
|
@ -922,16 +923,16 @@ var Sortable = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
serialize: function(element) {
|
serialize: function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var options = Object.extend(Sortable.options(element), arguments[1] || { });
|
var options = Object.extend(Sortable.options(element), arguments[1] || { });
|
||||||
var name = encodeURIComponent(
|
var name = encodeURIComponent(
|
||||||
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
|
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
|
||||||
|
|
||||||
if (options.tree) {
|
if (options.tree) {
|
||||||
return Sortable.tree(element, arguments[1]).children.map( function (item) {
|
return Sortable.tree(element, arguments[1]).children.map( function (item) {
|
||||||
return [name + Sortable._constructIndex(item) + "[id]=" +
|
return [name + Sortable._constructIndex(item) + "[id]=" +
|
||||||
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
|
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
|
||||||
}).flatten().join('&');
|
}).flatten().join('&');
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -940,16 +941,16 @@ var Sortable = {
|
||||||
}).join('&');
|
}).join('&');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Returns true if child is contained within element
|
// Returns true if child is contained within element
|
||||||
Element.isParent = function(child, element) {
|
Element.isParent = function(child, element) {
|
||||||
if (!child.parentNode || child == element) return false;
|
if (!child.parentNode || child == element) return false;
|
||||||
if (child.parentNode == element) return true;
|
if (child.parentNode == element) return true;
|
||||||
return Element.isParent(child.parentNode, element);
|
return Element.isParent(child.parentNode, element);
|
||||||
}
|
};
|
||||||
|
|
||||||
Element.findChildren = function(element, only, recursive, tagName) {
|
Element.findChildren = function(element, only, recursive, tagName) {
|
||||||
if(!element.hasChildNodes()) return null;
|
if(!element.hasChildNodes()) return null;
|
||||||
tagName = tagName.toUpperCase();
|
tagName = tagName.toUpperCase();
|
||||||
if(only) only = [only].flatten();
|
if(only) only = [only].flatten();
|
||||||
|
|
@ -965,8 +966,8 @@ Element.findChildren = function(element, only, recursive, tagName) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (elements.length>0 ? elements.flatten() : []);
|
return (elements.length>0 ? elements.flatten() : []);
|
||||||
}
|
};
|
||||||
|
|
||||||
Element.offsetSize = function (element, type) {
|
Element.offsetSize = function (element, type) {
|
||||||
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
|
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
|
||||||
}
|
};
|
||||||
338
public/javascripts/effects.js
vendored
338
public/javascripts/effects.js
vendored
|
|
@ -3,46 +3,46 @@
|
||||||
// Justin Palmer (http://encytemedia.com/)
|
// Justin Palmer (http://encytemedia.com/)
|
||||||
// Mark Pilgrim (http://diveintomark.org/)
|
// Mark Pilgrim (http://diveintomark.org/)
|
||||||
// Martin Bialasinki
|
// Martin Bialasinki
|
||||||
//
|
//
|
||||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||||
|
|
||||||
// converts rgb() and #xxx to #xxxxxx format,
|
// converts rgb() and #xxx to #xxxxxx format,
|
||||||
// returns self (or first argument) if not convertable
|
// returns self (or first argument) if not convertable
|
||||||
String.prototype.parseColor = function() {
|
String.prototype.parseColor = function() {
|
||||||
var color = '#';
|
var color = '#';
|
||||||
if (this.slice(0,4) == 'rgb(') {
|
if (this.slice(0,4) == 'rgb(') {
|
||||||
var cols = this.slice(4,this.length-1).split(',');
|
var cols = this.slice(4,this.length-1).split(',');
|
||||||
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
|
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
|
||||||
} else {
|
} else {
|
||||||
if (this.slice(0,1) == '#') {
|
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==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
|
||||||
if (this.length==7) color = this.toLowerCase();
|
if (this.length==7) color = this.toLowerCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (color.length==7 ? color : (arguments[0] || this));
|
return (color.length==7 ? color : (arguments[0] || this));
|
||||||
};
|
};
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
Element.collectTextNodes = function(element) {
|
Element.collectTextNodes = function(element) {
|
||||||
return $A($(element).childNodes).collect( function(node) {
|
return $A($(element).childNodes).collect( function(node) {
|
||||||
return (node.nodeType==3 ? node.nodeValue :
|
return (node.nodeType==3 ? node.nodeValue :
|
||||||
(node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
|
(node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
|
||||||
}).flatten().join('');
|
}).flatten().join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
Element.collectTextNodesIgnoreClass = function(element, className) {
|
Element.collectTextNodesIgnoreClass = function(element, className) {
|
||||||
return $A($(element).childNodes).collect( function(node) {
|
return $A($(element).childNodes).collect( function(node) {
|
||||||
return (node.nodeType==3 ? node.nodeValue :
|
return (node.nodeType==3 ? node.nodeValue :
|
||||||
((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
|
((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
|
||||||
Element.collectTextNodesIgnoreClass(node, className) : ''));
|
Element.collectTextNodesIgnoreClass(node, className) : ''));
|
||||||
}).flatten().join('');
|
}).flatten().join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
Element.setContentZoom = function(element, percent) {
|
Element.setContentZoom = function(element, percent) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
element.setStyle({fontSize: (percent/100) + 'em'});
|
element.setStyle({fontSize: (percent/100) + 'em'});
|
||||||
if (Prototype.Browser.WebKit) window.scrollBy(0,0);
|
if (Prototype.Browser.WebKit) window.scrollBy(0,0);
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
@ -70,28 +70,23 @@ var Effect = {
|
||||||
Transitions: {
|
Transitions: {
|
||||||
linear: Prototype.K,
|
linear: Prototype.K,
|
||||||
sinoidal: function(pos) {
|
sinoidal: function(pos) {
|
||||||
return (-Math.cos(pos*Math.PI)/2) + 0.5;
|
return (-Math.cos(pos*Math.PI)/2) + .5;
|
||||||
},
|
},
|
||||||
reverse: function(pos) {
|
reverse: function(pos) {
|
||||||
return 1-pos;
|
return 1-pos;
|
||||||
},
|
},
|
||||||
flicker: function(pos) {
|
flicker: function(pos) {
|
||||||
var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
|
var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
|
||||||
return pos > 1 ? 1 : pos;
|
return pos > 1 ? 1 : pos;
|
||||||
},
|
},
|
||||||
wobble: function(pos) {
|
wobble: function(pos) {
|
||||||
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
|
return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
|
||||||
},
|
},
|
||||||
pulse: function(pos, pulses) {
|
pulse: function(pos, pulses) {
|
||||||
pulses = pulses || 5;
|
return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
|
||||||
return (
|
|
||||||
((pos % (1/pulses)) * pulses).round() == 0 ?
|
|
||||||
((pos * pulses * 2) - (pos * pulses * 2).floor()) :
|
|
||||||
1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
spring: function(pos) {
|
spring: function(pos) {
|
||||||
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
|
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
|
||||||
},
|
},
|
||||||
none: function(pos) {
|
none: function(pos) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -112,14 +107,14 @@ var Effect = {
|
||||||
tagifyText: function(element) {
|
tagifyText: function(element) {
|
||||||
var tagifyStyle = 'position:relative';
|
var tagifyStyle = 'position:relative';
|
||||||
if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
|
if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
|
||||||
|
|
||||||
element = $(element);
|
element = $(element);
|
||||||
$A(element.childNodes).each( function(child) {
|
$A(element.childNodes).each( function(child) {
|
||||||
if (child.nodeType==3) {
|
if (child.nodeType==3) {
|
||||||
child.nodeValue.toArray().each( function(character) {
|
child.nodeValue.toArray().each( function(character) {
|
||||||
element.insertBefore(
|
element.insertBefore(
|
||||||
new Element('span', {style: tagifyStyle}).update(
|
new Element('span', {style: tagifyStyle}).update(
|
||||||
character == ' ' ? String.fromCharCode(160) : character),
|
character == ' ' ? String.fromCharCode(160) : character),
|
||||||
child);
|
child);
|
||||||
});
|
});
|
||||||
Element.remove(child);
|
Element.remove(child);
|
||||||
|
|
@ -128,13 +123,13 @@ var Effect = {
|
||||||
},
|
},
|
||||||
multiple: function(element, effect) {
|
multiple: function(element, effect) {
|
||||||
var elements;
|
var elements;
|
||||||
if (((typeof element == 'object') ||
|
if (((typeof element == 'object') ||
|
||||||
Object.isFunction(element)) &&
|
Object.isFunction(element)) &&
|
||||||
(element.length))
|
(element.length))
|
||||||
elements = element;
|
elements = element;
|
||||||
else
|
else
|
||||||
elements = $(element).childNodes;
|
elements = $(element).childNodes;
|
||||||
|
|
||||||
var options = Object.extend({
|
var options = Object.extend({
|
||||||
speed: 0.1,
|
speed: 0.1,
|
||||||
delay: 0.0
|
delay: 0.0
|
||||||
|
|
@ -156,7 +151,7 @@ var Effect = {
|
||||||
var options = Object.extend({
|
var options = Object.extend({
|
||||||
queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
|
queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
|
||||||
}, arguments[2] || { });
|
}, arguments[2] || { });
|
||||||
Effect[element.visible() ?
|
Effect[element.visible() ?
|
||||||
Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
|
Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -168,20 +163,20 @@ Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
|
||||||
Effect.ScopedQueue = Class.create(Enumerable, {
|
Effect.ScopedQueue = Class.create(Enumerable, {
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
this.effects = [];
|
this.effects = [];
|
||||||
this.interval = null;
|
this.interval = null;
|
||||||
},
|
},
|
||||||
_each: function(iterator) {
|
_each: function(iterator) {
|
||||||
this.effects._each(iterator);
|
this.effects._each(iterator);
|
||||||
},
|
},
|
||||||
add: function(effect) {
|
add: function(effect) {
|
||||||
var timestamp = new Date().getTime();
|
var timestamp = new Date().getTime();
|
||||||
|
|
||||||
var position = Object.isString(effect.options.queue) ?
|
var position = Object.isString(effect.options.queue) ?
|
||||||
effect.options.queue : effect.options.queue.position;
|
effect.options.queue : effect.options.queue.position;
|
||||||
|
|
||||||
switch(position) {
|
switch(position) {
|
||||||
case 'front':
|
case 'front':
|
||||||
// move unstarted effects after this effect
|
// move unstarted effects after this effect
|
||||||
this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
|
this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
|
||||||
e.startOn += effect.finishOn;
|
e.startOn += effect.finishOn;
|
||||||
e.finishOn += effect.finishOn;
|
e.finishOn += effect.finishOn;
|
||||||
|
|
@ -195,13 +190,13 @@ Effect.ScopedQueue = Class.create(Enumerable, {
|
||||||
timestamp = this.effects.pluck('finishOn').max() || timestamp;
|
timestamp = this.effects.pluck('finishOn').max() || timestamp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
effect.startOn += timestamp;
|
effect.startOn += timestamp;
|
||||||
effect.finishOn += timestamp;
|
effect.finishOn += timestamp;
|
||||||
|
|
||||||
if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
|
if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
|
||||||
this.effects.push(effect);
|
this.effects.push(effect);
|
||||||
|
|
||||||
if (!this.interval)
|
if (!this.interval)
|
||||||
this.interval = setInterval(this.loop.bind(this), 15);
|
this.interval = setInterval(this.loop.bind(this), 15);
|
||||||
},
|
},
|
||||||
|
|
@ -214,7 +209,7 @@ Effect.ScopedQueue = Class.create(Enumerable, {
|
||||||
},
|
},
|
||||||
loop: function() {
|
loop: function() {
|
||||||
var timePos = new Date().getTime();
|
var timePos = new Date().getTime();
|
||||||
for(var i=0, len=this.effects.length;i<len;i++)
|
for(var i=0, len=this.effects.length;i<len;i++)
|
||||||
this.effects[i] && this.effects[i].loop(timePos);
|
this.effects[i] && this.effects[i].loop(timePos);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -223,7 +218,7 @@ Effect.Queues = {
|
||||||
instances: $H(),
|
instances: $H(),
|
||||||
get: function(queueName) {
|
get: function(queueName) {
|
||||||
if (!Object.isString(queueName)) return queueName;
|
if (!Object.isString(queueName)) return queueName;
|
||||||
|
|
||||||
return this.instances.get(queueName) ||
|
return this.instances.get(queueName) ||
|
||||||
this.instances.set(queueName, new Effect.ScopedQueue());
|
this.instances.set(queueName, new Effect.ScopedQueue());
|
||||||
}
|
}
|
||||||
|
|
@ -248,23 +243,35 @@ Effect.Base = Class.create({
|
||||||
this.fromToDelta = this.options.to-this.options.from;
|
this.fromToDelta = this.options.to-this.options.from;
|
||||||
this.totalTime = this.finishOn-this.startOn;
|
this.totalTime = this.finishOn-this.startOn;
|
||||||
this.totalFrames = this.options.fps*this.options.duration;
|
this.totalFrames = this.options.fps*this.options.duration;
|
||||||
|
|
||||||
eval('this.render = function(pos){ '+
|
this.render = (function() {
|
||||||
'if (this.state=="idle"){this.state="running";'+
|
function dispatch(effect, eventName) {
|
||||||
codeForEvent(this.options,'beforeSetup')+
|
if (effect.options[eventName + 'Internal'])
|
||||||
(this.setup ? 'this.setup();':'')+
|
effect.options[eventName + 'Internal'](effect);
|
||||||
codeForEvent(this.options,'afterSetup')+
|
if (effect.options[eventName])
|
||||||
'};if (this.state=="running"){'+
|
effect.options[eventName](effect);
|
||||||
'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
|
}
|
||||||
'this.position=pos;'+
|
|
||||||
codeForEvent(this.options,'beforeUpdate')+
|
return function(pos) {
|
||||||
(this.update ? 'this.update(pos);':'')+
|
if (this.state === "idle") {
|
||||||
codeForEvent(this.options,'afterUpdate')+
|
this.state = "running";
|
||||||
'}}');
|
dispatch(this, 'beforeSetup');
|
||||||
|
if (this.setup) this.setup();
|
||||||
|
dispatch(this, 'afterSetup');
|
||||||
|
}
|
||||||
|
if (this.state === "running") {
|
||||||
|
pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
|
||||||
|
this.position = pos;
|
||||||
|
dispatch(this, 'beforeUpdate');
|
||||||
|
if (this.update) this.update(pos);
|
||||||
|
dispatch(this, 'afterUpdate');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
this.event('beforeStart');
|
this.event('beforeStart');
|
||||||
if (!this.options.sync)
|
if (!this.options.sync)
|
||||||
Effect.Queues.get(Object.isString(this.options.queue) ?
|
Effect.Queues.get(Object.isString(this.options.queue) ?
|
||||||
'global' : this.options.queue.scope).add(this);
|
'global' : this.options.queue.scope).add(this);
|
||||||
},
|
},
|
||||||
loop: function(timePos) {
|
loop: function(timePos) {
|
||||||
|
|
@ -273,9 +280,9 @@ Effect.Base = Class.create({
|
||||||
this.render(1.0);
|
this.render(1.0);
|
||||||
this.cancel();
|
this.cancel();
|
||||||
this.event('beforeFinish');
|
this.event('beforeFinish');
|
||||||
if (this.finish) this.finish();
|
if (this.finish) this.finish();
|
||||||
this.event('afterFinish');
|
this.event('afterFinish');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var pos = (timePos - this.startOn) / this.totalTime,
|
var pos = (timePos - this.startOn) / this.totalTime,
|
||||||
frame = (pos * this.totalFrames).round();
|
frame = (pos * this.totalFrames).round();
|
||||||
|
|
@ -287,7 +294,7 @@ Effect.Base = Class.create({
|
||||||
},
|
},
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
if (!this.options.sync)
|
if (!this.options.sync)
|
||||||
Effect.Queues.get(Object.isString(this.options.queue) ?
|
Effect.Queues.get(Object.isString(this.options.queue) ?
|
||||||
'global' : this.options.queue.scope).remove(this);
|
'global' : this.options.queue.scope).remove(this);
|
||||||
this.state = 'finished';
|
this.state = 'finished';
|
||||||
},
|
},
|
||||||
|
|
@ -325,10 +332,10 @@ Effect.Parallel = Class.create(Effect.Base, {
|
||||||
Effect.Tween = Class.create(Effect.Base, {
|
Effect.Tween = Class.create(Effect.Base, {
|
||||||
initialize: function(object, from, to) {
|
initialize: function(object, from, to) {
|
||||||
object = Object.isString(object) ? $(object) : object;
|
object = Object.isString(object) ? $(object) : object;
|
||||||
var args = $A(arguments), method = args.last(),
|
var args = $A(arguments), method = args.last(),
|
||||||
options = args.length == 5 ? args[3] : null;
|
options = args.length == 5 ? args[3] : null;
|
||||||
this.method = Object.isFunction(method) ? method.bind(object) :
|
this.method = Object.isFunction(method) ? method.bind(object) :
|
||||||
Object.isFunction(object[method]) ? object[method].bind(object) :
|
Object.isFunction(object[method]) ? object[method].bind(object) :
|
||||||
function(value) { object[method] = value };
|
function(value) { object[method] = value };
|
||||||
this.start(Object.extend({ from: from, to: to }, options || { }));
|
this.start(Object.extend({ from: from, to: to }, options || { }));
|
||||||
},
|
},
|
||||||
|
|
@ -392,7 +399,7 @@ Effect.Move = Class.create(Effect.Base, {
|
||||||
|
|
||||||
// for backwards compatibility
|
// for backwards compatibility
|
||||||
Effect.MoveBy = function(element, toTop, toLeft) {
|
Effect.MoveBy = function(element, toTop, toLeft) {
|
||||||
return new Effect.Move(element,
|
return new Effect.Move(element,
|
||||||
Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
|
Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -414,15 +421,15 @@ Effect.Scale = Class.create(Effect.Base, {
|
||||||
setup: function() {
|
setup: function() {
|
||||||
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
|
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
|
||||||
this.elementPositioning = this.element.getStyle('position');
|
this.elementPositioning = this.element.getStyle('position');
|
||||||
|
|
||||||
this.originalStyle = { };
|
this.originalStyle = { };
|
||||||
['top','left','width','height','fontSize'].each( function(k) {
|
['top','left','width','height','fontSize'].each( function(k) {
|
||||||
this.originalStyle[k] = this.element.style[k];
|
this.originalStyle[k] = this.element.style[k];
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
this.originalTop = this.element.offsetTop;
|
this.originalTop = this.element.offsetTop;
|
||||||
this.originalLeft = this.element.offsetLeft;
|
this.originalLeft = this.element.offsetLeft;
|
||||||
|
|
||||||
var fontSize = this.element.getStyle('font-size') || '100%';
|
var fontSize = this.element.getStyle('font-size') || '100%';
|
||||||
['em','px','%','pt'].each( function(fontSizeType) {
|
['em','px','%','pt'].each( function(fontSizeType) {
|
||||||
if (fontSize.indexOf(fontSizeType)>0) {
|
if (fontSize.indexOf(fontSizeType)>0) {
|
||||||
|
|
@ -430,9 +437,9 @@ Effect.Scale = Class.create(Effect.Base, {
|
||||||
this.fontSizeType = fontSizeType;
|
this.fontSizeType = fontSizeType;
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
|
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
|
||||||
|
|
||||||
this.dims = null;
|
this.dims = null;
|
||||||
if (this.options.scaleMode=='box')
|
if (this.options.scaleMode=='box')
|
||||||
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
|
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
|
||||||
|
|
@ -507,17 +514,16 @@ Effect.Highlight = Class.create(Effect.Base, {
|
||||||
|
|
||||||
Effect.ScrollTo = function(element) {
|
Effect.ScrollTo = function(element) {
|
||||||
var options = arguments[1] || { },
|
var options = arguments[1] || { },
|
||||||
scrollOffsets = document.viewport.getScrollOffsets(),
|
scrollOffsets = document.viewport.getScrollOffsets(),
|
||||||
elementOffsets = $(element).cumulativeOffset(),
|
elementOffsets = $(element).cumulativeOffset();
|
||||||
max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
|
|
||||||
|
|
||||||
if (options.offset) elementOffsets[1] += options.offset;
|
if (options.offset) elementOffsets[1] += options.offset;
|
||||||
|
|
||||||
return new Effect.Tween(null,
|
return new Effect.Tween(null,
|
||||||
scrollOffsets.top,
|
scrollOffsets.top,
|
||||||
elementOffsets[1] > max ? max : elementOffsets[1],
|
elementOffsets[1],
|
||||||
options,
|
options,
|
||||||
function(p){ scrollTo(scrollOffsets.left, p.round()) }
|
function(p){ scrollTo(scrollOffsets.left, p.round()); }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -529,9 +535,9 @@ Effect.Fade = function(element) {
|
||||||
var options = Object.extend({
|
var options = Object.extend({
|
||||||
from: element.getOpacity() || 1.0,
|
from: element.getOpacity() || 1.0,
|
||||||
to: 0.0,
|
to: 0.0,
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
if (effect.options.to!=0) return;
|
if (effect.options.to!=0) return;
|
||||||
effect.element.hide().setStyle({opacity: oldOpacity});
|
effect.element.hide().setStyle({opacity: oldOpacity});
|
||||||
}
|
}
|
||||||
}, arguments[1] || { });
|
}, arguments[1] || { });
|
||||||
return new Effect.Opacity(element,options);
|
return new Effect.Opacity(element,options);
|
||||||
|
|
@ -547,15 +553,15 @@ Effect.Appear = function(element) {
|
||||||
effect.element.forceRerendering();
|
effect.element.forceRerendering();
|
||||||
},
|
},
|
||||||
beforeSetup: function(effect) {
|
beforeSetup: function(effect) {
|
||||||
effect.element.setOpacity(effect.options.from).show();
|
effect.element.setOpacity(effect.options.from).show();
|
||||||
}}, arguments[1] || { });
|
}}, arguments[1] || { });
|
||||||
return new Effect.Opacity(element,options);
|
return new Effect.Opacity(element,options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Effect.Puff = function(element) {
|
Effect.Puff = function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var oldStyle = {
|
var oldStyle = {
|
||||||
opacity: element.getInlineOpacity(),
|
opacity: element.getInlineOpacity(),
|
||||||
position: element.getStyle('position'),
|
position: element.getStyle('position'),
|
||||||
top: element.style.top,
|
top: element.style.top,
|
||||||
left: element.style.left,
|
left: element.style.left,
|
||||||
|
|
@ -563,12 +569,12 @@ Effect.Puff = function(element) {
|
||||||
height: element.style.height
|
height: element.style.height
|
||||||
};
|
};
|
||||||
return new Effect.Parallel(
|
return new Effect.Parallel(
|
||||||
[ new Effect.Scale(element, 200,
|
[ new Effect.Scale(element, 200,
|
||||||
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
|
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
|
||||||
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
|
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
|
||||||
Object.extend({ duration: 1.0,
|
Object.extend({ duration: 1.0,
|
||||||
beforeSetupInternal: function(effect) {
|
beforeSetupInternal: function(effect) {
|
||||||
Position.absolutize(effect.effects[0].element)
|
Position.absolutize(effect.effects[0].element);
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.effects[0].element.hide().setStyle(oldStyle); }
|
effect.effects[0].element.hide().setStyle(oldStyle); }
|
||||||
|
|
@ -580,12 +586,12 @@ Effect.BlindUp = function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
element.makeClipping();
|
element.makeClipping();
|
||||||
return new Effect.Scale(element, 0,
|
return new Effect.Scale(element, 0,
|
||||||
Object.extend({ scaleContent: false,
|
Object.extend({ scaleContent: false,
|
||||||
scaleX: false,
|
scaleX: false,
|
||||||
restoreAfterFinish: true,
|
restoreAfterFinish: true,
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.element.hide().undoClipping();
|
effect.element.hide().undoClipping();
|
||||||
}
|
}
|
||||||
}, arguments[1] || { })
|
}, arguments[1] || { })
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -593,15 +599,15 @@ Effect.BlindUp = function(element) {
|
||||||
Effect.BlindDown = function(element) {
|
Effect.BlindDown = function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var elementDimensions = element.getDimensions();
|
var elementDimensions = element.getDimensions();
|
||||||
return new Effect.Scale(element, 100, Object.extend({
|
return new Effect.Scale(element, 100, Object.extend({
|
||||||
scaleContent: false,
|
scaleContent: false,
|
||||||
scaleX: false,
|
scaleX: false,
|
||||||
scaleFrom: 0,
|
scaleFrom: 0,
|
||||||
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
||||||
restoreAfterFinish: true,
|
restoreAfterFinish: true,
|
||||||
afterSetup: function(effect) {
|
afterSetup: function(effect) {
|
||||||
effect.element.makeClipping().setStyle({height: '0px'}).show();
|
effect.element.makeClipping().setStyle({height: '0px'}).show();
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.element.undoClipping();
|
effect.element.undoClipping();
|
||||||
}
|
}
|
||||||
|
|
@ -616,16 +622,16 @@ Effect.SwitchOff = function(element) {
|
||||||
from: 0,
|
from: 0,
|
||||||
transition: Effect.Transitions.flicker,
|
transition: Effect.Transitions.flicker,
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
new Effect.Scale(effect.element, 1, {
|
new Effect.Scale(effect.element, 1, {
|
||||||
duration: 0.3, scaleFromCenter: true,
|
duration: 0.3, scaleFromCenter: true,
|
||||||
scaleX: false, scaleContent: false, restoreAfterFinish: true,
|
scaleX: false, scaleContent: false, restoreAfterFinish: true,
|
||||||
beforeSetup: function(effect) {
|
beforeSetup: function(effect) {
|
||||||
effect.element.makePositioned().makeClipping();
|
effect.element.makePositioned().makeClipping();
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
|
effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}, arguments[1] || { }));
|
}, arguments[1] || { }));
|
||||||
};
|
};
|
||||||
|
|
@ -637,16 +643,16 @@ Effect.DropOut = function(element) {
|
||||||
left: element.getStyle('left'),
|
left: element.getStyle('left'),
|
||||||
opacity: element.getInlineOpacity() };
|
opacity: element.getInlineOpacity() };
|
||||||
return new Effect.Parallel(
|
return new Effect.Parallel(
|
||||||
[ new Effect.Move(element, {x: 0, y: 100, sync: true }),
|
[ new Effect.Move(element, {x: 0, y: 100, sync: true }),
|
||||||
new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
|
new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
|
||||||
Object.extend(
|
Object.extend(
|
||||||
{ duration: 0.5,
|
{ duration: 0.5,
|
||||||
beforeSetup: function(effect) {
|
beforeSetup: function(effect) {
|
||||||
effect.effects[0].element.makePositioned();
|
effect.effects[0].element.makePositioned();
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
|
effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
|
||||||
}
|
}
|
||||||
}, arguments[1] || { }));
|
}, arguments[1] || { }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -674,7 +680,7 @@ Effect.Shake = function(element) {
|
||||||
new Effect.Move(effect.element,
|
new Effect.Move(effect.element,
|
||||||
{ x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
|
{ x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
|
||||||
effect.element.undoPositioned().setStyle(oldStyle);
|
effect.element.undoPositioned().setStyle(oldStyle);
|
||||||
}}) }}) }}) }}) }}) }});
|
}}); }}); }}); }}); }}); }});
|
||||||
};
|
};
|
||||||
|
|
||||||
Effect.SlideDown = function(element) {
|
Effect.SlideDown = function(element) {
|
||||||
|
|
@ -682,9 +688,9 @@ Effect.SlideDown = function(element) {
|
||||||
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
|
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
|
||||||
var oldInnerBottom = element.down().getStyle('bottom');
|
var oldInnerBottom = element.down().getStyle('bottom');
|
||||||
var elementDimensions = element.getDimensions();
|
var elementDimensions = element.getDimensions();
|
||||||
return new Effect.Scale(element, 100, Object.extend({
|
return new Effect.Scale(element, 100, Object.extend({
|
||||||
scaleContent: false,
|
scaleContent: false,
|
||||||
scaleX: false,
|
scaleX: false,
|
||||||
scaleFrom: window.opera ? 0 : 1,
|
scaleFrom: window.opera ? 0 : 1,
|
||||||
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
||||||
restoreAfterFinish: true,
|
restoreAfterFinish: true,
|
||||||
|
|
@ -692,11 +698,11 @@ Effect.SlideDown = function(element) {
|
||||||
effect.element.makePositioned();
|
effect.element.makePositioned();
|
||||||
effect.element.down().makePositioned();
|
effect.element.down().makePositioned();
|
||||||
if (window.opera) effect.element.setStyle({top: ''});
|
if (window.opera) effect.element.setStyle({top: ''});
|
||||||
effect.element.makeClipping().setStyle({height: '0px'}).show();
|
effect.element.makeClipping().setStyle({height: '0px'}).show();
|
||||||
},
|
},
|
||||||
afterUpdateInternal: function(effect) {
|
afterUpdateInternal: function(effect) {
|
||||||
effect.element.down().setStyle({bottom:
|
effect.element.down().setStyle({bottom:
|
||||||
(effect.dims[0] - effect.element.clientHeight) + 'px' });
|
(effect.dims[0] - effect.element.clientHeight) + 'px' });
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.element.undoClipping().undoPositioned();
|
effect.element.undoClipping().undoPositioned();
|
||||||
|
|
@ -710,8 +716,8 @@ Effect.SlideUp = function(element) {
|
||||||
var oldInnerBottom = element.down().getStyle('bottom');
|
var oldInnerBottom = element.down().getStyle('bottom');
|
||||||
var elementDimensions = element.getDimensions();
|
var elementDimensions = element.getDimensions();
|
||||||
return new Effect.Scale(element, window.opera ? 0 : 1,
|
return new Effect.Scale(element, window.opera ? 0 : 1,
|
||||||
Object.extend({ scaleContent: false,
|
Object.extend({ scaleContent: false,
|
||||||
scaleX: false,
|
scaleX: false,
|
||||||
scaleMode: 'box',
|
scaleMode: 'box',
|
||||||
scaleFrom: 100,
|
scaleFrom: 100,
|
||||||
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
||||||
|
|
@ -721,7 +727,7 @@ Effect.SlideUp = function(element) {
|
||||||
effect.element.down().makePositioned();
|
effect.element.down().makePositioned();
|
||||||
if (window.opera) effect.element.setStyle({top: ''});
|
if (window.opera) effect.element.setStyle({top: ''});
|
||||||
effect.element.makeClipping().show();
|
effect.element.makeClipping().show();
|
||||||
},
|
},
|
||||||
afterUpdateInternal: function(effect) {
|
afterUpdateInternal: function(effect) {
|
||||||
effect.element.down().setStyle({bottom:
|
effect.element.down().setStyle({bottom:
|
||||||
(effect.dims[0] - effect.element.clientHeight) + 'px' });
|
(effect.dims[0] - effect.element.clientHeight) + 'px' });
|
||||||
|
|
@ -734,15 +740,15 @@ Effect.SlideUp = function(element) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bug in opera makes the TD containing this element expand for a instance after finish
|
// Bug in opera makes the TD containing this element expand for a instance after finish
|
||||||
Effect.Squish = function(element) {
|
Effect.Squish = function(element) {
|
||||||
return new Effect.Scale(element, window.opera ? 1 : 0, {
|
return new Effect.Scale(element, window.opera ? 1 : 0, {
|
||||||
restoreAfterFinish: true,
|
restoreAfterFinish: true,
|
||||||
beforeSetup: function(effect) {
|
beforeSetup: function(effect) {
|
||||||
effect.element.makeClipping();
|
effect.element.makeClipping();
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.element.hide().undoClipping();
|
effect.element.hide().undoClipping();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -762,13 +768,13 @@ Effect.Grow = function(element) {
|
||||||
width: element.style.width,
|
width: element.style.width,
|
||||||
opacity: element.getInlineOpacity() };
|
opacity: element.getInlineOpacity() };
|
||||||
|
|
||||||
var dims = element.getDimensions();
|
var dims = element.getDimensions();
|
||||||
var initialMoveX, initialMoveY;
|
var initialMoveX, initialMoveY;
|
||||||
var moveX, moveY;
|
var moveX, moveY;
|
||||||
|
|
||||||
switch (options.direction) {
|
switch (options.direction) {
|
||||||
case 'top-left':
|
case 'top-left':
|
||||||
initialMoveX = initialMoveY = moveX = moveY = 0;
|
initialMoveX = initialMoveY = moveX = moveY = 0;
|
||||||
break;
|
break;
|
||||||
case 'top-right':
|
case 'top-right':
|
||||||
initialMoveX = dims.width;
|
initialMoveX = dims.width;
|
||||||
|
|
@ -793,11 +799,11 @@ Effect.Grow = function(element) {
|
||||||
moveY = -dims.height / 2;
|
moveY = -dims.height / 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Effect.Move(element, {
|
return new Effect.Move(element, {
|
||||||
x: initialMoveX,
|
x: initialMoveX,
|
||||||
y: initialMoveY,
|
y: initialMoveY,
|
||||||
duration: 0.01,
|
duration: 0.01,
|
||||||
beforeSetup: function(effect) {
|
beforeSetup: function(effect) {
|
||||||
effect.element.hide().makeClipping().makePositioned();
|
effect.element.hide().makeClipping().makePositioned();
|
||||||
},
|
},
|
||||||
|
|
@ -806,17 +812,17 @@ Effect.Grow = function(element) {
|
||||||
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
|
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
|
||||||
new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
|
new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
|
||||||
new Effect.Scale(effect.element, 100, {
|
new Effect.Scale(effect.element, 100, {
|
||||||
scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
|
scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
|
||||||
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
|
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
|
||||||
], Object.extend({
|
], Object.extend({
|
||||||
beforeSetup: function(effect) {
|
beforeSetup: function(effect) {
|
||||||
effect.effects[0].element.setStyle({height: '0px'}).show();
|
effect.effects[0].element.setStyle({height: '0px'}).show();
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
|
effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
|
||||||
}
|
}
|
||||||
}, options)
|
}, options)
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -838,7 +844,7 @@ Effect.Shrink = function(element) {
|
||||||
|
|
||||||
var dims = element.getDimensions();
|
var dims = element.getDimensions();
|
||||||
var moveX, moveY;
|
var moveX, moveY;
|
||||||
|
|
||||||
switch (options.direction) {
|
switch (options.direction) {
|
||||||
case 'top-left':
|
case 'top-left':
|
||||||
moveX = moveY = 0;
|
moveX = moveY = 0;
|
||||||
|
|
@ -855,19 +861,19 @@ Effect.Shrink = function(element) {
|
||||||
moveX = dims.width;
|
moveX = dims.width;
|
||||||
moveY = dims.height;
|
moveY = dims.height;
|
||||||
break;
|
break;
|
||||||
case 'center':
|
case 'center':
|
||||||
moveX = dims.width / 2;
|
moveX = dims.width / 2;
|
||||||
moveY = dims.height / 2;
|
moveY = dims.height / 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Effect.Parallel(
|
return new Effect.Parallel(
|
||||||
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
|
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
|
||||||
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
|
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
|
||||||
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
|
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
|
||||||
], Object.extend({
|
], Object.extend({
|
||||||
beforeStartInternal: function(effect) {
|
beforeStartInternal: function(effect) {
|
||||||
effect.effects[0].element.makePositioned().makeClipping();
|
effect.effects[0].element.makePositioned().makeClipping();
|
||||||
},
|
},
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
|
effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
|
||||||
|
|
@ -877,12 +883,14 @@ Effect.Shrink = function(element) {
|
||||||
|
|
||||||
Effect.Pulsate = function(element) {
|
Effect.Pulsate = function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var options = arguments[1] || { };
|
var options = arguments[1] || { },
|
||||||
var oldOpacity = element.getInlineOpacity();
|
oldOpacity = element.getInlineOpacity(),
|
||||||
var transition = options.transition || Effect.Transitions.sinoidal;
|
transition = options.transition || Effect.Transitions.linear,
|
||||||
var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
|
reverser = function(pos){
|
||||||
reverser.bind(transition);
|
return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
|
||||||
return new Effect.Opacity(element,
|
};
|
||||||
|
|
||||||
|
return new Effect.Opacity(element,
|
||||||
Object.extend(Object.extend({ duration: 2.0, from: 0,
|
Object.extend(Object.extend({ duration: 2.0, from: 0,
|
||||||
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
|
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
|
||||||
}, options), {transition: reverser}));
|
}, options), {transition: reverser}));
|
||||||
|
|
@ -896,12 +904,12 @@ Effect.Fold = function(element) {
|
||||||
width: element.style.width,
|
width: element.style.width,
|
||||||
height: element.style.height };
|
height: element.style.height };
|
||||||
element.makeClipping();
|
element.makeClipping();
|
||||||
return new Effect.Scale(element, 5, Object.extend({
|
return new Effect.Scale(element, 5, Object.extend({
|
||||||
scaleContent: false,
|
scaleContent: false,
|
||||||
scaleX: false,
|
scaleX: false,
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
new Effect.Scale(element, 1, {
|
new Effect.Scale(element, 1, {
|
||||||
scaleContent: false,
|
scaleContent: false,
|
||||||
scaleY: false,
|
scaleY: false,
|
||||||
afterFinishInternal: function(effect) {
|
afterFinishInternal: function(effect) {
|
||||||
effect.element.hide().undoClipping().setStyle(oldStyle);
|
effect.element.hide().undoClipping().setStyle(oldStyle);
|
||||||
|
|
@ -916,7 +924,7 @@ Effect.Morph = Class.create(Effect.Base, {
|
||||||
var options = Object.extend({
|
var options = Object.extend({
|
||||||
style: { }
|
style: { }
|
||||||
}, arguments[1] || { });
|
}, arguments[1] || { });
|
||||||
|
|
||||||
if (!Object.isString(options.style)) this.style = $H(options.style);
|
if (!Object.isString(options.style)) this.style = $H(options.style);
|
||||||
else {
|
else {
|
||||||
if (options.style.include(':'))
|
if (options.style.include(':'))
|
||||||
|
|
@ -934,18 +942,18 @@ Effect.Morph = Class.create(Effect.Base, {
|
||||||
effect.transforms.each(function(transform) {
|
effect.transforms.each(function(transform) {
|
||||||
effect.element.style[transform.style] = '';
|
effect.element.style[transform.style] = '';
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.start(options);
|
this.start(options);
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: function(){
|
setup: function(){
|
||||||
function parseColor(color){
|
function parseColor(color){
|
||||||
if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
|
if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
|
||||||
color = color.parseColor();
|
color = color.parseColor();
|
||||||
return $R(0,2).map(function(i){
|
return $R(0,2).map(function(i){
|
||||||
return parseInt( color.slice(i*2+1,i*2+3), 16 )
|
return parseInt( color.slice(i*2+1,i*2+3), 16 );
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.transforms = this.style.map(function(pair){
|
this.transforms = this.style.map(function(pair){
|
||||||
|
|
@ -965,9 +973,9 @@ Effect.Morph = Class.create(Effect.Base, {
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalValue = this.element.getStyle(property);
|
var originalValue = this.element.getStyle(property);
|
||||||
return {
|
return {
|
||||||
style: property.camelize(),
|
style: property.camelize(),
|
||||||
originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
|
originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
|
||||||
targetValue: unit=='color' ? parseColor(value) : value,
|
targetValue: unit=='color' ? parseColor(value) : value,
|
||||||
unit: unit
|
unit: unit
|
||||||
};
|
};
|
||||||
|
|
@ -978,13 +986,13 @@ Effect.Morph = Class.create(Effect.Base, {
|
||||||
transform.unit != 'color' &&
|
transform.unit != 'color' &&
|
||||||
(isNaN(transform.originalValue) || isNaN(transform.targetValue))
|
(isNaN(transform.originalValue) || isNaN(transform.targetValue))
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
update: function(position) {
|
update: function(position) {
|
||||||
var style = { }, transform, i = this.transforms.length;
|
var style = { }, transform, i = this.transforms.length;
|
||||||
while(i--)
|
while(i--)
|
||||||
style[(transform = this.transforms[i]).style] =
|
style[(transform = this.transforms[i]).style] =
|
||||||
transform.unit=='color' ? '#'+
|
transform.unit=='color' ? '#'+
|
||||||
(Math.round(transform.originalValue[0]+
|
(Math.round(transform.originalValue[0]+
|
||||||
(transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
|
(transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
|
||||||
|
|
@ -993,7 +1001,7 @@ Effect.Morph = Class.create(Effect.Base, {
|
||||||
(Math.round(transform.originalValue[2]+
|
(Math.round(transform.originalValue[2]+
|
||||||
(transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
|
(transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
|
||||||
(transform.originalValue +
|
(transform.originalValue +
|
||||||
(transform.targetValue - transform.originalValue) * position).toFixed(3) +
|
(transform.targetValue - transform.originalValue) * position).toFixed(3) +
|
||||||
(transform.unit === null ? '' : transform.unit);
|
(transform.unit === null ? '' : transform.unit);
|
||||||
this.element.setStyle(style, true);
|
this.element.setStyle(style, true);
|
||||||
}
|
}
|
||||||
|
|
@ -1030,7 +1038,7 @@ Effect.Transform = Class.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
Element.CSS_PROPERTIES = $w(
|
Element.CSS_PROPERTIES = $w(
|
||||||
'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
|
'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
|
||||||
'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
|
'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
|
||||||
'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
|
'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
|
||||||
'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
|
'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
|
||||||
|
|
@ -1039,7 +1047,7 @@ Element.CSS_PROPERTIES = $w(
|
||||||
'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
|
'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
|
||||||
'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
|
'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
|
||||||
'right textIndent top width wordSpacing zIndex');
|
'right textIndent top width wordSpacing zIndex');
|
||||||
|
|
||||||
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
|
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
|
||||||
|
|
||||||
String.__parseStyleElement = document.createElement('div');
|
String.__parseStyleElement = document.createElement('div');
|
||||||
|
|
@ -1051,11 +1059,11 @@ String.prototype.parseStyle = function(){
|
||||||
String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
|
String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
|
||||||
style = String.__parseStyleElement.childNodes[0].style;
|
style = String.__parseStyleElement.childNodes[0].style;
|
||||||
}
|
}
|
||||||
|
|
||||||
Element.CSS_PROPERTIES.each(function(property){
|
Element.CSS_PROPERTIES.each(function(property){
|
||||||
if (style[property]) styleRules.set(property, style[property]);
|
if (style[property]) styleRules.set(property, style[property]);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Prototype.Browser.IE && this.include('opacity'))
|
if (Prototype.Browser.IE && this.include('opacity'))
|
||||||
styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
|
styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
|
||||||
|
|
||||||
|
|
@ -1074,14 +1082,14 @@ if (document.defaultView && document.defaultView.getComputedStyle) {
|
||||||
Element.getStyles = function(element) {
|
Element.getStyles = function(element) {
|
||||||
element = $(element);
|
element = $(element);
|
||||||
var css = element.currentStyle, styles;
|
var css = element.currentStyle, styles;
|
||||||
styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
|
styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
|
||||||
hash.set(property, css[property]);
|
results[property] = css[property];
|
||||||
return hash;
|
return results;
|
||||||
});
|
});
|
||||||
if (!styles.opacity) styles.set('opacity', element.getOpacity());
|
if (!styles.opacity) styles.opacity = element.getOpacity();
|
||||||
return styles;
|
return styles;
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
Effect.Methods = {
|
Effect.Methods = {
|
||||||
morph: function(element, style) {
|
morph: function(element, style) {
|
||||||
|
|
@ -1090,7 +1098,7 @@ Effect.Methods = {
|
||||||
return element;
|
return element;
|
||||||
},
|
},
|
||||||
visualEffect: function(element, effect, options) {
|
visualEffect: function(element, effect, options) {
|
||||||
element = $(element)
|
element = $(element);
|
||||||
var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
|
var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
|
||||||
new Effect[klass](element, options);
|
new Effect[klass](element, options);
|
||||||
return element;
|
return element;
|
||||||
|
|
@ -1104,17 +1112,17 @@ Effect.Methods = {
|
||||||
|
|
||||||
$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
|
$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
|
||||||
'pulsate shake puff squish switchOff dropOut').each(
|
'pulsate shake puff squish switchOff dropOut').each(
|
||||||
function(effect) {
|
function(effect) {
|
||||||
Effect.Methods[effect] = function(element, options){
|
Effect.Methods[effect] = function(element, options){
|
||||||
element = $(element);
|
element = $(element);
|
||||||
Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
|
Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
|
||||||
return element;
|
return element;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
|
$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
|
||||||
function(f) { Effect.Methods[f] = Element[f]; }
|
function(f) { Effect.Methods[f] = Element[f]; }
|
||||||
);
|
);
|
||||||
|
|
||||||
Element.addMethods(Effect.Methods);
|
Element.addMethods(Effect.Methods);
|
||||||
629
public/javascripts/prototype.js
vendored
629
public/javascripts/prototype.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -29,15 +29,18 @@ div.footer a {
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
color: #f00;
|
color: #f00;
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
margin-top:.3em;
|
||||||
|
margin-bottom:.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
background-color: #cccccc;
|
background-color: #aaaaaa;
|
||||||
font-size : small;
|
font-size : small;
|
||||||
margin-top: 10px;
|
margin: .3em 0;
|
||||||
margin-bottom: 0;
|
padding: .3em 0 .1em .3em;
|
||||||
|
border-top: 1px solid #777777;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4.alert {
|
h4.alert {
|
||||||
|
|
@ -60,12 +63,8 @@ h4.notice {
|
||||||
color: #007E00;
|
color: #007E00;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.t {
|
|
||||||
padding-left:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.tag {
|
span.tag {
|
||||||
font-size: XX-small;
|
font-size: x-small;
|
||||||
background-color: #CCE7FF;
|
background-color: #CCE7FF;
|
||||||
color: #000;
|
color: #000;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
|
|
@ -86,43 +85,78 @@ span.prj, span.ctx{
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #f00;
|
background: #f00;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
font-size: 10px;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.amber {
|
.amber {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #ff6600;
|
background: #ff6600;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
font-size: 10px;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.orange {
|
.orange {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #FFA500;
|
background: #FFA500;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
font-size: 10px;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.green {
|
.green {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #33cc00;
|
background: #33cc00;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
font-size: 10px;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grey {
|
.grey {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #999;
|
background: #999;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
font-size: 10px;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #000;
|
background: #000;
|
||||||
font-size: medium;
|
font-size: medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.errors {
|
.errors {
|
||||||
background: #FFC2C2;
|
background: #FFC2C2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.c li.star {
|
||||||
|
list-style-image:url(../images/menustar_small.gif)
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.c {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.c li {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.c li span.r {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.r {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
font-size: x-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
#database_auth_form table td {
|
||||||
|
width:7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.c {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,293 +0,0 @@
|
||||||
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {margin:0; padding:0}
|
|
||||||
table {border-collapse:collapse; border-spacing:0}
|
|
||||||
fieldset,img {border:0}
|
|
||||||
address,caption,cite,code,dfn,em,strong,th,var {font-style:normal; font-weight:normal}
|
|
||||||
ol,ul {list-style:none}
|
|
||||||
caption,th {text-align:left}
|
|
||||||
h1,h2,h3,h4,h5,h6 {font-size:100%; font-weight:normal}
|
|
||||||
q:before,q:after {content:''}
|
|
||||||
abbr,acronym {border:0}
|
|
||||||
body {font-family: "Lucida Grande", Verdana, Geneva, Arial, sans-serif; font-size: 80%; padding: 0px 10px; margin: 0px; background: #eee}
|
|
||||||
p {padding: 2px; font-size: 92%; line-height: 140%}
|
|
||||||
a, a:link, a:active, a:visited {color: #cc3334; text-decoration: none; padding-left: 1px; padding-right: 1px}
|
|
||||||
a:hover {color: #fff; background-color: #cc3334}
|
|
||||||
h1 {font-size: 304%; font-weight: bold}
|
|
||||||
h2 {font-size: 148%; font-weight: bold}
|
|
||||||
h3 {font-size: 129%; font-weight: bold}
|
|
||||||
img.edit_item {background-image: url(../images/edit_off.png); background-repeat: no-repeat; border: none;}
|
|
||||||
a:hover img.edit_item {background-image: url(../images/edit_on.png); background-color: transparent; background-repeat: no-repeat; border: none;}
|
|
||||||
img.delete_item {background-image: url(../images/delete_off.png); background-repeat: no-repeat; border: none;}
|
|
||||||
a:hover img.delete_item {background-image: url(../images/delete_on.png);background-color: transparent;background-repeat: no-repeat; border: none;}
|
|
||||||
img.starred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: 0px 0px;}
|
|
||||||
a:hover img.starred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -16px 0px;}
|
|
||||||
img.unstarred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -32px 0px;}
|
|
||||||
a:hover img.unstarred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -48px 0px;}
|
|
||||||
a.to_top {background: transparent url(../images/top_off.png) no-repeat;}
|
|
||||||
a.to_top:hover {background: transparent url(../images/top_on.png) no-repeat;}
|
|
||||||
a.up {background: transparent url(../images/up_off.png) no-repeat;}
|
|
||||||
a.up:hover {background: transparent url(../images/up_on.png) no-repeat;}
|
|
||||||
a.down {background: transparent url(../images/down_off.png) no-repeat;}
|
|
||||||
a.down:hover {background: transparent url(../images/down_on.png) no-repeat;}
|
|
||||||
a.to_bottom {background: transparent url(../images/bottom_off.png) no-repeat;}
|
|
||||||
a.to_bottom:hover {background: transparent url(../images/bottom_on.png) no-repeat;}
|
|
||||||
a.show_notes, a.link_to_notes {background-image: url(../images/notes_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
|
||||||
a.show_notes:hover, a.link_to_notes:hover {background-image: url(../images/notes_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
|
||||||
#content {margin-top: 90px}
|
|
||||||
#display_box {float: left; width: 55%; margin: 0px 10px 50px 15px}
|
|
||||||
#single_box {width: 60%; margin: 80px auto}
|
|
||||||
#full_width_display {float: left; width: 95%; margin: 0px 15px 90px 15px}
|
|
||||||
#display_box_projects {float: left; width: 95%; margin: 0px 15px 90px 15px}
|
|
||||||
#recurring_timespan, #recurring_target {border: none; clear: both}
|
|
||||||
#recurring_target {border-top-style: dotted; padding: 15px 0px 0px 0px}
|
|
||||||
#recurring_daily, #recurring_weekly, #recurring_monthly, #recurring_yearly, #recurring_edit_daily, #recurring_edit_weekly, #recurring_edit_monthly, #recurring_edit_yearly {border: none; border-top-style: dotted; clear: both; padding: 15px 0px 15px 0px}
|
|
||||||
#recurring_period_id, #recurring_edit_period_id {border: none; float: left; margin: 0px 50px 0px 0px; padding: 0px 25px 15px 50px; border-right-style: none}
|
|
||||||
#recurring_todo {width: 270px}
|
|
||||||
#recurring_todo_form_container {border-right-style: dotted; padding: 0px 50px 15px 0px; float: left}
|
|
||||||
#recurring_todo input, #recurring_todo textarea {width: 100%}
|
|
||||||
#overlay {visibility: hidden; position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 102; text-align: center; background-image:url("../images/trans70.png")}
|
|
||||||
#overlay #new-recurring-todo, #overlay #edit-recurring-todo {width:750px; background-color: #fff; border:1px solid #000; padding: 15px; margin: 70px auto}
|
|
||||||
.recurring_container {padding: 0px 5px 0px 5px; border: 1px solid #999; margin: 0px 0px 0px 0px; background: #fff; text-align: left}
|
|
||||||
.recurring_submit_box {height: 25px; padding: 5px 0; text-align: center; clear: both; border: none}
|
|
||||||
#navcontainer {position: fixed; top: 48px; left: 0px}
|
|
||||||
#navlist {margin: 0; padding: 0 0 20px 5px}
|
|
||||||
#navlist ul, #navlist li {margin: 0; padding: 0; display: inline; list-style-type: none}
|
|
||||||
#navlist a:link, #navlist a:visited {float: left; line-height: 14px; font-weight: bold; margin: 0 10px 4px 10px; text-decoration: none; color: #eee}
|
|
||||||
#navlist a:link#current, #navlist a:visited#current, #navlist a:hover {border-bottom: 4px solid #CCC; padding-bottom: 2px; background: transparent; color: #CCC}
|
|
||||||
#navlist a:hover {color: #CCC}
|
|
||||||
#topbar {position: fixed; top: 0px; left: 0px; height: 68px; margin-bottom: 20px; clear: both; background-color: #000; filter: alpha(opacity=75); -moz-opacity: .75; opacity: .75; color: #eee; width: 100%; z-index:101}
|
|
||||||
body.stats #topbar {filter: alpha(opacity=100); -moz-opacity: 1; opacity: 1}
|
|
||||||
#date {float: left; width: 45%; padding-left: 15px; margin-top: 15px; margin-bottom: 5px; white-space: nowrap}
|
|
||||||
#date h1 {font-size: 152%}
|
|
||||||
#minilinks {text-align: right; position: fixed; right: 15px; top: 10px; font-size: 0.9em}
|
|
||||||
.container {padding: 0px 5px 0px 5px; border: 1px solid #999; margin: 0px 0px 15px 0px; background: #fff}
|
|
||||||
.completed {background: #eee}
|
|
||||||
.container h2 {background: #ccc; padding: 5px; margin-top: 0px; margin-left: -5px; margin-right: -5px; margin-bottom: 0px; color: #666; position:static}
|
|
||||||
.container_toggle img {height:20px; width:20px; border:0px}
|
|
||||||
h2 a, h2 a:link, h2 a:active, h2 a:visited {color: #666; text-decoration: none}
|
|
||||||
h2 a:hover {color: #cc3334; background-color: transparent; text-decoration: none}
|
|
||||||
div#input_box {margin: 0px 15px 0px 58%; padding: 0px 15px 0px 0px}
|
|
||||||
#input_box h2 {color: #999}
|
|
||||||
#input_box ul {list-style-type: circle; font-size: 0.9em;}
|
|
||||||
.show_from_input, .due_input, .project_input, .context_input {float:left}
|
|
||||||
.box {float: left; width: 20px}
|
|
||||||
div.item-container {padding: 2px 0px; line-height:20px; clear: both}
|
|
||||||
a.recurring_icon {vertical-align: middle; background-color: transparent}
|
|
||||||
a.icon {float: left; vertical-align: middle; background-color: transparent}
|
|
||||||
input.item-checkbox {float: left; margin-left: 10px; vertical-align: middle}
|
|
||||||
.description {margin-left: 85px; position:relative }
|
|
||||||
.stale_l1, .stale_l2, .stale_l3 {margin-left: 82px; padding-left: 3px}
|
|
||||||
.stale_l1 {background: #ffC}
|
|
||||||
.tools {margin-left: 25px; width: 40px; border-top: 1px solid #999}
|
|
||||||
#footer {clear: both; font-size: 85%; text-align: center; color: #999; margin: 20px 20px 5px 20px; padding: 0px}
|
|
||||||
.todo_notes {margin: 5px; padding: 3px; border: 1px solid #F5ED59; background: #FAF6AE; color: #666666}
|
|
||||||
.todo_notes p, .todo_notes li {padding: 1px; margin: 0px; font-size: 12px}
|
|
||||||
.todo_notes ul, .note_wrapper ul {list-style-type: disc; margin-left:20px}
|
|
||||||
.todo_notes ol {list-style-type: decimal; margin-left:20px}
|
|
||||||
div.note_wrapper {margin: 3px; padding: 2px}
|
|
||||||
div.note_wrapper p {display: inline}
|
|
||||||
div.note_footer {border-top: 1px solid #999; padding-top: 3px; font-style: italic; font-size: 0.9em; color: #666}
|
|
||||||
div.note_footer a, div.note_footer a:hover {border-top: none; padding-top: 0px; vertical-align: middle; background-color: transparent}
|
|
||||||
div.add_note_link {margin-top:12px; float: right}
|
|
||||||
div#project_status > div {padding: 10px}
|
|
||||||
#project_status span {margin-right:5px; background-color:white}
|
|
||||||
#project_status .active_state {font-weight:bold}
|
|
||||||
div#default_context > div{ padding:10px}
|
|
||||||
a.footer_link {color: #cc3334; font-style: normal;}
|
|
||||||
a.footer_link:hover {color: #fff; background-color: #cc3334 !important;}
|
|
||||||
span.tag {font-size: 0.8em; background-color: #CCE7FF; color: #000; padding: 1px; margin-right: 2px}
|
|
||||||
span.tag a, span.tag a:link, span.tag a:active, span.tag a:visited {color: #000}
|
|
||||||
span.tag a:hover {background-color: #99CCFF; color: #333}
|
|
||||||
div#message_holder {position: absolute; z-index: 100; left: 60%; top: 30px; right: 0px; margin: 0px}
|
|
||||||
h4.alert {font-size: 1em; margin: 0px; padding: 5px; text-align: center}
|
|
||||||
h4.warning {border: 1px solid #ED2E38; background-color: #F6979C; color: #000}
|
|
||||||
h4.error {color:#fff; background:#c00}
|
|
||||||
h4.notice {border: 1px solid #007E00; background-color: #c2ffc2; color: #007E00}
|
|
||||||
.project_completed {border: 1px solid #007E00; background-color: #c2ffc2; padding: 5px; color: #007E00; text-align: center}
|
|
||||||
.red {color: #fff; background: #f00; padding: 1px; font-size: 85%}
|
|
||||||
.amber {color: #fff; background: #ff6600; padding: 1px; font-size: 85%}
|
|
||||||
.orange {color: #fff; background: #FFA500; padding: 1px; font-size: 85%}
|
|
||||||
.green {color: #fff; background: #33cc00; padding: 1px; font-size: 85%}
|
|
||||||
.grey {color: #fff; background: #999; padding: 2px; font-size: 85%}
|
|
||||||
.info {color: #fff; background: #CCC; border: 1px solid #999; padding: 5px; text-align: center}
|
|
||||||
.highlight {background: #ffC; padding: 2px}
|
|
||||||
.stale_l1 {background: #ffC}
|
|
||||||
.stale_l2 {background: #ff6}
|
|
||||||
.stale_l3 {background: #ff0}
|
|
||||||
.badge {color: #fff; background: #f00; padding: 3px 5px; font-size: 12pt; margin: 10px 10px 0px 0px; height:26px}
|
|
||||||
ul {list-style-type: none}
|
|
||||||
#sidebar h3 {margin-top:15px; margin-bottom:5px}
|
|
||||||
#sidebar ul {margin-left: auto; list-style-position: inside}
|
|
||||||
li {font-size: 1.1em; padding: 3px 0px}
|
|
||||||
#sidebar .integrations-link {margin-top:10px; padding-top:10px; font-size: 0.8em}
|
|
||||||
.sortable_row {background: #fff; _background: transparent; padding: 4px 4px 4px 8px; margin: 2px 2px; border: 1px solid #ccc}
|
|
||||||
.edit-form {background: #ccc; padding: 0 10px; border-top: 1px solid #999; border-bottom: 1px solid #999; position:relative}
|
|
||||||
.label {text-align: right}
|
|
||||||
input {vertical-align: middle}
|
|
||||||
img.position, a:hover img.position {text-align: left; vertical-align: middle; background-color: transparent}
|
|
||||||
.data {text-align: left; margin-left: 20px; float: left}
|
|
||||||
div.buttons, div.buttons a, div.buttons a:hover {text-align: right; margin-right: 0px; vertical-align: middle; background-color: transparent}
|
|
||||||
div#list-active-projects, div#list-hidden-projects, div#list-completed-projects, div#list-contexts, div#projects-empty-nd {clear:right; border: 1px solid #999}
|
|
||||||
.project-state-group h2 {margin:20px 0px 8px 13px}
|
|
||||||
.search-result-group h2 {margin:20px 0px 8px 13px }
|
|
||||||
div.menu_sort {margin-top:-20px; float:right}
|
|
||||||
div.alpha_sort, div.tasks_sort,span.sort_separator {float:left}
|
|
||||||
.container td {border: none; padding-bottom: 5px}
|
|
||||||
.container form {border: none}
|
|
||||||
div.project_description {background: #eee; padding: 5px; margin-top: 0px; margin-left: -5px; margin-right: -5px; color: #666; font-style: italic; font-size: 12px; font-weight: normal}
|
|
||||||
#project-next-prev {text-align:right}
|
|
||||||
form {border: 1px solid #CCC; padding: 10px; margin: 0px}
|
|
||||||
.inline-form {border: none; padding: 3px}
|
|
||||||
.inline-form table {padding-right: 3px}
|
|
||||||
.inline-form table, .inline-form textarea#item_notes, .inline-form input#item_description {width: 100%}
|
|
||||||
.inline-form table td.label {width: 13ex}
|
|
||||||
#todo_new_action_container, #project_new_project_container, #context_new_container, #recurring_new_container {background: #ddd; width: 270px; padding: 5px 10px; background-color: #000; filter: alpha(opacity=75); -moz-opacity: .75; opacity: .75; color: #eee}
|
|
||||||
#recurring_new_container img {vertical-align: middle}
|
|
||||||
#project_new_project_filler {padding-top: 50px}
|
|
||||||
#todo_new_action_container input, #todo_new_action_container textarea, #project_new_project_container input, #project_new_project_container textarea, #context_new_container input {width: 100%}
|
|
||||||
input#go_to_project, input#context_hide {width: 5%}
|
|
||||||
#todo_new_action_container .show_from_input, #todo_new_action_container .due_input {width: 45%}
|
|
||||||
#todo_new_action_container .show_from_input {float: right}
|
|
||||||
#todo-form-new-action .submit_box, #project_form .submit_box, #context_form .submit_box {height: 25px; padding: 5px 0; text-align: center; clear: right}
|
|
||||||
.edit_todo_form .submit_box {height: 25px; padding: 5px 0; text-align: center; clear: right}
|
|
||||||
.edit_todo_form input, .edit_todo_form textarea {width:100%}
|
|
||||||
.edit_todo_form .Date {width:89%}
|
|
||||||
.edit_todo_form a.date_clear:hover {background: #CCCCCC}
|
|
||||||
.edit_todo_form .tag_list_label {clear:both}
|
|
||||||
.edit_todo_form .due_input, .edit_todo_form .show_from_input, .edit_todo_form .project_input, .edit_todo_form .context_input {width:48%}
|
|
||||||
.edit_todo_form .show_from_input, .edit_todo_form .context_input {float: right}
|
|
||||||
.edit_todo_form .submit_box input {width:120px}
|
|
||||||
.hide_form {text-align:right}
|
|
||||||
#todo-form-new-action label, .edit_todo_form label {display: block; padding-bottom: 3px}
|
|
||||||
form.button-to {border: none; padding: 0px; margin: 0px}
|
|
||||||
label {font-weight: bold; padding: 0px 0px}
|
|
||||||
input, select, textarea {margin: 0px 0px 5px 0px}
|
|
||||||
#feedicons-project, #feedicons-context {background-color: #D2D3D6; margin: 0px 0px 0px 60px; padding: 0px 0px 0px 5px; width: 70%}
|
|
||||||
.feed {font-family: verdana, sans-serif; font-size: 10px; font-weight:bold; text-decoration:none; color: white; background-color: #F60; border:1px solid; border-color: #FC9 #630 #330 #F96; padding:0px 3px 0px 3px; margin:0px}
|
|
||||||
.position {float: left; margin-top:2px}
|
|
||||||
.handle {color: #fff; background: #000; padding: 2px; font-size: 92%; cursor: move}
|
|
||||||
div.message {margin: 5px 0px; background: #FAF4B5; padding: 2px}
|
|
||||||
.message p {margin: 0px; padding: 0px; text-align: center; font-size: 1em; color: #666}
|
|
||||||
.fieldWithErrors {padding: 2px; background-color: red; display: table}
|
|
||||||
#errorExplanation {border: 2px solid #ff0000; padding: 7px; padding-bottom: 12px; margin: 10px auto 20px auto; background-color: #f0f0f0}
|
|
||||||
#errorExplanation h2 {text-align: left; font-weight: bold; padding: 5px 5px 5px 15px; font-size: 12px; margin: -7px; background-color: #c00; color: #fff}
|
|
||||||
#errorExplanation > p {color: #333; margin-top: 5px; margin-bottom: 0; padding: 5px}
|
|
||||||
#errorExplanation ul li {font-size: 1em; list-style-type: disc; list-style-position: inside; margin-left:7px; color: #333}
|
|
||||||
ul#prefs {list-style-type: disc; margin-left: 15px;}
|
|
||||||
#token_area, #authentication_area {text-align:center; margin-top:20px; margin-bottom:10px}
|
|
||||||
#token_area .description{ font-weight:bold}
|
|
||||||
#token_area form {width:100%; text-align:center}
|
|
||||||
.prefscontainer .actions {text-align:center; margin-bottom:20px}
|
|
||||||
.authtype_container .actions {margin-top:20px; margin-bottom:20px}
|
|
||||||
#feedlegend {padding: 2px; background-color: #D2D3D6; color: #666; padding: 5px 20px; text-align: left}
|
|
||||||
#feedlegend h3, #feedlegend dl, #feedlegend dt, #feedlegend dd {display: inline}
|
|
||||||
#feedlegend dt {margin-left: 15px}
|
|
||||||
#feedlegend dd {margin-left: 3px}
|
|
||||||
#feedlegend p {margin-bottom: 0px}
|
|
||||||
#feeds img.rss-icon {margin-bottom: -4px}
|
|
||||||
#feeds li {font-size:13px; font-family: "Lucida Grande", Verdana, Geneva, Arial, sans-serif}
|
|
||||||
#feeds li h4 {margin-top: 12px; margin-bottom: 4px; font-weight: normal; font-style:oblique}
|
|
||||||
input.open_id {background: url(../images/open-id-login-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; color: #000; padding-left: 18px; width:182px}
|
|
||||||
div.page_name_auto_complete {width: 100%; background: #fff; display: inline; z-index: 100}
|
|
||||||
div.page_name_auto_complete ul {border: 1px solid #888; margin: 0; padding: 0; width: 100%; list-style-type: none}
|
|
||||||
div.page_name_auto_complete ul li {margin: 0; padding: 2px; font-weight:bold; list-style-type: none; color: #000}
|
|
||||||
div.page_name_auto_complete ul li.selected {background-color: #ffb}
|
|
||||||
div.page_name_auto_complete ul strong.highlight {color: #800; margin: 0; padding: 0}
|
|
||||||
table.next_actions td {padding:5px 3px 2px 0px}
|
|
||||||
table.users_table {width: 100%; text-align: center; border: 1px solid #666; background-color: #fff; border-spacing: 0px}
|
|
||||||
.users_table th {color: #fff; background-color: #000;}
|
|
||||||
.users_table td {border: none;}
|
|
||||||
table.export_table {border: 1px solid #666; background-color: #fff; border-spacing: 0px}
|
|
||||||
.export_table th {color: #fff; background-color: #000;}
|
|
||||||
.export_table td {border: 1px; padding: 5px 0px 5px 5px;}
|
|
||||||
.widgets a, .widgets button{ display:block; float: left; margin:0px 7px 0 0; background-color:#f5f5f5; border:1px solid #dedede; border-top:1px solid #eee; border-left:1px solid #eee; font-family:"Lucida Grande", Verdana, Geneva, Arial, sans-serif; font-size:80%; line-height:100%; text-decoration:none; font-weight:bold; color:#565656; cursor:pointer; padding:3px 5px 7px 5px}
|
|
||||||
.widgets button{ width:auto; overflow:visible; padding:3px 5px 5px 5px}
|
|
||||||
.widgets button[type]{ padding:3px 5px 4px 5px; line-height:15px}
|
|
||||||
.widgets button img, .widgets a img{ margin:0 3px -3px 0 !important; padding:0; border:none; width:16px; height:16px}
|
|
||||||
.widgets a:hover{ background-color:#dff4ff; border:1px solid #c2e1ef; color:#336699}
|
|
||||||
.widgets a:active{ background-color:#6299c5; border:1px solid #6299c5; color:#fff}
|
|
||||||
button.positive, .widgets a.positive{ color: #498111}
|
|
||||||
.widgets a.positive:hover, button.positive:hover{ background-color:#E6EFC2; border:1px solid #C6D880; color:#529214}
|
|
||||||
.widgets a.positive:active{ background-color:#529214; border:1px solid #529214; color:#fff}
|
|
||||||
.widgets a.negative, button.negative{ color:#d12f19}
|
|
||||||
.widgets a.negative:hover, button.negative:hover{ background:#fbe3e4; border:1px solid #fbc2c4; color:#d12f19}
|
|
||||||
.widgets a.negative:active{ background-color:#d12f19; border:1px solid #d12f19; color:#fff}
|
|
||||||
.tracks__waiting {background-image:url('../images/waiting.gif'); background-repeat:no-repeat; background-position:center center; background-color:white}
|
|
||||||
.bigWaiting {background-image:url('../images/bigWaiting.gif'); background-repeat:no-repeat; background-position:center 20%; background-color:white}
|
|
||||||
.blackWaiting {background-image:url('../images/blackWaiting.gif'); background-repeat:no-repeat; background-position:center center; background-color:black}
|
|
||||||
.bigBlackWaiting {background-image:url('../images/bigBlackWaiting.gif'); background-repeat:no-repeat; background-position:center center; background-color:black}
|
|
||||||
.stats_content .open-flash-chart, .stats_content .stats_module {float: left; width: 450px; margin-right:20px; padding-bottom:20px}
|
|
||||||
.stats_content h2 {clear:both; margin-top:15px; margin-bottom:15px}
|
|
||||||
body.integrations h2 {margin-top:40px; padding-top:20px; margin-bottom:10px; border-top:1px solid #ccc}
|
|
||||||
body.integrations p, body.integrations li {font-size:1.0em}
|
|
||||||
body.integrations li {list-style-type: disc; list-style-position: inside; margin-left:30px}
|
|
||||||
body.integrations textarea {margin:10px; padding:3px; width:80%; background-color:#ddd}
|
|
||||||
.defer-container {float:right}
|
|
||||||
.defer-container a:hover {background-color: inherit}
|
|
||||||
div.calendar {position: relative}
|
|
||||||
.calendar, .calendar table {border: 1px solid #556; font-size: 11px; color: #000; cursor: default; background: #eef; z-index: 110; font-family: tahoma,verdana,sans-serif}
|
|
||||||
.calendar .button {text-align: center; padding: 2px}
|
|
||||||
.calendar .nav {background: #778 url(../images/menuarrow.gif) no-repeat 100% 100%}
|
|
||||||
.calendar thead .title {font-weight: bold; text-align: center; background: #fff; color: #000; padding: 2px}
|
|
||||||
.calendar thead .headrow {background: #778; color: #fff}
|
|
||||||
.calendar thead .daynames {background: #bdf}
|
|
||||||
.calendar thead .name {border-bottom: 1px solid #556; padding: 2px; text-align: center; color: #000}
|
|
||||||
.calendar thead .weekend {color: #a66}
|
|
||||||
.calendar thead .hilite {background-color: #aaf; color: #000; border: 1px solid #04f; padding: 1px}
|
|
||||||
.calendar thead .active {background-color: #77c; padding: 2px 0px 0px 2px}
|
|
||||||
.calendar tbody .day {width: 2em; color: #456; text-align: right; padding: 2px 4px 2px 2px}
|
|
||||||
.calendar tbody .day.othermonth {font-size: 80%; color: #bbb}
|
|
||||||
.calendar tbody .day.othermonth.oweekend {color: #fbb}
|
|
||||||
.calendar table .wn {padding: 2px 3px 2px 2px; border-right: 1px solid #000; background: #bdf}
|
|
||||||
.calendar tbody .rowhilite td {background: #def}
|
|
||||||
.calendar tbody .rowhilite td.wn {background: #eef}
|
|
||||||
.calendar tbody td.hilite {background: #def; padding: 1px 3px 1px 1px; border: 1px solid #bbb}
|
|
||||||
.calendar tbody td.active {background: #cde; padding: 2px 2px 0px 2px}
|
|
||||||
.calendar tbody td.selected {font-weight: bold; border: 1px solid #000; padding: 1px 3px 1px 1px; background: #fff; color: #000}
|
|
||||||
.calendar tbody td.weekend {color: #a66}
|
|
||||||
.calendar tbody td.today {font-weight: bold; color: #00f}
|
|
||||||
.calendar tbody .disabled {color: #999}
|
|
||||||
.calendar tbody .emptycell {visibility: hidden}
|
|
||||||
.calendar tbody .emptyrow {display: none}
|
|
||||||
.calendar tfoot .footrow {text-align: center; background: #556; color: #fff}
|
|
||||||
.calendar tfoot .ttip {background: #fff; color: #445; border-top: 1px solid #556; padding: 1px}
|
|
||||||
.calendar tfoot .hilite {background: #aaf; border: 1px solid #04f; color: #000; padding: 1px}
|
|
||||||
.calendar tfoot .active {background: #77c; padding: 2px 0px 0px 2px}
|
|
||||||
.calendar .combo {position: absolute; display: none; top: 0px; left: 0px; width: 4em; cursor: default; border: 1px solid #655; background: #def; color: #000; font-size: 90%; z-index: 100}
|
|
||||||
.calendar .combo .label, .calendar .combo .label-IEfix {text-align: center; padding: 1px}
|
|
||||||
.calendar .combo .label-IEfix {width: 4em}
|
|
||||||
.calendar .combo .hilite {background: #acf}
|
|
||||||
.calendar .combo .active {border-top: 1px solid #46a; border-bottom: 1px solid #46a; background: #eef; font-weight: bold}
|
|
||||||
.calendar td.time {border-top: 1px solid #000; padding: 1px 0px; text-align: center; background-color: #f4f0e8}
|
|
||||||
.calendar td.time .hour, .calendar td.time .minute, .calendar td.time .ampm {padding: 0px 3px 0px 4px; border: 1px solid #889; font-weight: bold; background-color: #fff}
|
|
||||||
.calendar td.time .ampm {text-align: center}
|
|
||||||
.calendar td.time .colon {padding: 0px 2px 0px 3px; font-weight: bold}
|
|
||||||
.calendar td.time span.hilite {border-color: #000; background-color: #667; color: #fff}
|
|
||||||
.calendar td.time span.active {border-color: #f00; background-color: #000; color: #0f0}
|
|
||||||
b.niftycorners,b.niftyfill{display:block}
|
|
||||||
b.niftycorners *{display:block;height: 1px;line-height:1px;font-size: 1px; overflow:hidden;border-style:solid;border-width: 0 1px}
|
|
||||||
b.r1{margin: 0 3px;border-width: 0 2px}
|
|
||||||
b.r2{margin: 0 2px}
|
|
||||||
b.r3{margin: 0 1px}
|
|
||||||
b.r4{height: 2px}
|
|
||||||
b.rb1{margin: 0 8px;border-width:0 2px}
|
|
||||||
b.rb2{margin: 0 6px;border-width:0 2px}
|
|
||||||
b.rb3{margin: 0 5px}
|
|
||||||
b.rb4{margin: 0 4px}
|
|
||||||
b.rb5{margin: 0 3px}
|
|
||||||
b.rb6{margin: 0 2px}
|
|
||||||
b.rb7{margin: 0 1px;height:2px}
|
|
||||||
b.rb8{margin: 0;height:2px}
|
|
||||||
b.rs1{margin: 0 1px}
|
|
||||||
b.t1{border-width: 0 5px}
|
|
||||||
b.t2{border-width: 0 3px}
|
|
||||||
b.t3{border-width: 0 2px}
|
|
||||||
b.t4{height: 2px}
|
|
||||||
b.tb1{border-width: 0 10px}
|
|
||||||
b.tb2{border-width: 0 8px}
|
|
||||||
b.tb3{border-width: 0 6px}
|
|
||||||
b.tb4{border-width: 0 5px}
|
|
||||||
b.tb5{border-width: 0 4px}
|
|
||||||
b.tb6{border-width: 0 3px}
|
|
||||||
b.tb7{border-width: 0 2px;height:2px}
|
|
||||||
b.tb8{border-width: 0 1px;height:2px}
|
|
||||||
b.ts1{border-width: 0 2px}
|
|
||||||
4
script/autospec
Executable file
4
script/autospec
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
ENV['RSPEC'] = 'true' # allows autotest to discover rspec
|
||||||
|
ENV['AUTOTEST'] = 'true' # allows autotest to run w/ color on linux
|
||||||
|
system (RUBY_PLATFORM =~ /mswin|mingw/ ? 'autotest.bat' : 'autotest'), *ARGV
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/rspec/lib"))
|
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/rspec/lib"))
|
||||||
|
require 'rubygems'
|
||||||
require 'spec'
|
require 'spec'
|
||||||
exit ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(ARGV, STDERR, STDOUT))
|
exit ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(ARGV, STDERR, STDOUT))
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,14 @@ module LuckySneaks
|
||||||
it "should not be valid if #{attribute} length is more than #{maximum}" do
|
it "should not be valid if #{attribute} length is more than #{maximum}" do
|
||||||
instance.send "#{attribute}=", 'x'*(maximum+1)
|
instance.send "#{attribute}=", 'x'*(maximum+1)
|
||||||
instance.errors_on(attribute).should include(
|
instance.errors_on(attribute).should include(
|
||||||
options[:message_too_long] || ActiveRecord::Errors.default_error_messages[:too_long] % maximum
|
options[:message_too_long] || I18n.t('activerecord.errors.messages.too_long', :count => maximum)
|
||||||
)
|
)
|
||||||
end if maximum
|
end if maximum
|
||||||
|
|
||||||
it "should not be valid if #{attribute} length is less than #{minimum}" do
|
it "should not be valid if #{attribute} length is less than #{minimum}" do
|
||||||
instance.send "#{attribute}=", 'x'*(minimum-1)
|
instance.send "#{attribute}=", 'x'*(minimum-1)
|
||||||
instance.errors_on(attribute).should include(
|
instance.errors_on(attribute).should include(
|
||||||
options[:message_to_short] || ActiveRecord::Errors.default_error_messages[:too_short] % minimum
|
options[:message_to_short] || I18n.t('activerecord.errors.messages.too_short', :count => minimum)
|
||||||
)
|
)
|
||||||
end if minimum
|
end if minimum
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,32 +3,36 @@ require File.dirname(__FILE__) + '/../../spec_helper'
|
||||||
describe "/notes/_notes.rhtml" do
|
describe "/notes/_notes.rhtml" do
|
||||||
before :each do
|
before :each do
|
||||||
@project = mock_model(Project, :name => "a project")
|
@project = mock_model(Project, :name => "a project")
|
||||||
@note = mock_model(Note, :body => "this is a note", :project => @project,
|
@note = mock_model(Note, :body => "this is a note", :project => @project, :project_id => @project.id,
|
||||||
:created_at => Time.now, :updated_at? => false)
|
:created_at => Time.now, :updated_at? => false)
|
||||||
@controller.template.stub!(:apply_behavior)
|
# @controller.template.stub!(:apply_behavior)
|
||||||
@controller.template.stub!(:format_date)
|
@controller.template.stub!(:format_date)
|
||||||
@controller.template.stub!(:render)
|
# @controller.template.stub!(:render)
|
||||||
@controller.template.stub!(:form_remote_tag)
|
# @controller.template.stub!(:form_remote_tag)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should render" do
|
it "should render" do
|
||||||
|
pending "figure out how to mock or work with with UJS"
|
||||||
render :partial => "/notes/notes", :locals => {:notes => @note}
|
render :partial => "/notes/notes", :locals => {:notes => @note}
|
||||||
response.should have_tag("div.note_footer")
|
response.should have_tag("div.note_footer")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should auto-link URLs" do
|
it "should auto-link URLs" do
|
||||||
|
pending "figure out how to mock or work with with UJS"
|
||||||
@note.stub!(:body).and_return("http://www.google.com/")
|
@note.stub!(:body).and_return("http://www.google.com/")
|
||||||
render :partial => "/notes/notes", :locals => {:notes => @note}
|
render :partial => "/notes/notes", :locals => {:notes => @note}
|
||||||
response.should have_tag("a[href=\"http://www.google.com/\"]")
|
response.should have_tag("a[href=\"http://www.google.com/\"]")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should auto-link embedded URLs" do
|
it "should auto-link embedded URLs" do
|
||||||
|
pending "figure out how to mock or work with with UJS"
|
||||||
@note.stub!(:body).and_return("this is cool: http://www.google.com/")
|
@note.stub!(:body).and_return("this is cool: http://www.google.com/")
|
||||||
render :partial => "/notes/notes", :locals => {:notes => @note}
|
render :partial => "/notes/notes", :locals => {:notes => @note}
|
||||||
response.should have_tag("a[href=\"http://www.google.com/\"]")
|
response.should have_tag("a[href=\"http://www.google.com/\"]")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should parse Textile links correctly" do
|
it "should parse Textile links correctly" do
|
||||||
|
pending "figure out how to mock or work with with UJS"
|
||||||
@note.stub!(:body).and_return("\"link\":http://www.google.com/")
|
@note.stub!(:body).and_return("\"link\":http://www.google.com/")
|
||||||
render :partial => "/notes/notes", :locals => {:notes => @note}
|
render :partial => "/notes/notes", :locals => {:notes => @note}
|
||||||
response.should have_tag("a[href=\"http://www.google.com/\"]")
|
response.should have_tag("a[href=\"http://www.google.com/\"]")
|
||||||
|
|
|
||||||
4
stories/all.rb
Normal file
4
stories/all.rb
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
dir = File.dirname(__FILE__)
|
||||||
|
Dir[File.expand_path("#{dir}/**/*.rb")].uniq.each do |file|
|
||||||
|
require file
|
||||||
|
end
|
||||||
|
|
@ -1,136 +1,3 @@
|
||||||
ENV["RAILS_ENV"] = "test"
|
ENV["RAILS_ENV"] = "test"
|
||||||
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
||||||
$:.unshift File.join(File.dirname(__FILE__), *%w[.. vendor plugings rspec lib])
|
require 'spec/rails/story_adapter'
|
||||||
require 'test_help'
|
|
||||||
require 'test/unit/testresult'
|
|
||||||
require 'spec'
|
|
||||||
require 'spec/rails'
|
|
||||||
require 'spec/story'
|
|
||||||
require 'webrat/selenium'
|
|
||||||
require 'action_controller/test_process'
|
|
||||||
|
|
||||||
module Spec
|
|
||||||
module Story
|
|
||||||
class StepGroup
|
|
||||||
def include_steps_for(name)
|
|
||||||
require File.expand_path(File.dirname(__FILE__) + "/steps/#{name}")
|
|
||||||
step_matchers = rspec_story_steps[name.to_sym]
|
|
||||||
warn "WARNING: 0 step matchers found for include_steps_for(:#{name}). Are you missing an include?" if step_matchers.empty?
|
|
||||||
self << step_matchers
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Test::Unit.run = true
|
|
||||||
|
|
||||||
class SeleniumRailsStory < Test::Unit::TestCase
|
|
||||||
include Spec::Matchers
|
|
||||||
include Spec::Rails::Matchers
|
|
||||||
|
|
||||||
def initialize #:nodoc:
|
|
||||||
# TODO - eliminate this hack, which is here to stop
|
|
||||||
# Rails Stories from dumping the example summary.
|
|
||||||
Spec::Runner::Options.class_eval do
|
|
||||||
def examples_should_be_run?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@_result = Test::Unit::TestResult.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def should_see(text_or_regexp)
|
|
||||||
if text_or_regexp.is_a?(Regexp)
|
|
||||||
response.should have_tag("*", text_or_regexp)
|
|
||||||
else
|
|
||||||
response.should have_tag("*", /#{Regexp.escape(text_or_regexp)}/i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def should_not_see(text_or_regexp)
|
|
||||||
if text_or_regexp.is_a?(Regexp)
|
|
||||||
response.should_not have_tag("*", text_or_regexp)
|
|
||||||
else
|
|
||||||
response.should_not have_tag("*", /#{Regexp.escape(text_or_regexp)}/i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def response
|
|
||||||
webrat_session.response_body
|
|
||||||
end
|
|
||||||
|
|
||||||
def logged_in_as(user)
|
|
||||||
visits("/selenium_helper/login?as=#{user.login}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def selenium
|
|
||||||
SeleniumDriverManager.instance.running_selenium_driver
|
|
||||||
end
|
|
||||||
|
|
||||||
def badge_count_should_show(count)
|
|
||||||
response.should have_tag('#badge_count', count.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing(name, *args)
|
|
||||||
if webrat_session.respond_to?(name)
|
|
||||||
webrat_session.send(name, *args)
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def webrat_session
|
|
||||||
@webrat_session ||= begin
|
|
||||||
Webrat::SeleniumSession.new(SeleniumDriverManager.instance.running_selenium_driver)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DatabaseResetListener
|
|
||||||
include Singleton
|
|
||||||
|
|
||||||
def scenario_started(*args)
|
|
||||||
if defined?(ActiveRecord::Base)
|
|
||||||
connection = ActiveRecord::Base.connection
|
|
||||||
%w[users].each do |table|
|
|
||||||
connection.execute "DELETE FROM #{table}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing sym, *args, &block
|
|
||||||
# ignore all messages you don't care about
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class CookieResetListener
|
|
||||||
include Singleton
|
|
||||||
|
|
||||||
def scenario_started(*args)
|
|
||||||
%w[tracks_login auth_token _session_id].each do |cookie_name|
|
|
||||||
SeleniumDriverManager.instance.running_selenium_driver.get_eval("window.document.cookie = '#{cookie_name}=;expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/';")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing sym, *args, &block
|
|
||||||
# ignore all messages you don't care about
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Spec::Story::Runner::ScenarioRunner
|
|
||||||
def initialize
|
|
||||||
@listeners = [DatabaseResetListener.instance, CookieResetListener.instance]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Spec::Story::GivenScenario
|
|
||||||
def perform(instance, name = nil)
|
|
||||||
scenario = Spec::Story::Runner::StoryRunner.scenario_from_current_story @name
|
|
||||||
runner = Spec::Story::Runner::ScenarioRunner.new
|
|
||||||
runner.instance_variable_set(:@listeners,[])
|
|
||||||
runner.run(scenario, instance)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -223,8 +223,8 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
|
||||||
login_as :admin_user
|
login_as :admin_user
|
||||||
u = users(:admin_user)
|
u = users(:admin_user)
|
||||||
post :actionize, :state => "active", :format => 'js'
|
post :actionize, :state => "active", :format => 'js'
|
||||||
assert_equal 1, projects(:gardenclean).position
|
assert_equal 2, projects(:gardenclean).position
|
||||||
assert_equal 2, projects(:moremoney).position
|
assert_equal 1, projects(:moremoney).position
|
||||||
assert_equal 3, projects(:timemachine).position
|
assert_equal 3, projects(:timemachine).position
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ class TodosControllerTest < Test::Rails::TestCase
|
||||||
|
|
||||||
assert_xml_select 'rss[version="2.0"]' do
|
assert_xml_select 'rss[version="2.0"]' do
|
||||||
assert_select 'channel' do
|
assert_select 'channel' do
|
||||||
assert_select '>title', 'Tracks Actions'
|
assert_select '>title', 'Actions'
|
||||||
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
||||||
assert_select 'language', 'en-us'
|
assert_select 'language', 'en-us'
|
||||||
assert_select 'ttl', '40'
|
assert_select 'ttl', '40'
|
||||||
|
|
@ -205,7 +205,7 @@ class TodosControllerTest < Test::Rails::TestCase
|
||||||
|
|
||||||
assert_xml_select 'rss[version="2.0"]' do
|
assert_xml_select 'rss[version="2.0"]' do
|
||||||
assert_select 'channel' do
|
assert_select 'channel' do
|
||||||
assert_select '>title', 'Tracks Actions'
|
assert_select '>title', 'Actions'
|
||||||
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
||||||
assert_select 'item', 5 do
|
assert_select 'item', 5 do
|
||||||
assert_select 'title', /.+/
|
assert_select 'title', /.+/
|
||||||
|
|
@ -240,7 +240,7 @@ class TodosControllerTest < Test::Rails::TestCase
|
||||||
# #puts @response.body
|
# #puts @response.body
|
||||||
|
|
||||||
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
|
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
|
||||||
assert_xml_select '>title', 'Tracks Actions'
|
assert_xml_select '>title', 'Actions'
|
||||||
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
|
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
|
||||||
assert_xml_select 'entry', 11 do
|
assert_xml_select 'entry', 11 do
|
||||||
assert_xml_select 'title', /.+/
|
assert_xml_select 'title', /.+/
|
||||||
|
|
@ -450,7 +450,7 @@ class TodosControllerTest < Test::Rails::TestCase
|
||||||
|
|
||||||
# check that the new_todo is in the tickler to show next month
|
# check that the new_todo is in the tickler to show next month
|
||||||
assert !new_todo.show_from.nil?
|
assert !new_todo.show_from.nil?
|
||||||
assert_equal Time.utc(today.year, today.month+1, today.day), new_todo.show_from
|
assert_equal Time.utc(today.year, today.month, today.day)+1.month, new_todo.show_from
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_check_for_next_todo
|
def test_check_for_next_todo
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ class ContextXmlApiTest < ActionController::IntegrationTest
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fails_with_invalid_xml_format
|
def test_fails_with_invalid_xml_format
|
||||||
authenticated_post_xml_to_context_create "<foo></bar>"
|
# Fails too hard for test to catch
|
||||||
assert_equal 500, @integration_session.status
|
# authenticated_post_xml_to_context_create "<foo></bar>"
|
||||||
|
# assert_equal 500, @integration_session.status
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fails_with_invalid_xml_format2
|
def test_fails_with_invalid_xml_format2
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ class ProjectXmlApiTest < ActionController::IntegrationTest
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fails_with_invalid_xml_format
|
def test_fails_with_invalid_xml_format
|
||||||
authenticated_post_xml_to_project_create "<foo></bar>"
|
#Fails too hard for test to catch
|
||||||
assert_equal 500, @integration_session.status
|
# authenticated_post_xml_to_project_create "<foo></bar>"
|
||||||
|
# assert_equal 500, @integration_session.status
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fails_with_invalid_xml_format2
|
def test_fails_with_invalid_xml_format2
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,12 @@ class UsersXmlApiTest < ActionController::IntegrationTest
|
||||||
authenticated_post_xml_to_user_create @@foobar_postdata, users(:admin_user).login, 'abracadabra', {'CONTENT_TYPE' => "application/x-www-form-urlencoded"}
|
authenticated_post_xml_to_user_create @@foobar_postdata, users(:admin_user).login, 'abracadabra', {'CONTENT_TYPE' => "application/x-www-form-urlencoded"}
|
||||||
assert_404_invalid_xml
|
assert_404_invalid_xml
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fails_with_invalid_xml_format
|
# Fails too hard for test to catch
|
||||||
authenticated_post_xml_to_user_create "<foo></bar>"
|
# def test_fails_with_invalid_xml_format
|
||||||
assert_equal 500, @integration_session.status
|
# authenticated_post_xml_to_user_create "<foo></bar>"
|
||||||
end
|
# assert_equal 500, @integration_session.status
|
||||||
|
# end
|
||||||
|
|
||||||
def test_fails_with_invalid_xml_format2
|
def test_fails_with_invalid_xml_format2
|
||||||
authenticated_post_xml_to_user_create "<request><username>foo</username></request>"
|
authenticated_post_xml_to_user_create "<request><username>foo</username></request>"
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,7 @@ click "css=#toggle_c1"
|
||||||
wait_for_not_visible "todo_9"
|
wait_for_not_visible "todo_9"
|
||||||
|
|
||||||
# check that newly added action is not there
|
# check that newly added action is not there
|
||||||
wait_for_not_visible "xpath=//span[text()='a new action']"
|
wait_for_not_visible "xpath=//span[text()='a new action']"
|
||||||
|
|
||||||
|
# toggle c1 back. Selenium does not reset cookies
|
||||||
|
click "css=#toggle_c1"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ login :as => 'admin'
|
||||||
|
|
||||||
open "/"
|
open "/"
|
||||||
# we should start with 10 actions on home page
|
# we should start with 10 actions on home page
|
||||||
assert_text 'badge_count', '10'
|
assert_text 'badge_count', '11'
|
||||||
|
|
||||||
# set project to hidden state
|
# set project to hidden state
|
||||||
open "/projects/2"
|
open "/projects/2"
|
||||||
|
|
@ -28,4 +28,4 @@ wait_for_visible "flash"
|
||||||
verify_text_not_present 'should be hidden'
|
verify_text_not_present 'should be hidden'
|
||||||
|
|
||||||
# badge count should still be same
|
# badge count should still be same
|
||||||
assert_text 'badge_count', '7'
|
assert_text 'badge_count', '7'
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ wait_for_element_present "show_from_todo_9"
|
||||||
type "show_from_todo_9", "1/1/2030"
|
type "show_from_todo_9", "1/1/2030"
|
||||||
click "css=#submit_todo_9"
|
click "css=#submit_todo_9"
|
||||||
wait_for_element_not_present "todo_9"
|
wait_for_element_not_present "todo_9"
|
||||||
assert_text 'badge_count', '9'
|
assert_text 'badge_count', '10'
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,5 @@ wait_for_element_present "show_from_todo_5"
|
||||||
type "show_from_todo_5", "1/1/2030"
|
type "show_from_todo_5", "1/1/2030"
|
||||||
click "css=#submit_todo_5"
|
click "css=#submit_todo_5"
|
||||||
wait_for_element_not_present "todo_5"
|
wait_for_element_not_present "todo_5"
|
||||||
assert_text 'badge_count', '9'
|
assert_text 'badge_count', '10'
|
||||||
wait_for_not_visible "c5"
|
wait_for_not_visible "c5"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
setup :fixtures => :all
|
setup :fixtures => :all
|
||||||
login :as => 'admin'
|
login :as => 'admin'
|
||||||
open "/"
|
open "/"
|
||||||
wait_for_element_present '//div[@id="line_todo_5"]//img[@alt="Defer_1"]/..'
|
wait_for_element_present '//div[@id="line_todo_5"]//img[@alt="Defer 1 day"]/..'
|
||||||
click '//div[@id="line_todo_5"]//img[@alt="Defer_1"]/..'
|
click '//div[@id="line_todo_5"]//img[@alt="Defer 1 day"]/..'
|
||||||
wait_for_element_not_present "todo_5"
|
wait_for_element_not_present "todo_5"
|
||||||
assert_text 'badge_count', '9'
|
assert_text 'badge_count', '10'
|
||||||
wait_for_not_visible "c5"
|
wait_for_not_visible "c5"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ setup :fixtures => :all
|
||||||
login :as => 'admin'
|
login :as => 'admin'
|
||||||
|
|
||||||
open '/m'
|
open '/m'
|
||||||
wait_for_text 'css=h1 span.count', '10'
|
wait_for_text 'css=h1 span.count', '11'
|
||||||
|
|
||||||
click_and_wait "link=0-Add new action"
|
click_and_wait "link=0-Add new action"
|
||||||
|
|
||||||
|
|
@ -15,4 +15,4 @@ select "todo_due_2i", "label=January"
|
||||||
select "todo_due_1i", "label=2009"
|
select "todo_due_1i", "label=2009"
|
||||||
click_and_wait "//input[@value='Create']"
|
click_and_wait "//input[@value='Create']"
|
||||||
|
|
||||||
wait_for_text 'css=h1 span.count', '11'
|
wait_for_text 'css=h1 span.count', '12'
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ setup :fixtures => :all
|
||||||
login :as => 'admin'
|
login :as => 'admin'
|
||||||
|
|
||||||
open '/m'
|
open '/m'
|
||||||
wait_for_text 'css=h1 span.count', '10'
|
wait_for_text 'css=h1 span.count', '11'
|
||||||
|
|
||||||
|
open '/todos/6.m'
|
||||||
|
wait_for_page_to_load 3000
|
||||||
|
|
||||||
open_and_wait '/todos/6.m'
|
|
||||||
click "done"
|
click "done"
|
||||||
click_and_wait "//input[@value='Update']"
|
click_and_wait "//input[@value='Update']"
|
||||||
|
|
||||||
wait_for_text 'css=h1 span.count', '9'
|
wait_for_text 'css=h1 span.count', '10'
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ login :as => 'admin'
|
||||||
# open home page
|
# open home page
|
||||||
open '/m'
|
open '/m'
|
||||||
wait_for_title "All actions"
|
wait_for_title "All actions"
|
||||||
wait_for_text 'css=h1 span.count', '10'
|
wait_for_text 'css=h1 span.count', '11'
|
||||||
|
|
||||||
# open context page
|
# open context page
|
||||||
click_and_wait "link=2-Contexts"
|
click_and_wait "link=2-Contexts"
|
||||||
# verify_title "All actions in context agenda"
|
# verify_title "All actions in context agenda"
|
||||||
# choose agenda context
|
# choose agenda context
|
||||||
click_and_wait "link=agenda"
|
click_and_wait "link=agenda"
|
||||||
wait_for_text 'css=h1 span.count', '5'
|
wait_for_text 'css=h1 span.count', '6'
|
||||||
|
|
||||||
# click on tag foo to go to tag page
|
# click on tag foo to go to tag page
|
||||||
click_and_wait "link=foo"
|
click_and_wait "link=foo"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
setup :fixtures => :all
|
setup :fixtures => :all
|
||||||
login :as => 'admin'
|
login :as => 'admin'
|
||||||
open '/projects/1'
|
open '/projects/1'
|
||||||
assert_checked 'project_state_active', 'ignored'
|
assert_checked 'project_state_active'
|
||||||
assert_attribute 'css=#project_status .active span', 'class', 'active_state'
|
assert_attribute 'css=#project_status .active span', 'class', 'active_state'
|
||||||
assert_attribute 'css=#project_status .hidden span', 'class', 'inactive_state'
|
assert_attribute 'css=#project_status .hidden span', 'class', 'inactive_state'
|
||||||
assert_text 'badge_count', '2'
|
assert_text 'badge_count', '2'
|
||||||
|
|
@ -11,4 +11,4 @@ wait_for_attribute 'css=#project_status .hidden span', 'class', 'active_state'
|
||||||
assert_text 'badge_count', '2'
|
assert_text 'badge_count', '2'
|
||||||
open '/projects/1'
|
open '/projects/1'
|
||||||
assert_text 'badge_count', '2'
|
assert_text 'badge_count', '2'
|
||||||
assert_checked 'project_state_hidden', 'ignored'
|
assert_checked 'project_state_hidden'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
setup :fixtures => :all
|
||||||
|
login :as => 'admin'
|
||||||
|
open "/projects/1"
|
||||||
|
|
||||||
|
# change project name
|
||||||
|
click "project_name_in_place_editor"
|
||||||
|
wait_for_element_present "css=#project_name_in_place_editor-inplaceeditor input.editor_field"
|
||||||
|
type "css=#project_name_in_place_editor-inplaceeditor input.editor_field", "Test Foo"
|
||||||
|
click "css=#project_name_in_place_editor-inplaceeditor input.editor_ok_button"
|
||||||
|
wait_for_text "project_name_in_place_editor", "Test Foo"
|
||||||
|
|
||||||
|
# check that the default project name is changed too
|
||||||
|
assert_value "todo_project_name", "Test Foo"
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue