diff --git a/.gitignore b/.gitignore index 0b625b2e..bafc8e6c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /config/configuration.yml /config/database.yml /config/email.yml +/config/setup_load_paths.rb /config/initializers/session_store.rb /coverage /db/*.db diff --git a/Gemfile b/Gemfile index 904673e4..6ec56e28 100644 --- a/Gemfile +++ b/Gemfile @@ -1,11 +1,14 @@ +# -*- coding: utf-8 -*- source :rubygems gem "rails", "2.3.14" -gem "coderay", "~> 0.9.7" +gem "coderay", "~> 1.0.0" gem "i18n", "~> 0.4.2" gem "rubytree", "~> 0.5.2", :require => 'tree' gem "rdoc", ">= 2.4.2" +gem "liquid", "~> 2.3.0" +gem "acts-as-taggable-on", "= 2.1.0" # Needed only on RUBY_VERSION = 1.8, ruby 1.9+ compatible interpreters should bring their csv gem "fastercsv", "~> 1.5.0", :platforms => [:ruby_18, :jruby, :mingw_18] diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 0b63d0f9..66a0294b 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -19,6 +19,10 @@ class AdminController < ApplicationController include SortHelper + menu_item :projects, :only => [:projects] + menu_item :plugins, :only => [:plugins] + menu_item :info, :only => [:info] + def index @no_configuration_data = Redmine::DefaultData::Loader::no_data? end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6e8dc46e..c5200d58 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -65,6 +65,9 @@ class ApplicationController < ActionController::Base filter_parameter_logging :password rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token + # FIXME: This doesn't work with Rails >= 3.0 anymore + # Possible workaround: https://github.com/rails/rails/issues/671#issuecomment-1780159 + rescue_from ActionController::RoutingError, :with => proc{render_404} include Redmine::Search::Controller include Redmine::MenuManager::MenuController @@ -75,8 +78,6 @@ class ApplicationController < ActionController::Base end def user_setup - # Check the settings cache for each request - Setting.check_cache # Find the current user User.current = find_current_user end diff --git a/app/controllers/auto_completes_controller.rb b/app/controllers/auto_completes_controller.rb index 36fd4f2e..ad138666 100644 --- a/app/controllers/auto_completes_controller.rb +++ b/app/controllers/auto_completes_controller.rb @@ -13,7 +13,8 @@ #++ class AutoCompletesController < ApplicationController - before_filter :find_project + before_filter :find_project, :only => :issues + before_filter :require_admin, :only => :projects def issues @issues = [] @@ -33,6 +34,38 @@ class AutoCompletesController < ApplicationController render :layout => false end + def users + if params[:remove_group_members].present? + @group = Group.find(params[:remove_group_members]) + @removed_users = @group.users + end + + if params[:remove_watchers].present? && params[:klass].present? + watcher_class = params[:klass].constantize + if watcher_class.included_modules.include?(Redmine::Acts::Watchable) # check class is a watching class + @object = watcher_class.find(params[:remove_watchers]) + @removed_users = @object.watcher_users + end + end + + @removed_users ||= [] + + if params[:include_groups] + user_finder = Principal + else + user_finder = User + end + + @users = user_finder.active.like(params[:q]).find(:all, :limit => 100) - @removed_users + render :layout => false + end + + def projects + @principal = Principal.find(params[:id]) + @projects = Project.active.like(params[:q]).find(:all, :limit => 100) - @principal.projects + render :layout => false + end + private def find_project diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index 56bddc28..b0e35ad0 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -44,19 +44,32 @@ class DocumentsController < ApplicationController def new @document = @project.documents.build(params[:document]) - if request.post? and @document.save - attachments = Attachment.attach_files(@document, params[:attachments]) - render_attachment_warning_if_needed(@document) - flash[:notice] = l(:notice_successful_create) - redirect_to :action => 'index', :project_id => @project + if request.post? + if User.current.allowed_to?(:add_document_watchers, @project) && params[:document]['watcher_user_ids'].present? + @document.watcher_user_ids = params[:document]['watcher_user_ids'] + end + + if @document.save + attachments = Attachment.attach_files(@document, params[:attachments]) + render_attachment_warning_if_needed(@document) + flash[:notice] = l(:notice_successful_create) + redirect_to :action => 'index', :project_id => @project + end end end def edit @categories = DocumentCategory.all - if request.post? and @document.update_attributes(params[:document]) - flash[:notice] = l(:notice_successful_update) - redirect_to :action => 'show', :id => @document + + if request.post? + if User.current.allowed_to?(:add_document_watchers, @project) && params[:document]['watcher_user_ids'].present? + @document.watcher_user_ids = params[:document]['watcher_user_ids'] + end + + if @document.update_attributes(params[:document]) + flash[:notice] = l(:notice_successful_update) + redirect_to :action => 'show', :id => @document + end end end @@ -69,7 +82,12 @@ class DocumentsController < ApplicationController attachments = Attachment.attach_files(@document, params[:attachments]) render_attachment_warning_if_needed(@document) - Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added') + if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added') + # TODO: refactor + attachments.first.container.recipients.each do |recipient| + Mailer.deliver_attachments_added(attachments[:files], recipient) + end + end redirect_to :action => 'show', :id => @document end diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 2608ae9e..8966fed9 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -42,7 +42,11 @@ class FilesController < ApplicationController render_attachment_warning_if_needed(container) if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added') - Mailer.deliver_attachments_added(attachments[:files]) + # TODO: refactor + recipients = attachments[:files].first.container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail} + recipients.each do |recipient| + Mailer.deliver_attachments_added(attachments[:files], recipient) + end end redirect_to project_files_path(@project) end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 6097b4cf..901b4e30 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -126,16 +126,19 @@ class GroupsController < ApplicationController end end - def autocomplete_for_user - @group = Group.find(params[:id]) - @users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100) - render :layout => false - end - def edit_membership @group = Group.find(params[:id]) - @membership = Member.edit_membership(params[:membership_id], params[:membership], @group) - @membership.save if request.post? + + if params[:project_ids] # Multiple memberships, one per project + params[:project_ids].each do |project_id| + @membership = Member.edit_membership(params[:membership_id], (params[:membership]|| {}).merge(:project_id => project_id), @group) + @membership.save if request.post? + end + else # Single membership + @membership = Member.edit_membership(params[:membership_id], params[:membership], @group) + @membership.save if request.post? + end + respond_to do |format| if @membership.valid? format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' } diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb index cb248971..8bf2fe3c 100644 --- a/app/controllers/journals_controller.rb +++ b/app/controllers/journals_controller.rb @@ -12,6 +12,8 @@ # See doc/COPYRIGHT.rdoc for more details. #++ +require 'diff' + class JournalsController < ApplicationController before_filter :find_journal, :only => [:edit, :diff] before_filter :find_issue, :only => [:new] @@ -84,6 +86,22 @@ class JournalsController < ApplicationController end end + def diff + if valid_field?(params[:field]) + from = @journal.changes[params[:field]][0] + to = @journal.changes[params[:field]][1] + + @diff = Redmine::Helpers::Diff.new(to, from) + @issue = @journal.journaled + respond_to do |format| + format.html { } + format.js { render :layout => false } + end + else + render_404 + end + end + private def find_journal @@ -100,4 +118,9 @@ class JournalsController < ApplicationController rescue ActiveRecord::RecordNotFound render_404 end + + # Is this a valid field for diff'ing? + def valid_field?(field) + field.to_s.strip == "description" + end end diff --git a/app/controllers/ldap_auth_sources_controller.rb b/app/controllers/ldap_auth_sources_controller.rb index 71bc7f30..dc586600 100644 --- a/app/controllers/ldap_auth_sources_controller.rb +++ b/app/controllers/ldap_auth_sources_controller.rb @@ -14,6 +14,7 @@ class LdapAuthSourcesController < AuthSourcesController + menu_item :ldap_authentication, :only => [:index] protected def auth_source_class diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index f7b50e87..39df123f 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -110,7 +110,7 @@ class MessagesController < ApplicationController content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> " content << text.to_s.strip.gsub(%r{
((.|\s)*?)
}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n" render(:update) { |page| - page << "$('reply_subject').value = \"#{subject}\";" + page << "$('message_subject').value = \"#{subject}\";" page.<< "$('message_content').value = \"#{content}\";" page.show 'reply' page << "Form.Element.focus('message_content');" diff --git a/app/controllers/queries_controller.rb b/app/controllers/queries_controller.rb index fdfedbbd..f301c469 100644 --- a/app/controllers/queries_controller.rb +++ b/app/controllers/queries_controller.rb @@ -20,6 +20,7 @@ class QueriesController < ApplicationController def new @query = Query.new(params[:query]) @query.project = params[:query_is_for_all] ? nil : @project + @query.display_subprojects = params[:display_subprojects] if params[:display_subprojects].present? @query.user = User.current @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin? diff --git a/app/controllers/time_entry_reports_controller.rb b/app/controllers/time_entry_reports_controller.rb index 18eebd41..ba9b7833 100644 --- a/app/controllers/time_entry_reports_controller.rb +++ b/app/controllers/time_entry_reports_controller.rb @@ -72,8 +72,7 @@ class TimeEntryReportsController < ApplicationController @periods = [] # Date#at_beginning_of_ not supported in Rails 1.2.x date_from = @from.to_time - # 100 columns max - while date_from <= @to.to_time && @periods.length < 100 + while date_from <= @to.to_time case @columns when 'year' @periods << "#{date_from.year}" @@ -161,6 +160,9 @@ class TimeEntryReportsController < ApplicationController @available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id", :klass => Project, :label => :label_project}, + 'status' => {:sql => "#{Issue.table_name}.status_id", + :klass => IssueStatus, + :label => :field_status}, 'version' => {:sql => "#{Issue.table_name}.fixed_version_id", :klass => Version, :label => :label_version}, diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f4fb70d2..4dc95652 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -197,8 +197,16 @@ class UsersController < ApplicationController def edit_membership - @membership = Member.edit_membership(params[:membership_id], params[:membership], @user) - @membership.save if request.post? + if params[:project_ids] # Multiple memberships, one per project + params[:project_ids].each do |project_id| + @membership = Member.edit_membership(params[:membership_id], (params[:membership] || {}).merge(:project_id => project_id), @user) + @membership.save if request.post? + end + else # Single membership + @membership = Member.edit_membership(params[:membership_id], params[:membership], @user) + @membership.save if request.post? + end + respond_to do |format| if @membership.valid? format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } diff --git a/app/controllers/watchers_controller.rb b/app/controllers/watchers_controller.rb index e6b4625e..8b6e414c 100644 --- a/app/controllers/watchers_controller.rb +++ b/app/controllers/watchers_controller.rb @@ -16,6 +16,7 @@ class WatchersController < ApplicationController before_filter :find_project before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch] before_filter :authorize, :only => [:new, :destroy] + before_filter :authorize_access_to_object, :only => [:new, :destroy] verify :method => :post, :only => [ :watch, :unwatch ], @@ -34,9 +35,12 @@ class WatchersController < ApplicationController end def new - @watcher = Watcher.new(params[:watcher]) - @watcher.watchable = @watched - @watcher.save if request.post? + params[:user_ids].each do |user_id| + @watcher = Watcher.new((params[:watcher] || {}).merge({:user_id => user_id})) + @watcher.watchable = @watched + @watcher.save if request.post? + end if params[:user_ids].present? + respond_to do |format| format.html { redirect_to :back } format.js do @@ -50,7 +54,7 @@ class WatchersController < ApplicationController end def destroy - @watched.set_watcher(User.find(params[:user_id]), false) if request.post? + @watched.set_watcher(Principal.find(params[:user_id]), false) if request.post? respond_to do |format| format.html { redirect_to :back } format.js do @@ -94,4 +98,24 @@ private rescue ::ActionController::RedirectBackError render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true end + + def authorize_access_to_object + permission = '' + case params[:action] + when 'new' + permission << 'add_' + when 'destroy' + permission << 'delete_' + end + + # Ends up like: :delete_wiki_page_watchers + permission << "#{@watched.class.name.underscore}_watchers" + + if User.current.allowed_to?(permission.to_sym, @project) + return true + else + deny_access + end + end + end diff --git a/app/drops/base_drop.rb b/app/drops/base_drop.rb new file mode 100644 index 00000000..5204b92a --- /dev/null +++ b/app/drops/base_drop.rb @@ -0,0 +1,37 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class BaseDrop < Liquid::Drop + def initialize(object) + @object = object unless object.respond_to?(:visible?) && !object.visible? + end + + # Defines a Liquid method on the drop that is allowed to call the + # Ruby method directly. Best used for attributes. + # + # Based on Module#liquid_methods + def self.allowed_methods(*allowed_methods) + class_eval do + allowed_methods.each do |sym| + define_method sym do + if @object.respond_to?(:public_send) + @object.public_send(sym) rescue nil + else + @object.send(sym) rescue nil + end + end + end + end + end +end diff --git a/app/drops/issue_drop.rb b/app/drops/issue_drop.rb new file mode 100644 index 00000000..bdec63cd --- /dev/null +++ b/app/drops/issue_drop.rb @@ -0,0 +1,79 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class IssueDrop < BaseDrop + allowed_methods :id + allowed_methods :subject + allowed_methods :description + allowed_methods :project + allowed_methods :tracker + allowed_methods :status + allowed_methods :due_date + allowed_methods :category + allowed_methods :assigned_to + allowed_methods :priority + allowed_methods :fixed_version + allowed_methods :author + allowed_methods :created_on + allowed_methods :updated_on + allowed_methods :start_date + allowed_methods :done_ratio + allowed_methods :estimated_hours + allowed_methods :parent + + def custom_field(name) + return '' unless name.present? + custom_field = IssueCustomField.find_by_name(name.strip) + return '' unless custom_field.present? + custom_value = @object.custom_value_for(custom_field) + if custom_value.present? + return custom_value.value + else + return '' + end + end + + # TODO: both required, method_missing for Ruby and before_method for Liquid + + # Allows accessing custom fields by their name: + # + # - issue.the_name_of_player => CustomField(:name => "The name Of Player") + # + def before_method(method_sym) + if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym) + custom_field(custom_field_with_matching_name.name) + else + super + end + end + + # Allows accessing custom fields by their name: + # + # - issue.the_name_of_player => CustomField(:name => "The name Of Player") + # + def method_missing(method_sym, *arguments, &block) + if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym) + custom_field(custom_field_with_matching_name.name) + else + super + end + end + +private + def has_custom_field_with_matching_name?(method_sym) + custom_field_with_matching_name = @object.available_custom_fields.detect {|custom_field| + custom_field.name.downcase.underscore.gsub(' ','_') == method_sym.to_s + } + end +end diff --git a/app/drops/issue_status_drop.rb b/app/drops/issue_status_drop.rb new file mode 100644 index 00000000..30d0cf85 --- /dev/null +++ b/app/drops/issue_status_drop.rb @@ -0,0 +1,17 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class IssueStatusDrop < BaseDrop + allowed_methods :name +end diff --git a/app/drops/principal_drop.rb b/app/drops/principal_drop.rb new file mode 100644 index 00000000..7751a478 --- /dev/null +++ b/app/drops/principal_drop.rb @@ -0,0 +1,17 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class PrincipalDrop < BaseDrop + allowed_methods :name +end diff --git a/app/drops/project_drop.rb b/app/drops/project_drop.rb new file mode 100644 index 00000000..9d8dd345 --- /dev/null +++ b/app/drops/project_drop.rb @@ -0,0 +1,17 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class ProjectDrop < BaseDrop + allowed_methods :name, :identifier +end diff --git a/app/drops/tracker_drop.rb b/app/drops/tracker_drop.rb new file mode 100644 index 00000000..e5671ef1 --- /dev/null +++ b/app/drops/tracker_drop.rb @@ -0,0 +1,17 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class TrackerDrop < BaseDrop + allowed_methods :name +end diff --git a/app/drops/wiki_page_drop.rb b/app/drops/wiki_page_drop.rb new file mode 100644 index 00000000..7c1fc597 --- /dev/null +++ b/app/drops/wiki_page_drop.rb @@ -0,0 +1,17 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class WikiPageDrop < BaseDrop + allowed_methods :title +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e3573ac4..7503437c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -16,7 +16,6 @@ require 'forwardable' require 'cgi' module ApplicationHelper - include Redmine::WikiFormatting::Macros::Definitions include Redmine::I18n include GravatarHelper::PublicMethods @@ -224,17 +223,15 @@ module ApplicationHelper end # Renders the project quick-jump box - def render_project_jump_box - projects = User.current.memberships.collect(&:project).compact.uniq + def render_project_jump_box(projects = [], html_options = {}) + projects ||= User.current.memberships.collect(&:project).compact.uniq if projects.any? - s = '' - s + # option_tags = content_tag :option, l(:label_jump_to_a_project), :value => "" + option_tags = (content_tag :option, "", :value => "" ) + option_tags << project_tree_options_for_select(projects, :selected => @project) do |p| + { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) } + end + select_tag "", option_tags, html_options.merge({ :onchange => "if (this.value != \'\') { window.location = this.value; }" }) end end @@ -288,7 +285,15 @@ module ApplicationHelper def principals_check_box_tags(name, principals) s = '' principals.sort.each do |principal| - s << "\n" + s << "\n" + end + s + end + + def projects_check_box_tags(name, projects) + s = '' + projects.each do |project| + s << "\n" end s end @@ -389,7 +394,9 @@ module ApplicationHelper end def page_header_title - if @project.nil? || @project.new_record? + if @page_header_title.present? + h(@page_header_title) + elsif @project.nil? || @project.new_record? h(Setting.app_title) else b = [] @@ -429,8 +436,8 @@ module ApplicationHelper css << 'theme-' + theme.name end - css << 'controller-' + params[:controller] - css << 'action-' + params[:action] + css << 'controller-' + params[:controller] if params[:controller] + css << 'action-' + params[:action] if params[:action] css.join(' ') end @@ -447,19 +454,51 @@ module ApplicationHelper case args.size when 1 obj = options[:object] - text = args.shift + input_text = args.shift when 2 obj = args.shift attr = args.shift - text = obj.send(attr).to_s + input_text = obj.send(attr).to_s else raise ArgumentError, 'invalid arguments to textilizable' end - return '' if text.blank? + return '' if input_text.blank? project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil) only_path = options.delete(:only_path) == false ? false : true - text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) } + begin + text = ChiliProject::Liquid::Legacy.run_macros(input_text) + liquid_template = ChiliProject::Liquid::Template.parse(text) + liquid_variables = get_view_instance_variables_for_liquid + liquid_variables.merge!({'current_user' => User.current}) + liquid_variables.merge!({'toc' => '{{toc}}'}) # Pass toc through to replace later + liquid_variables.merge!(ChiliProject::Liquid::Variables.all) + + # Pass :view in a register so this view (with helpers) can be used inside of a tag + text = liquid_template.render(liquid_variables, :registers => {:view => self, :object => obj, :attribute => attr}) + + # Add Liquid errors to the log + if Rails.logger && Rails.logger.debug? + msg = "" + liquid_template.errors.each do |exception| + msg << "[Liquid Error] #{exception.message}\n:\n#{exception.backtrace.join("\n")}" + msg << "\n\n" + end + Rails.logger.debug msg + end + rescue Liquid::SyntaxError => exception + msg = "[Liquid Syntax Error] #{exception.message}" + if Rails.logger && Rails.logger.debug? + log_msg = "#{msg}\n" + log_msg << exception.backtrace.collect{ |str| " #{str}" }.join("\n") + log_msg << "\n\n" + Rails.logger.debug log_msg + end + + # Skip Liquid if there is a syntax error + text = content_tag(:div, msg, :class => "flash error") + text << h(input_text) + end @parsed_headings = [] text = parse_non_pre_blocks(text) do |text| @@ -712,7 +751,7 @@ module ApplicationHelper end end - TOC_RE = /

\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE) + TOC_RE = /

\{%\s*toc(_right|_left)?\s*%\}<\/p>/i unless const_defined?(:TOC_RE) # Renders the TOC with given headings def replace_toc(text, headings) @@ -720,10 +759,14 @@ module ApplicationHelper if headings.empty? '' else - div_class = 'toc' - div_class << ' right' if $1 == '>' - div_class << ' left' if $1 == '<' - out = "

' + out << '' end end end @@ -879,6 +923,12 @@ module ApplicationHelper # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe ') def avatar(user, options = { }) if Setting.gravatar_enabled? + if user.is_a?(Group) + size = options[:size] || 50 + size = "#{size}x#{size}" # image_tag uses WxH + options[:class] ||= 'gravatar' + return image_tag("group.png", options.merge(:size => size)) + end options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default}) email = nil if user.respond_to?(:mail) @@ -935,6 +985,72 @@ module ApplicationHelper end end + # Expands the current menu item using JavaScript based on the params + def expand_current_menu + current_menu_class = + case + when params[:controller] == "timelog" + "reports" + when params[:controller] == 'projects' && params[:action] == 'changelog' + "reports" + when params[:controller] == 'issues' && ['calendar','gantt'].include?(params[:action]) + "reports" + when params[:controller] == 'projects' && params[:action] == 'roadmap' + 'roadmap' + when params[:controller] == 'versions' && params[:action] == 'show' + 'roadmap' + when params[:controller] == 'projects' && params[:action] == 'settings' + 'settings' + when params[:controller] == 'contracts' || params[:controller] == 'deliverables' + 'contracts' + else + params[:controller] + end + + + javascript_tag("jQuery.menu_expand({ menuItem: '.#{current_menu_class}' });") + end + + # Menu items for the main top menu + def main_top_menu_items + split_top_menu_into_main_or_more_menus[:main] + end + + # Menu items for the more top menu + def more_top_menu_items + split_top_menu_into_main_or_more_menus[:more] + end + + def help_menu_item + split_top_menu_into_main_or_more_menus[:help] + end + + # Split the :top_menu into separate :main and :more items + def split_top_menu_into_main_or_more_menus + unless @top_menu_split + items_for_main_level = [] + items_for_more_level = [] + help_menu = nil + menu_items_for(:top_menu) do |item| + if item.name == :home || item.name == :my_page + items_for_main_level << item + elsif item.name == :help + help_menu = item + elsif item.name == :projects + # Remove, present in layout + else + items_for_more_level << item + end + end + @top_menu_split = { + :main => items_for_main_level, + :more => items_for_more_level, + :help => help_menu + } + end + @top_menu_split + end + private def wiki_helper @@ -946,4 +1062,20 @@ module ApplicationHelper def link_to_content_update(text, url_params = {}, html_options = {}) link_to(text, url_params, html_options) end + + def get_view_instance_variables_for_liquid + internal_variables = %w{ + @output_buffer @cookies @helpers @real_format @assigns_added @assigns + @view_paths @controller + } + self.instance_variables.collect(&:to_s).reject do |ivar| + ivar.match(/^@_/) || # Rails "internal" variables: @_foo + ivar.match(/^@template/) || + internal_variables.include?(ivar) + end.inject({}) do |acc,ivar| + acc[ivar.sub('@','')] = instance_variable_get(ivar) + acc + end + end + end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 20c4100a..d8d6cd00 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -52,13 +52,14 @@ module IssuesHelper "#{@cached_label_priority}: #{h(issue.priority.name)}" end + # TODO: deprecate and/or remove def render_issue_subject_with_tree(issue) s = '' ancestors = issue.root? ? [] : issue.ancestors.all ancestors.each do |ancestor| - s << '
' + content_tag('p', link_to_issue(ancestor)) + s << '
' + content_tag('h2', link_to_issue(ancestor)) end - s << '
' + content_tag('h3', h(issue.subject)) + s << '
' + content_tag('h2', h(issue.subject)) s << '
' * (ancestors.size + 1) s end diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb index 895ce2f4..66ebc27c 100644 --- a/app/helpers/journals_helper.rb +++ b/app/helpers/journals_helper.rb @@ -27,24 +27,34 @@ module JournalsHelper def render_journal(model, journal, options = {}) return "" if journal.initial? - journal_content = render_journal_details(journal, :label_updated_time_by) - journal_content += render_notes(model, journal, options) unless journal.notes.blank? - content_tag "div", journal_content, { :id => "change-#{journal.id}", :class => journal.css_classes } + + journal_classes = journal.css_classes + journal_content = render_journal_details(journal, :label_updated_time_by, model, options) + + avatar = avatar(journal.user, :size => "40") + unless avatar.blank? + profile_wrap = content_tag("div", avatar, {:class => "profile-wrap"}, false) + journal_content = profile_wrap + journal_content + journal_classes << " has-avatar" + end + + content_tag("div", journal_content, :id => "change-#{journal.id}", :class => journal_classes) end - # This renders a journal entry wiht a header and details - def render_journal_details(journal, header_label = :label_updated_time_by) + # This renders a journal entry with a header and details + def render_journal_details(journal, header_label = :label_updated_time_by, model=nil, options={}) header = <<-HTML

-
#{link_to "##{journal.anchor}", :anchor => "note-#{journal.anchor}"}
- #{avatar(journal.user, :size => "24")} - #{content_tag('a', '', :name => "note-#{journal.anchor}")} + #{authoring journal.created_at, journal.user, :label => header_label} + #{content_tag('a', '', :name => "note-#{journal.anchor}")}

HTML + header << render_notes(model, journal, options) unless journal.notes.blank? + if journal.details.any? - details = content_tag "ul", :class => "details" do + details = content_tag "ul", :class => "journal-attributes details" do journal.details.collect do |detail| if d = journal.render_detail(detail) content_tag("li", d) @@ -53,7 +63,7 @@ module JournalsHelper end end - content_tag("div", "#{header}#{details}", :id => "change-#{journal.id}", :class => "journal") + content_tag "div", "#{header}#{details}", :class => "journal-details" end def render_notes(model, journal, options={}) diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 92a236eb..446ac0be 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -84,11 +84,12 @@ module QueriesHelper end end @query.group_by = params[:group_by] + @query.display_subprojects = params[:display_subprojects] if params[:display_subprojects] @query.column_names = params[:c] || (params[:query] && params[:query][:column_names]) - session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names} + session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :display_subprojects => @query.display_subprojects} else @query = Query.find_by_id(session[:query][:id]) if session[:query][:id] - @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names]) + @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names], :display_subprojects => session[:query][:display_subprojects]) @query.project = @project end end diff --git a/app/models/document.rb b/app/models/document.rb index 7e17997c..1438370f 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -24,6 +24,7 @@ class Document < ActiveRecord::Base end) acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project + acts_as_watchable validates_presence_of :project, :title, :category validates_length_of :title, :maximum => 60 @@ -48,4 +49,10 @@ class Document < ActiveRecord::Base end @updated_on end + + def recipients + mails = super # from acts_as_event + mails += watcher_recipients + mails.uniq + end end diff --git a/app/models/document_observer.rb b/app/models/document_observer.rb index 9f68ad15..68c508c3 100644 --- a/app/models/document_observer.rb +++ b/app/models/document_observer.rb @@ -14,6 +14,10 @@ class DocumentObserver < ActiveRecord::Observer def after_create(document) - Mailer.deliver_document_added(document) if Setting.notified_events.include?('document_added') + if Setting.notified_events.include?('document_added') + document.recipients.each do |recipient| + Mailer.deliver_document_added(document, recipient) + end + end end end diff --git a/app/models/group.rb b/app/models/group.rb index f78f0162..7c88a7b1 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -22,6 +22,11 @@ class Group < Principal validates_uniqueness_of :lastname, :case_sensitive => false validates_length_of :lastname, :maximum => 30 + # Returns an array of all of the email addresses of the group's users + def mails + users.collect(&:mail) + end + def to_s lastname.to_s end diff --git a/app/models/issue.rb b/app/models/issue.rb index 446c92e3..2f0d350e 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -103,6 +103,10 @@ class Issue < ActiveRecord::Base (usr || User.current).allowed_to?(:view_issues, self.project) end + def to_liquid + IssueDrop.new(self) + end + def after_initialize if new_record? # set default values for new records only diff --git a/app/models/issue_observer.rb b/app/models/issue_observer.rb index ea1a592d..a0dfe8bc 100644 --- a/app/models/issue_observer.rb +++ b/app/models/issue_observer.rb @@ -17,7 +17,9 @@ class IssueObserver < ActiveRecord::Observer def after_create(issue) if self.send_notification - Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added') + (issue.recipients + issue.watcher_recipients).uniq.each do |recipient| + Mailer.deliver_issue_add(issue, recipient) + end end clear_notification end diff --git a/app/models/issue_status.rb b/app/models/issue_status.rb index 409fe67b..fe9b44fb 100644 --- a/app/models/issue_status.rb +++ b/app/models/issue_status.rb @@ -28,6 +28,10 @@ class IssueStatus < ActiveRecord::Base IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default? end + def to_liquid + IssueStatusDrop.new(self) + end + # Returns the default status for new issues def self.default find(:first, :conditions =>["is_default=?", true]) diff --git a/app/models/journal_observer.rb b/app/models/journal_observer.rb index d2aa6e5b..7ea174ff 100644 --- a/app/models/journal_observer.rb +++ b/app/models/journal_observer.rb @@ -27,11 +27,15 @@ class JournalObserver < ActiveRecord::Observer if journal.initial? if Setting.notified_events.include?('wiki_content_added') - Mailer.deliver_wiki_content_added(wiki_content) + (wiki_content.recipients + wiki_page.wiki.watcher_recipients).uniq.each do |recipient| + Mailer.deliver_wiki_content_added(wiki_content, recipient) + end end else if Setting.notified_events.include?('wiki_content_updated') - Mailer.deliver_wiki_content_updated(wiki_content) + (wiki_content.recipients + wiki_page.wiki.watcher_recipients + wiki_page.watcher_recipients).uniq.each do |recipient| + Mailer.deliver_wiki_content_updated(wiki_content, recipient) + end end end end @@ -43,7 +47,10 @@ class JournalObserver < ActiveRecord::Observer (Setting.notified_events.include?('issue_note_added') && journal.notes.present?) || (Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) || (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?) - Mailer.deliver_issue_edit(journal) + issue = journal.issue + (issue.recipients + issue.watcher_recipients).uniq.each do |recipient| + Mailer.deliver_issue_edit(journal, recipient) + end end end diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb index 03b814ff..560a3ded 100644 --- a/app/models/mail_handler.rb +++ b/app/models/mail_handler.rb @@ -69,6 +69,7 @@ class MailHandler < ActionMailer::Base else # Default behaviour, emails from unknown users are ignored logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" if logger && logger.info + Mailer.deliver_mail_handler_unauthorized_action(user, email.subject.to_s, :to => sender_email) if Setting.mail_handler_confirmation_on_failure return false end end @@ -102,12 +103,15 @@ class MailHandler < ActionMailer::Base rescue ActiveRecord::RecordInvalid => e # TODO: send a email to the user logger.error e.message if logger + Mailer.deliver_mail_handler_missing_information(user, email.subject.to_s, e.message) if Setting.mail_handler_confirmation_on_failure false rescue MissingInformation => e logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger + Mailer.deliver_mail_handler_missing_information(user, email.subject.to_s, e.message) if Setting.mail_handler_confirmation_on_failure false rescue UnauthorizedAction => e logger.error "MailHandler: unauthorized attempt from #{user}" if logger + Mailer.deliver_mail_handler_unauthorized_action(user, email.subject.to_s) if Setting.mail_handler_confirmation_on_failure false end @@ -141,6 +145,7 @@ class MailHandler < ActionMailer::Base issue.save! add_attachments(issue) logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info + Mailer.deliver_mail_handler_confirmation(issue, user, issue.subject) if Setting.mail_handler_confirmation_on_success issue end @@ -162,6 +167,7 @@ class MailHandler < ActionMailer::Base add_attachments(issue) issue.save! logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info + Mailer.deliver_mail_handler_confirmation(issue.last_journal, user, email.subject) if Setting.mail_handler_confirmation_on_success issue.last_journal end @@ -190,6 +196,7 @@ class MailHandler < ActionMailer::Base reply.board = message.board message.children << reply add_attachments(reply) + Mailer.deliver_mail_handler_confirmation(message, user, reply.subject) if Setting.mail_handler_confirmation_on_success reply else logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" if logger && logger.info diff --git a/app/models/mailer.rb b/app/models/mailer.rb index aaf3228a..5da7317c 100644 --- a/app/models/mailer.rb +++ b/app/models/mailer.rb @@ -30,20 +30,19 @@ class Mailer < ActionMailer::Base { :host => h, :protocol => Setting.protocol } end - # Builds a tmail object used to email recipients of the added issue. + # Builds a tmail object used to email a recipient of the added issue. # # Example: - # issue_add(issue) => tmail object - # Mailer.deliver_issue_add(issue) => sends an email to issue recipients - def issue_add(issue) + # issue_add(issue, 'user@example.com') => tmail object + # Mailer.deliver_issue_add(issue, 'user@example.com') => sends an email to 'user@example.com' + def issue_add(issue, recipient) redmine_headers 'Project' => issue.project.identifier, 'Issue-Id' => issue.id, 'Issue-Author' => issue.author.login, 'Type' => "Issue" redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to message_id issue - recipients issue.recipients - cc(issue.watcher_recipients - @recipients) + recipients [recipient] subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}" body :issue => issue, :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue) @@ -53,9 +52,9 @@ class Mailer < ActionMailer::Base # Builds a tmail object used to email recipients of the edited issue. # # Example: - # issue_edit(journal) => tmail object - # Mailer.deliver_issue_edit(journal) => sends an email to issue recipients - def issue_edit(journal) + # issue_edit(journal, 'user@example.com') => tmail object + # Mailer.deliver_issue_edit(journal, 'user@example.com') => sends an email to issue recipients + def issue_edit(journal, recipient) issue = journal.journaled.reload redmine_headers 'Project' => issue.project.identifier, 'Issue-Id' => issue.id, @@ -65,9 +64,7 @@ class Mailer < ActionMailer::Base message_id journal references issue @author = journal.user - recipients issue.recipients - # Watchers in cc - cc(issue.watcher_recipients - @recipients) + recipients [recipient] s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] " s << "(#{issue.status.name}) " if journal.details['status_id'] s << issue.subject @@ -93,12 +90,12 @@ class Mailer < ActionMailer::Base # Builds a tmail object used to email users belonging to the added document's project. # # Example: - # document_added(document) => tmail object - # Mailer.deliver_document_added(document) => sends an email to the document's project recipients - def document_added(document) + # document_added(document, 'test@example.com') => tmail object + # Mailer.deliver_document_added(document, 'test@example.com') => sends an email to the document's project recipients + def document_added(document, recipient) redmine_headers 'Project' => document.project.identifier, 'Type' => "Document" - recipients document.recipients + recipients [recipient] subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}" body :document => document, :document_url => url_for(:controller => 'documents', :action => 'show', :id => document) @@ -110,7 +107,7 @@ class Mailer < ActionMailer::Base # Example: # attachments_added(attachments) => tmail object # Mailer.deliver_attachments_added(attachments) => sends an email to the project's recipients - def attachments_added(attachments) + def attachments_added(attachments, recipient) container = attachments.first.container added_to = '' added_to_url = '' @@ -118,16 +115,14 @@ class Mailer < ActionMailer::Base when 'Project' added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container) added_to = "#{l(:label_project)}: #{container}" - recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail} when 'Version' added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project) added_to = "#{l(:label_version)}: #{container.name}" - recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail} when 'Document' added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id) added_to = "#{l(:label_document)}: #{container.title}" - recipients container.recipients end + recipients [recipient] redmine_headers 'Project' => container.project.identifier, 'Type' => "Attachment" subject "[#{container.project.name}] #{l(:label_attachment_new)}" @@ -142,11 +137,11 @@ class Mailer < ActionMailer::Base # Example: # news_added(news) => tmail object # Mailer.deliver_news_added(news) => sends an email to the news' project recipients - def news_added(news) + def news_added(news, recipient) redmine_headers 'Project' => news.project.identifier, 'Type' => "News" message_id news - recipients news.recipients + recipients [recipient] subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}" body :news => news, :news_url => url_for(:controller => 'news', :action => 'show', :id => news) @@ -176,14 +171,13 @@ class Mailer < ActionMailer::Base # Example: # message_posted(message) => tmail object # Mailer.deliver_message_posted(message) => sends an email to the recipients - def message_posted(message) + def message_posted(message, recipient) redmine_headers 'Project' => message.project.identifier, 'Topic-Id' => (message.parent_id || message.id), 'Type' => "Forum" message_id message references message.parent unless message.parent.nil? - recipients(message.recipients) - cc((message.root.watcher_recipients + message.board.watcher_recipients).uniq - @recipients) + recipients [recipient] subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}" body :message => message, :message_url => url_for({ :controller => 'messages', :action => 'show', :board_id => message.board, :id => message.root, :r => message, :anchor => "message-#{message.id}" }) @@ -195,13 +189,12 @@ class Mailer < ActionMailer::Base # Example: # wiki_content_added(wiki_content) => tmail object # Mailer.deliver_wiki_content_added(wiki_content) => sends an email to the project's recipients - def wiki_content_added(wiki_content) + def wiki_content_added(wiki_content, recipient) redmine_headers 'Project' => wiki_content.project.identifier, 'Wiki-Page-Id' => wiki_content.page.id, 'Type' => "Wiki" message_id wiki_content - recipients wiki_content.recipients - cc(wiki_content.page.wiki.watcher_recipients - recipients) + recipients [recipient] subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}" body :wiki_content => wiki_content, :wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title) @@ -213,13 +206,12 @@ class Mailer < ActionMailer::Base # Example: # wiki_content_updated(wiki_content) => tmail object # Mailer.deliver_wiki_content_updated(wiki_content) => sends an email to the project's recipients - def wiki_content_updated(wiki_content) + def wiki_content_updated(wiki_content, recipient) redmine_headers 'Project' => wiki_content.project.identifier, 'Wiki-Page-Id' => wiki_content.page.id, 'Type' => "Wiki" message_id wiki_content - recipients wiki_content.recipients - cc(wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients) + recipients [recipient] subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}" body :wiki_content => wiki_content, :wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title), @@ -293,6 +285,44 @@ class Mailer < ActionMailer::Base render_multipart('register', body) end + def mail_handler_confirmation(object, user, email_subject) + recipients user.mail + + case + when object.is_a?(Issue) + project = object.project.name + url = url_for(:controller => 'issues', :action => 'show', :id => object.id) + when object.is_a?(Journal) + project = object.project.name + url = url_for(:controller => 'issues', :action => 'show', :id => object.issue.id) + when object.class == Message + project = object.project.name + url = url_for(object.event_url) + else + project = '' + url = '' + end + + subject "[#{project}] #{l(:label_mail_handler_confirmation, :subject => email_subject)}" + body(:object => object, + :url => url) + render_multipart('mail_handler_confirmation', body) + end + + def mail_handler_unauthorized_action(user, email_subject, options={}) + recipients options[:to] || user.mail + subject l(:label_mail_handler_failure, :subject => email_subject) + body({}) + render_multipart('mail_handler_unauthorized_action', body) + end + + def mail_handler_missing_information(user, email_subject, error_message) + recipients user.mail + subject l(:label_mail_handler_failure, :subject => email_subject) + body({:errors => error_message.to_s}) + render_multipart('mail_handler_missing_information', body) + end + def test(user) redmine_headers 'Type' => "Test" set_language_if_valid(user.language) @@ -395,7 +425,7 @@ class Mailer < ActionMailer::Base # Removes the current user from the recipients and cc # if he doesn't want to receive notifications about what he does @author ||= User.current - if @author.pref[:no_self_notified] + if @author && @author.mail && @author.pref[:no_self_notified] recipients((recipients.is_a?(Array) ? recipients : [recipients]) - [@author.mail]) if recipients.present? cc((cc.is_a?(Array) ? cc : [cc]) - [@author.mail]) if cc.present? end @@ -403,13 +433,6 @@ class Mailer < ActionMailer::Base notified_users = [recipients, cc].flatten.compact.uniq # Rails would log recipients only, not cc and bcc mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger - - # Blind carbon copy recipients - if Setting.bcc_recipients? - bcc(notified_users) - recipients [] - cc [] - end super end diff --git a/app/models/message_observer.rb b/app/models/message_observer.rb index a3a0f93d..72301850 100644 --- a/app/models/message_observer.rb +++ b/app/models/message_observer.rb @@ -14,6 +14,13 @@ class MessageObserver < ActiveRecord::Observer def after_create(message) - Mailer.deliver_message_posted(message) if Setting.notified_events.include?('message_posted') + if Setting.notified_events.include?('message_posted') + recipients = message.recipients + recipients += message.root.watcher_recipients + recipients += message.board.watcher_recipients + recipients.uniq.each do |recipient| + Mailer.deliver_message_posted(message, recipient) + end + end end end diff --git a/app/models/news_observer.rb b/app/models/news_observer.rb index efd25d49..34ff7bde 100644 --- a/app/models/news_observer.rb +++ b/app/models/news_observer.rb @@ -14,6 +14,10 @@ class NewsObserver < ActiveRecord::Observer def after_create(news) - Mailer.deliver_news_added(news) if Setting.notified_events.include?('news_added') + if Setting.notified_events.include?('news_added') + news.recipients.each do |recipient| + Mailer.deliver_news_added(news, recipient) + end + end end end diff --git a/app/models/principal.rb b/app/models/principal.rb index 1080f265..78af7d03 100644 --- a/app/models/principal.rb +++ b/app/models/principal.rb @@ -31,6 +31,10 @@ class Principal < ActiveRecord::Base before_create :set_default_empty_values + def to_liquid + PrincipalDrop.new(self) + end + def name(formatter = nil) to_s end @@ -44,6 +48,82 @@ class Principal < ActiveRecord::Base end end + def active? + true + end + + def logged? + true # TODO: should all principals default to logged or not? + end + + # Return true if the user is allowed to do the specified action on a specific context + # Action can be: + # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') + # * a permission Symbol (eg. :edit_project) + # Context can be: + # * a project : returns true if user is allowed to do the specified action on this project + # * a group of projects : returns true if user is allowed on every project + # * nil with options[:global] set : check if user has at least one role allowed for this action, + # or falls back to Non Member / Anonymous permissions depending if the user is logged + def allowed_to?(action, context, options={}) + if context && context.is_a?(Project) + # No action allowed on archived projects + return false unless context.active? + # No action allowed on disabled modules + return false unless context.allows_to?(action) + # Admin users are authorized for anything else + return true if admin? + + roles = roles_for_project(context) + return false unless roles + roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)} + + elsif context && context.is_a?(Array) + # Authorize if user is authorized on every element of the array + context.map do |project| + allowed_to?(action,project,options) + end.inject do |memo,allowed| + memo && allowed + end + elsif options[:global] + # Admin users are always authorized + return true if admin? + + # authorize if user has at least one role that has this permission + roles = memberships.collect {|m| m.roles}.flatten.uniq + roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action)) + else + false + end + end + + # Is the user allowed to do the specified action on any project? + # See allowed_to? for the actions and valid options. + def allowed_to_globally?(action, options) + allowed_to?(action, nil, options.reverse_merge(:global => true)) + end + + # Return user's roles for project + def roles_for_project(project) + roles = [] + # No role on archived projects + return roles unless project && project.active? + if logged? + # Find project membership + membership = memberships.detect {|m| m.project_id == project.id} + if membership + roles = membership.roles + else + @role_non_member ||= Role.non_member + roles << @role_non_member + end + else + @role_anonymous ||= Role.anonymous + roles << @role_anonymous + end + roles + end + protected # Make sure we don't try to insert NULL values (see #4632) diff --git a/app/models/project.rb b/app/models/project.rb index 87269f54..c0309305 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -82,6 +82,16 @@ class Project < ActiveRecord::Base named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"} named_scope :all_public, { :conditions => { :is_public => true } } named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } } + named_scope :like, lambda {|q| + s = "%#{q.to_s.strip.downcase}%" + { + :conditions => ["LOWER(name) LIKE ?", s] + } + } + + def to_liquid + ProjectDrop.new(self) + end def initialize(attributes = nil) super @@ -131,6 +141,11 @@ class Project < ActiveRecord::Base end end + # Is the project visible to the current user + def visible? + User.current.allowed_to?(:view_project, self) + end + def self.allowed_to_condition(user, permission, options={}) base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}" if perm = Redmine::AccessControl.permission(permission) @@ -616,7 +631,7 @@ class Project < ActiveRecord::Base while (ancestors.any? && !project.is_descendant_of?(ancestors.last)) ancestors.pop end - yield project, ancestors.size + yield project, ancestors.size if block_given? ancestors << project end end diff --git a/app/models/query.rb b/app/models/query.rb index a28235b4..867ef25c 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -12,64 +12,7 @@ # See doc/COPYRIGHT.rdoc for more details. #++ -class QueryColumn - attr_accessor :name, :sortable, :groupable, :default_order - include Redmine::I18n - - def initialize(name, options={}) - self.name = name - self.sortable = options[:sortable] - self.groupable = options[:groupable] || false - if groupable == true - self.groupable = name.to_s - end - self.default_order = options[:default_order] - @caption_key = options[:caption] || "field_#{name}" - end - - def caption - l(@caption_key) - end - - # Returns true if the column is sortable, otherwise false - def sortable? - !sortable.nil? - end - - def value(issue) - issue.send name - end -end - -class QueryCustomFieldColumn < QueryColumn - - def initialize(custom_field) - self.name = "cf_#{custom_field.id}".to_sym - self.sortable = custom_field.order_statement || false - if %w(list date bool int).include?(custom_field.field_format) - self.groupable = custom_field.order_statement - end - self.groupable ||= false - @cf = custom_field - end - - def caption - @cf.name - end - - def custom_field - @cf - end - - def value(issue) - cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id} - cv && @cf.cast_value(cv.value) - end -end - class Query < ActiveRecord::Base - class StatementInvalid < ::ActiveRecord::StatementInvalid - end belongs_to :project belongs_to :user @@ -90,6 +33,7 @@ class Query < ActiveRecord::Base "*" => :label_all, ">=" => :label_greater_or_equal, "<=" => :label_less_or_equal, + "><" => :label_between, " :label_in_less_than, ">t+" => :label_in_more_than, "t+" => :label_in, @@ -107,8 +51,8 @@ class Query < ActiveRecord::Base :list_status => [ "o", "=", "!", "c", "*" ], :list_optional => [ "=", "!", "!*", "*" ], :list_subprojects => [ "*", "!*", "=" ], - :date => [ "t+", "t+", "t", "w", ">t-", " [ ">t-", " [ "=", ">=", "<=", "><", "t+", "t+", "t", "w", ">t-", " [ "=", ">=", "<=", "><", ">t-", " [ "=", "~", "!", "!~" ], :text => [ "~", "!~" ], :integer => [ "=", ">=", "<=", "!*", "*" ] } @@ -139,6 +83,7 @@ class Query < ActiveRecord::Base def initialize(attributes = nil) super attributes self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} } + self.display_subprojects ||= Setting.display_subprojects_issues? end def after_initialize @@ -245,7 +190,7 @@ class Query < ActiveRecord::Base def add_filter(field, operator, values) # values must be an array - return unless values and values.is_a? Array # and !values.first.empty? + return unless values.nil? || values.is_a?(Array) # check if field is defined as an available filter if available_filters.has_key? field filter_options = available_filters[field] @@ -254,7 +199,7 @@ class Query < ActiveRecord::Base # allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]}) # filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator #end - filters[field] = {:operator => operator, :values => values } + filters[field] = {:operator => operator, :values => (values || ['']) } end end @@ -266,9 +211,9 @@ class Query < ActiveRecord::Base # Add multiple filters using +add_filter+ def add_filters(fields, operators, values) - if fields.is_a?(Array) && operators.is_a?(Hash) && values.is_a?(Hash) + if fields.is_a?(Array) && operators.is_a?(Hash) && (values.nil? || values.is_a?(Hash)) fields.each do |field| - add_filter(field, operators[field], values[field]) + add_filter(field, operators[field], values && values[field]) end end end @@ -277,6 +222,10 @@ class Query < ActiveRecord::Base filters and filters[field] end + def type_for(field) + available_filters[field][:type] if available_filters.has_key?(field) + end + def operator_for(field) has_filter?(field) ? filters[field][:operator] : nil end @@ -285,6 +234,10 @@ class Query < ActiveRecord::Base has_filter?(field) ? filters[field][:values] : nil end + def value_for(field, index=0) + (values_for(field) || [])[index] + end + def label_for(field) label = available_filters[field][:name] if available_filters.has_key?(field) label ||= field.gsub(/\_id$/, "") @@ -410,7 +363,7 @@ class Query < ActiveRecord::Base # all subprojects ids += project.descendants.collect(&:id) end - elsif Setting.display_subprojects_issues? + elsif display_subprojects? ids += project.descendants.collect(&:id) end project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',') @@ -519,7 +472,7 @@ class Query < ActiveRecord::Base def issue_count Issue.count(:include => [:status, :project], :conditions => statement) rescue ::ActiveRecord::StatementInvalid => e - raise StatementInvalid.new(e.message) + raise Query::StatementInvalid.new(e.message) end # Returns the issue count by group or nil if query is not grouped @@ -539,7 +492,7 @@ class Query < ActiveRecord::Base end r rescue ::ActiveRecord::StatementInvalid => e - raise StatementInvalid.new(e.message) + raise Query::StatementInvalid.new(e.message) end # Returns the issues @@ -554,7 +507,7 @@ class Query < ActiveRecord::Base :limit => options[:limit], :offset => options[:offset] rescue ::ActiveRecord::StatementInvalid => e - raise StatementInvalid.new(e.message) + raise Query::StatementInvalid.new(e.message) end # Returns the journals @@ -566,7 +519,7 @@ class Query < ActiveRecord::Base :limit => options[:limit], :offset => options[:offset] rescue ::ActiveRecord::StatementInvalid => e - raise StatementInvalid.new(e.message) + raise Query::StatementInvalid.new(e.message) end # Returns the versions @@ -575,7 +528,7 @@ class Query < ActiveRecord::Base Version.find :all, :include => :project, :conditions => Query.merge_conditions(project_statement, options[:conditions]) rescue ::ActiveRecord::StatementInvalid => e - raise StatementInvalid.new(e.message) + raise Query::StatementInvalid.new(e.message) end private @@ -585,11 +538,15 @@ class Query < ActiveRecord::Base sql = '' case operator when "=" - if value.present? - sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" + if [:date, :date_past].include?(type_for(field)) + sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), (Date.parse(value.first) rescue nil)) else - # empty set of allowed values produces no result - sql = "0=1" + if value.any? + sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" + else + # IN an empty set + sql = "0=1" + end end when "!" if value.present? @@ -605,42 +562,58 @@ class Query < ActiveRecord::Base sql = "#{db_table}.#{db_field} IS NOT NULL" sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter when ">=" - if is_custom_filter - sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,4)) >= #{value.first.to_f}" + if [:date, :date_past].include?(type_for(field)) + sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), nil) else - sql = "#{db_table}.#{db_field} >= #{value.first.to_f}" + if is_custom_filter + sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) >= #{value.first.to_i}" + else + sql = "#{db_table}.#{db_field} >= #{value.first.to_i}" + end end when "<=" - if is_custom_filter - sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,4)) <= #{value.first.to_f}" + if [:date, :date_past].include?(type_for(field)) + sql = date_clause(db_table, db_field, nil, (Date.parse(value.first) rescue nil)) else - sql = "#{db_table}.#{db_field} <= #{value.first.to_f}" + if is_custom_filter + sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) <= #{value.first.to_i}" + else + sql = "#{db_table}.#{db_field} <= #{value.first.to_i}" + end + end + when "><" + if [:date, :date_past].include?(type_for(field)) + sql = date_clause(db_table, db_field, (Date.parse(value[0]) rescue nil), (Date.parse(value[1]) rescue nil)) + else + if is_custom_filter + sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) BETWEEN #{value[0].to_i} AND #{value[1].to_i}" + else + sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_i} AND #{value[1].to_i}" + end end when "o" sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id" when "c" sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id" when ">t-" - sql = date_range_clause(db_table, db_field, - value.first.to_i, 0) + sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0) when "t+" - sql = date_range_clause(db_table, db_field, value.first.to_i, nil) + sql = relative_date_clause(db_table, db_field, value.first.to_i, nil) when "= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) + sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6) when "~" sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'" when "!~" @@ -676,14 +649,19 @@ class Query < ActiveRecord::Base end # Returns a SQL clause for a date or datetime field. - def date_range_clause(table, field, from, to) + def date_clause(table, field, from, to) s = [] if from - s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((Date.yesterday + from).to_time.end_of_day)]) + s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((from - 1).to_time.end_of_day)]) end if to - s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date((Date.today + to).to_time.end_of_day)]) + s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to.to_time.end_of_day)]) end s.join(' AND ') end + + # Returns a SQL clause for a date or datetime field using relative dates. + def relative_date_clause(table, field, days_from, days_to) + date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil)) + end end diff --git a/app/models/query/statement_invalid.rb b/app/models/query/statement_invalid.rb new file mode 100644 index 00000000..35eecfe5 --- /dev/null +++ b/app/models/query/statement_invalid.rb @@ -0,0 +1,16 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class Query::StatementInvalid < ActiveRecord::StatementInvalid +end diff --git a/app/models/query_column.rb b/app/models/query_column.rb new file mode 100644 index 00000000..270f8c95 --- /dev/null +++ b/app/models/query_column.rb @@ -0,0 +1,42 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class QueryColumn + attr_accessor :name, :sortable, :groupable, :default_order + include Redmine::I18n + + def initialize(name, options={}) + self.name = name + self.sortable = options[:sortable] + self.groupable = options[:groupable] || false + if groupable == true + self.groupable = name.to_s + end + self.default_order = options[:default_order] + @caption_key = options[:caption] || "field_#{name}" + end + + def caption + l(@caption_key) + end + + # Returns true if the column is sortable, otherwise false + def sortable? + !sortable.nil? + end + + def value(issue) + issue.send name + end +end diff --git a/app/models/query_custom_field_column.rb b/app/models/query_custom_field_column.rb new file mode 100644 index 00000000..b7adce87 --- /dev/null +++ b/app/models/query_custom_field_column.rb @@ -0,0 +1,40 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class QueryCustomFieldColumn < QueryColumn + + def initialize(custom_field) + self.name = "cf_#{custom_field.id}".to_sym + self.sortable = custom_field.order_statement || false + if %w(list date bool int).include?(custom_field.field_format) + self.groupable = custom_field.order_statement + end + self.groupable ||= false + @cf = custom_field + end + + def caption + @cf.name + end + + def custom_field + @cf + end + + def value(issue) + cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id} + cv && @cf.cast_value(cv.value) + end +end + diff --git a/app/models/setting.rb b/app/models/setting.rb index b8ad1f1a..581766de 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -97,13 +97,17 @@ class Setting < ActiveRecord::Base # Returns the value of the setting named name def self.[](name) - Marshal.load(Rails.cache.fetch("chiliproject/setting/#{name}") {Marshal.dump(find_or_default(name).value)}) + if use_caching? + Marshal.load(Rails.cache.fetch(self.cache_key(name)) {Marshal.dump(find_or_default(name).value)}) + else + find_or_default(name).value + end end def self.[]=(name, v) setting = find_or_default(name) setting.value = (v ? v : "") - Rails.cache.delete "chiliproject/setting/#{name}" + Rails.cache.delete self.cache_key(name) setting.save setting.value end @@ -137,23 +141,42 @@ class Setting < ActiveRecord::Base Object.const_defined?(:OpenID) && self[:openid].to_i > 0 end - # Checks if settings have changed since the values were read - # and clears the cache hash if it's the case - # Called once per request + # Deprecation Warning: This method is no longer available. There is no + # replacement. def self.check_cache - settings_updated_on = Setting.maximum(:updated_on) - cache_cleared_on = Rails.cache.read('chiliproject/setting-cleared_on') - cache_cleared_on = cache_cleared_on ? Marshal.load(cache_cleared_on) : Time.now - if settings_updated_on && cache_cleared_on <= settings_updated_on - clear_cache - end + # DEPRECATED SINCE 3.0.0beta2 + ActiveSupport::Deprecation.warn "The Setting.check_cache method is " + + "deprecated and will be removed in the future. There should be no " + + "replacement for this functionality needed." end # Clears all of the Setting caches def self.clear_cache - Rails.cache.delete_matched( /^chiliproject\/setting\/.+$/ ) - Rails.cache.write('chiliproject/setting-cleared_on', Marshal.dump(Time.now)) - logger.info 'Settings cache cleared.' if logger + # DEPRECATED SINCE 3.0.0beta2 + ActiveSupport::Deprecation.warn "The Setting.clear_cache method is " + + "deprecated and will be removed in the future. There should be no " + + "replacement for this functionality needed. To sweep the whole " + + "cache Rails.cache.clear may be used. To invalidate the Settings " + + "only, you may use Setting.first.try(:touch)" + end + + # Temporarily deactivate settings caching in the block scope + def self.uncached + cache_setting = self.use_caching + self.use_caching = false + yield + ensure + self.use_caching = cache_setting + end + + # Check if Setting caching should be performed + def self.use_caching? + !Thread.current['chiliproject/settings/do_not_use_caching'] + end + + # Dis-/En-able Setting caching. This is mainly intended to be used in tests + def self.use_caching=(new_value) + Thread.current['chiliproject/settings/do_not_use_caching'] = !new_value end private @@ -165,4 +188,8 @@ private setting = find_by_name(name) setting ||= new(:name => name, :value => @@available_settings[name]['default']) if @@available_settings.has_key? name end + + def self.cache_key(name) + "chiliproject/setting/#{Setting.maximum(:updated_on).to_i}/#{name}" + end end diff --git a/app/models/tracker.rb b/app/models/tracker.rb index 697c728c..284adc19 100644 --- a/app/models/tracker.rb +++ b/app/models/tracker.rb @@ -35,6 +35,10 @@ class Tracker < ActiveRecord::Base name <=> tracker.name end + def to_liquid + TrackerDrop.new(self) + end + def self.all find(:all, :order => 'position') end diff --git a/app/models/user.rb b/app/models/user.rb index d22eda90..dfbfb515 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -345,27 +345,6 @@ class User < Principal !logged? end - # Return user's roles for project - def roles_for_project(project) - roles = [] - # No role on archived projects - return roles unless project && project.active? - if logged? - # Find project membership - membership = memberships.detect {|m| m.project_id == project.id} - if membership - roles = membership.roles - else - @role_non_member ||= Role.non_member - roles << @role_non_member - end - else - @role_anonymous ||= Role.anonymous - roles << @role_anonymous - end - roles - end - # Return true if the user is a member of project def member_of?(project) !roles_for_project(project).detect {|role| role.member?}.nil? @@ -388,53 +367,6 @@ class User < Principal @projects_by_role end - # Return true if the user is allowed to do the specified action on a specific context - # Action can be: - # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') - # * a permission Symbol (eg. :edit_project) - # Context can be: - # * a project : returns true if user is allowed to do the specified action on this project - # * a group of projects : returns true if user is allowed on every project - # * nil with options[:global] set : check if user has at least one role allowed for this action, - # or falls back to Non Member / Anonymous permissions depending if the user is logged - def allowed_to?(action, context, options={}) - if context && context.is_a?(Project) - # No action allowed on archived projects - return false unless context.active? - # No action allowed on disabled modules - return false unless context.allows_to?(action) - # Admin users are authorized for anything else - return true if admin? - - roles = roles_for_project(context) - return false unless roles - roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)} - - elsif context && context.is_a?(Array) - # Authorize if user is authorized on every element of the array - context.map do |project| - allowed_to?(action,project,options) - end.inject do |memo,allowed| - memo && allowed - end - elsif options[:global] - # Admin users are always authorized - return true if admin? - - # authorize if user has at least one role that has this permission - roles = memberships.collect {|m| m.roles}.flatten.uniq - roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action)) - else - false - end - end - - # Is the user allowed to do the specified action on any project? - # See allowed_to? for the actions and valid options. - def allowed_to_globally?(action, options) - allowed_to?(action, nil, options.reverse_merge(:global => true)) - end - safe_attributes 'login', 'firstname', 'lastname', diff --git a/app/models/watcher.rb b/app/models/watcher.rb index 00e044e6..954a862b 100644 --- a/app/models/watcher.rb +++ b/app/models/watcher.rb @@ -14,7 +14,7 @@ class Watcher < ActiveRecord::Base belongs_to :watchable, :polymorphic => true - belongs_to :user + belongs_to :user, :class_name => 'Principal', :foreign_key => 'user_id' validates_presence_of :user validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id] diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index ed465ff8..6bbe5f49 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -53,6 +53,10 @@ class WikiPage < ActiveRecord::Base end end + def to_liquid + WikiPageDrop.new(self) + end + def visible?(user=User.current) !user.nil? && user.allowed_to?(:view_wiki_pages, project) end diff --git a/app/views/account/_login.rhtml b/app/views/account/_login.rhtml new file mode 100644 index 00000000..bebade71 --- /dev/null +++ b/app/views/account/_login.rhtml @@ -0,0 +1,34 @@ + diff --git a/app/views/account/login.rhtml b/app/views/account/login.rhtml index fad59e9a..eddb02a0 100644 --- a/app/views/account/login.rhtml +++ b/app/views/account/login.rhtml @@ -5,7 +5,7 @@ - + diff --git a/app/views/admin/_menu.rhtml b/app/views/admin/_menu.rhtml index bd3abebe..8ce307c1 100644 --- a/app/views/admin/_menu.rhtml +++ b/app/views/admin/_menu.rhtml @@ -1,5 +1 @@ -
-
    - <%= render_menu :admin_menu %> -
-
+<%= render_menu :admin_menu %> diff --git a/app/views/admin/index.rhtml b/app/views/admin/index.rhtml index f7e72313..3015907a 100644 --- a/app/views/admin/index.rhtml +++ b/app/views/admin/index.rhtml @@ -1,5 +1,3 @@ -

<%=l(:label_administration)%>

-
<%= render :partial => 'no_data' if @no_configuration_data %> <%= render :partial => 'menu' %> diff --git a/app/views/auto_completes/projects.html.erb b/app/views/auto_completes/projects.html.erb new file mode 100644 index 00000000..ce5d7dfd --- /dev/null +++ b/app/views/auto_completes/projects.html.erb @@ -0,0 +1 @@ +<%= projects_check_box_tags 'project_ids[]', @projects %> \ No newline at end of file diff --git a/app/views/groups/autocomplete_for_user.html.erb b/app/views/auto_completes/users.html.erb similarity index 100% rename from app/views/groups/autocomplete_for_user.html.erb rename to app/views/auto_completes/users.html.erb diff --git a/app/views/boards/show.rhtml b/app/views/boards/show.rhtml index eb33d880..e796b8c6 100644 --- a/app/views/boards/show.rhtml +++ b/app/views/boards/show.rhtml @@ -71,3 +71,13 @@ <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %> <%= stylesheet_link_tag 'scm' %> <% end %> + +<% content_for :sidebar do %> + <% if User.current.allowed_to?(:add_board_watchers, @project) || + (@board.watchers.present? && User.current.allowed_to?(:view_board_watchers, @project)) %> +
+ <%= render :partial => 'watchers/watchers', :locals => {:watched => @board} %> +
+ <% end %> + +<% end %> diff --git a/app/views/context_menus/issues.html.erb b/app/views/context_menus/issues.html.erb index 71bac63b..af34805c 100644 --- a/app/views/context_menus/issues.html.erb +++ b/app/views/context_menus/issues.html.erb @@ -1,52 +1,55 @@ -
    + diff --git a/app/views/documents/_form.rhtml b/app/views/documents/_form.rhtml index 37d67d1e..74d9248c 100644 --- a/app/views/documents/_form.rhtml +++ b/app/views/documents/_form.rhtml @@ -9,6 +9,15 @@

    <%= text_area 'document', 'description', :cols => 60, :rows => 15, :class => 'wiki-edit' %>

    + +<% if User.current.allowed_to?(:add_document_watchers, @project) -%> +

    +<% @document.project.users.sort.each do |user| -%> + +<% end -%> +

    +<% end %> +
diff --git a/app/views/documents/show.rhtml b/app/views/documents/show.rhtml index 922fe36b..63444f0a 100644 --- a/app/views/documents/show.rhtml +++ b/app/views/documents/show.rhtml @@ -27,6 +27,16 @@ <% html_title h(@document.title) -%> +<% content_for :sidebar do %> + <% if User.current.allowed_to?(:add_document_watchers, @project) || + (@document.watchers.present? && User.current.allowed_to?(:view_document_watchers, @project)) %> +
+ <%= render :partial => 'watchers/watchers', :locals => {:watched => @document} %> +
+ <% end %> + +<% end %> + <% content_for :header_tags do %> <%= stylesheet_link_tag 'scm' %> <% end %> diff --git a/app/views/groups/_memberships.html.erb b/app/views/groups/_memberships.html.erb index 46f2d11e..7969d768 100644 --- a/app/views/groups/_memberships.html.erb +++ b/app/views/groups/_memberships.html.erb @@ -41,17 +41,5 @@
-<% if projects.any? %> -
<%=l(:label_project_new)%> -<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => @group }) do %> -<%= label_tag "membership_project_id", l(:description_choose_project), :class => "hidden-for-sighted" %> -<%= select_tag 'membership[project_id]', options_for_membership_project_select(@group, projects) %> -

<%= l(:label_role_plural) %>: -<% roles.each do |role| %> - -<% end %>

-

<%= submit_tag l(:button_add) %>

-<% end %> -
-<% end %> +<%= render :partial => 'members/membership_assignment', :locals => {:principal => @group, :projects => projects - @group.projects, :roles => roles } %>
diff --git a/app/views/groups/_users.html.erb b/app/views/groups/_users.html.erb index 33091ea3..2fa91cb5 100644 --- a/app/views/groups/_users.html.erb +++ b/app/views/groups/_users.html.erb @@ -33,7 +33,7 @@ <%= observe_field(:user_search, :frequency => 0.5, :update => :users, - :url => { :controller => 'groups', :action => 'autocomplete_for_user', :id => @group }, + :url => auto_complete_users_path(:remove_group_members => @group), :with => 'q') %> diff --git a/app/views/help/wiki_syntax_detailed.html.erb b/app/views/help/wiki_syntax_detailed.html.erb index 26603f5f..6317e6e8 100644 --- a/app/views/help/wiki_syntax_detailed.html.erb +++ b/app/views/help/wiki_syntax_detailed.html.erb @@ -13,20 +13,22 @@ } a.new { color: #b73535; } - .CodeRay .c { color:#666; } + .syntaxhl .line-numbers { padding: 2px 4px 2px 4px; background-color: #eee; margin:0 } + .syntaxhl .comment { color:#666; } - .CodeRay .cl { color:#B06; font-weight:bold } - .CodeRay .dl { color:black } - .CodeRay .fu { color:#06B; font-weight:bold } + .syntaxhl .class { color:#B06; font-weight:bold } + .syntaxhl .delimiter { color:black } + .syntaxhl .function { color:#06B; font-weight:bold } - .CodeRay .il { background: #eee } - .CodeRay .il .idl { font-weight: bold; color: #888 } + .syntaxhl .inline { background: #eee } + .syntaxhl .inline .inline-delimiter { font-weight: bold; color: #888 } - .CodeRay .iv { color:#33B } - .CodeRay .r { color:#080; font-weight:bold } + .syntaxhl .instance-variable { color:#33B } + .syntaxhl .reserved { color:#080; font-weight:bold } + + .syntaxhl .string { background-color:#fff0f0; color: #D20; } + .syntaxhl .string .delimiter { color:#710 } - .CodeRay .s { background-color:#fff0f0 } - .CodeRay .s .dl { color:#710 } <% end %> <% html_title "Wiki Formatting" %> @@ -236,7 +238,7 @@ To go live, all you need to add is a database and a web server.

Code highlighting

-

Code highlightment relies on CodeRay, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.

+

The default code highlighting relies on CodeRay, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.

You can highlight code in your wiki page using this syntax:

@@ -248,15 +250,14 @@ To go live, all you need to add is a database and a web server.

Example:

-
 1 # The Greeter class
- 2 class Greeter
- 3   def initialize(name)
- 4     @name = name.capitalize
- 5   end
- 6
- 7   def salute
- 8     puts "Hello #{@name}!"
- 9   end
-10 end
-
+
 1 # The Greeter class
+ 2 class Greeter
+ 3   def initialize(name)
+ 4     @name = name.capitalize
+ 5   end
+ 6
+ 7   def salute
+ 8     puts "Hello #{@name}!"
+ 9   end
+10 end
 
diff --git a/app/views/issues/_edit.rhtml b/app/views/issues/_edit.rhtml index c73b2980..8f76744e 100644 --- a/app/views/issues/_edit.rhtml +++ b/app/views/issues/_edit.rhtml @@ -38,7 +38,7 @@ <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %> -
<%= text_field_tag 'username', nil, :tabindex => '1' %><%= text_field_tag 'username', nil, :tabindex => '2' %>
diff --git a/app/views/issues/_sidebar.rhtml b/app/views/issues/_sidebar.rhtml index 6de1c2d6..1bb4ee68 100644 --- a/app/views/issues/_sidebar.rhtml +++ b/app/views/issues/_sidebar.rhtml @@ -1,16 +1,6 @@

<%= l(:label_issue_plural) %>

-<%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %>
-<% if @project %> -<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %>
-<% end %> <%= call_hook(:view_issues_sidebar_issues_bottom) %> -<% if User.current.allowed_to?(:view_calendar, @project, :global => true) %> - <%= link_to(l(:label_calendar), :controller => 'calendars', :action => 'show', :project_id => @project) %>
-<% end %> -<% if User.current.allowed_to?(:view_gantt, @project, :global => true) %> - <%= link_to(l(:label_gantt), :controller => 'gantts', :action => 'show', :project_id => @project) %>
-<% end %> <%= call_hook(:view_issues_sidebar_planning_bottom) %> <%= render_sidebar_queries %> diff --git a/app/views/issues/index.rhtml b/app/views/issues/index.rhtml index 122b0c06..3878dc5f 100644 --- a/app/views/issues/index.rhtml +++ b/app/views/issues/index.rhtml @@ -1,23 +1,29 @@ +
+ +

<%= @query.new_record? ? l(:label_issue_plural) : h(@query.name) %>

+ +
+
<% if !@query.new_record? && @query.editable_by?(User.current) %> <%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %> <%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> <% end %> +<%= render :partial => 'queries/new_issue_button' %>
-

<%= @query.new_record? ? l(:label_issue_plural) : h(@query.name) %>

<% html_title(@query.new_record? ? l(:label_issue_plural) : h(@query.name)) %> <% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %> <%= hidden_field_tag('project_id', @project.to_param) if @project %>
-
"> +
"> <%= l(:label_filter_plural) %> -
"> +
"> <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
-
@@ -29,6 +35,20 @@ + + + +
<%= select_tag('group_by', options_for_select([[]] + @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, @query.group_by)) %>
<%= l(:label_project_plural) %> + +
+ +
@@ -55,6 +75,9 @@

<% end %> +
+
+ <%= error_messages_for 'query' %> <% if @query.valid? %> <% if @issues.empty? %> diff --git a/app/views/issues/show.rhtml b/app/views/issues/show.rhtml index eddc7da0..9642c289 100644 --- a/app/views/issues/show.rhtml +++ b/app/views/issues/show.rhtml @@ -1,63 +1,75 @@ -<%= render :partial => 'action_menu' %> - -

<%= h(@issue.tracker.name) %> #<%= h(@issue.id) %><%= call_hook(:view_issues_show_identifier, :issue => @issue) %>

+
+
+ <%= render :partial => 'action_menu' %> +
+
- <%= avatar(@issue.author, :size => "50") %> -
-<%= render_issue_subject_with_tree(@issue) %> -
-

- <%= authoring @issue.created_on, @issue.author %>. - <% if @issue.created_on != @issue.updated_on %> - <%= l(:label_updated_time, time_tag(@issue.updated_on)) %>. +

+ <%=h(@issue.subject) %> (<%= h(@issue.tracker.name) + ' #' +@issue.id.to_s %>) +

+
+ +

+ <%= avatar(@issue.author, :size => "14") %> + <%= authoring @issue.created_on, @issue.author %>. + <% if @issue.created_on != @issue.updated_on %> + <%= l(:label_updated_time, time_tag(@issue.updated_on)) %>. + <% end %> +

+
+ +
+ + + + + + + + + + + + + + + + <% if User.current.allowed_to?(:view_time_entries, @project) %> + + <% end %> -

+ + + + <% if @issue.estimated_hours %> + + <% end %> + + <%= render_custom_fields_rows(@issue) %> + <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %> +
<%=l(:field_status)%>:<%= h(@issue.status.name) %><%=l(:field_start_date)%>:<%= format_date(@issue.start_date) %>
<%=l(:field_priority)%>:<%= h(@issue.priority.name) %><%=l(:field_due_date)%>:<%= format_date(@issue.due_date) %>
<%=l(:field_assigned_to)%>:<%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %><%=l(:field_done_ratio)%>:<%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %>
<%=l(:field_category)%>:<%=h(@issue.category ? @issue.category.name : "-") %><%=l(:label_spent_time)%>:<%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %>
<%=l(:field_fixed_version)%>:<%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %><%=l(:field_estimated_hours)%>:<%= l_hours(@issue.estimated_hours) %>
+
- - - - - - - - - - - - - - - - <% if User.current.allowed_to?(:view_time_entries, @project) %> - - - <% end %> - - - - <% if @issue.estimated_hours %> - - <% end %> - -<%= render_custom_fields_rows(@issue) %> -<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %> -
<%=l(:field_status)%>:<%= h(@issue.status.name) %><%=l(:field_start_date)%>:<%= format_date(@issue.start_date) %>
<%=l(:field_priority)%>:<%= h(@issue.priority.name) %><%=l(:field_due_date)%>:<%= format_date(@issue.due_date) %>
<%=l(:field_assigned_to)%>:<%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %><%=l(:field_done_ratio)%>:<%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %>
<%=l(:field_category)%>:<%=h(@issue.category ? @issue.category.name : "-") %><%=l(:label_spent_time)%>:<%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %>
<%=l(:field_fixed_version)%>:<%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %><%=l(:field_estimated_hours)%>:<%= l_hours(@issue.estimated_hours) %>
- -<% if @issue.description? || @issue.attachments.any? -%> -
<% if @issue.description? %> -
- <%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %> -
+
-

<%=l(:field_description)%>

-
- <%= textilizable @issue, :description, :attachments => @issue.attachments %> -
+
+
+ <%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %> +
+ +

<%=l(:field_description)%>

+
+ <%= textilizable @issue, :description, :attachments => @issue.attachments %> +
+
<% end %> -<%= link_to_attachments @issue %> + +<% if @issue.attachments.any? -%> +
+ <%= link_to_attachments @issue %> <% end -%> <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %> @@ -65,10 +77,11 @@ <% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
-
- <%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %> -
-

<%=l(:label_subtask_plural)%>

+

+ <%=l(:label_subtask_plural)%> + (<%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>) +

+ <%= render_descendants_tree(@issue) unless @issue.leaf? %>
<% end %> @@ -78,6 +91,7 @@
<%= render :partial => 'relations' %>
+
<% end %>
@@ -91,14 +105,19 @@ <% if @journals.present? %>
-

<%=l(:label_history)%>

+

<%=l(:label_history)%>

<%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
<% end %>
-<%= render :partial => 'action_menu' %> + +
+
+ <%= render :partial => 'action_menu', :locals => {:replace_watcher => 'watcher2' } %> +
+
<% if authorize_for('issues', 'edit') %> diff --git a/app/views/journals/diff.html.erb b/app/views/journals/diff.html.erb new file mode 100644 index 00000000..b1783494 --- /dev/null +++ b/app/views/journals/diff.html.erb @@ -0,0 +1,9 @@ +

<%= authoring @journal.created_at, @journal.user, :label => :label_updated_time_by %>

+ +
+<%= simple_format_without_paragraph @diff.to_html %> +
+ +

<%= link_to(l(:button_back), issue_path(@issue)) %>

+ +<% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %> diff --git a/app/views/layouts/admin.rhtml b/app/views/layouts/admin.rhtml index 197c32a8..bf376abb 100644 --- a/app/views/layouts/admin.rhtml +++ b/app/views/layouts/admin.rhtml @@ -1,8 +1,6 @@ -<% unless controller_name == 'admin' && action_name == 'index' %> - <% content_for :sidebar do %> -

<%=l(:label_administration)%>

- <%= render :partial => 'admin/menu' %> - <% end %> +<% @page_header_title = l(:label_administration) %> +<% content_for :main_menu do %> + <%= render :partial => 'admin/menu' %> <% end %> <%= render :file => "layouts/base" %> diff --git a/app/views/layouts/base.rhtml b/app/views/layouts/base.rhtml index 3db6b3a1..4d34d8d8 100644 --- a/app/views/layouts/base.rhtml +++ b/app/views/layouts/base.rhtml @@ -7,76 +7,149 @@ <%= csrf_meta_tag %> <%= favicon %> +<%= stylesheet_link_tag 'reset', :media => 'all' %> +<%= stylesheet_link_tag 'smoothness/jquery-ui', :media => 'all' %> <%= stylesheet_link_tag 'application', :media => 'all' %> +<%= stylesheet_link_tag 'print', :media => 'print' %> <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %> + + + + +<%= javascript_include_tag 'jquery.min.js' %> +<%= javascript_include_tag 'jquery-ui.min.js' %> +<%= javascript_include_tag 'jquery.menu_expand.js' %> +<%= javascript_tag('jQuery.noConflict();') %> <%= javascript_heads %> +<%= stylesheet_link_tag 'jstoolbar' %> <%= heads_for_theme %> - +<% heads_for_wiki_formatter %> <%= call_hook :view_layouts_base_html_head %> <%= yield :header_tags -%>
-
-
-
- <%= render_menu :account_menu -%> -
- <%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}", :id => 'loggedas') if User.current.logged? %> - <%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%> -
+
+ + + - - - -
<%= call_hook :view_layouts_base_body_bottom %> diff --git a/app/views/mailer/mail_handler_confirmation.text.html.rhtml b/app/views/mailer/mail_handler_confirmation.text.html.rhtml new file mode 100644 index 00000000..5e115e2b --- /dev/null +++ b/app/views/mailer/mail_handler_confirmation.text.html.rhtml @@ -0,0 +1,2 @@ +

<%= l(:text_mail_handler_confirmation_successful) %>
+<%= auto_link(@url) %>

diff --git a/app/views/mailer/mail_handler_confirmation.text.plain.rhtml b/app/views/mailer/mail_handler_confirmation.text.plain.rhtml new file mode 100644 index 00000000..0b8348e4 --- /dev/null +++ b/app/views/mailer/mail_handler_confirmation.text.plain.rhtml @@ -0,0 +1,2 @@ +<%= l(:text_mail_handler_confirmation_successful) %> +<%= @url %> diff --git a/app/views/mailer/mail_handler_missing_information.text.html.rhtml b/app/views/mailer/mail_handler_missing_information.text.html.rhtml new file mode 100644 index 00000000..639e2aa6 --- /dev/null +++ b/app/views/mailer/mail_handler_missing_information.text.html.rhtml @@ -0,0 +1,3 @@ +

<%= l(:label_mail_handler_errors_with_submission) %>

+ +

<%= h(@errors) %>

diff --git a/app/views/mailer/mail_handler_missing_information.text.plain.rhtml b/app/views/mailer/mail_handler_missing_information.text.plain.rhtml new file mode 100644 index 00000000..458f515d --- /dev/null +++ b/app/views/mailer/mail_handler_missing_information.text.plain.rhtml @@ -0,0 +1,3 @@ +<%= l(:label_mail_handler_errors_with_submission) %> + +<%= h(@errors) %> diff --git a/app/views/mailer/mail_handler_unauthorized_action.text.html.rhtml b/app/views/mailer/mail_handler_unauthorized_action.text.html.rhtml new file mode 100644 index 00000000..e1f58ad4 --- /dev/null +++ b/app/views/mailer/mail_handler_unauthorized_action.text.html.rhtml @@ -0,0 +1 @@ +<%= l(:notice_not_authorized_action) %> diff --git a/app/views/mailer/mail_handler_unauthorized_action.text.plain.rhtml b/app/views/mailer/mail_handler_unauthorized_action.text.plain.rhtml new file mode 100644 index 00000000..e1f58ad4 --- /dev/null +++ b/app/views/mailer/mail_handler_unauthorized_action.text.plain.rhtml @@ -0,0 +1 @@ +<%= l(:notice_not_authorized_action) %> diff --git a/app/views/members/_membership_assignment.html.erb b/app/views/members/_membership_assignment.html.erb new file mode 100644 index 00000000..a22c43b4 --- /dev/null +++ b/app/views/members/_membership_assignment.html.erb @@ -0,0 +1,23 @@ +<% if projects.any? %> +
<%=l(:label_project_new)%> +<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => principal }) do %> +

<%= text_field_tag 'project_search', nil, :size => "40" %>

+ <%= observe_field(:project_search, + :frequency => 0.5, + :update => :projects, + :url => { :controller => 'auto_completes', :action => 'projects', :id => principal }, + :with => 'q') + %> + +
+ <%= principals_check_box_tags 'project_ids[]', projects %> +
+ +

<%= l(:label_role_plural) %>: +<% roles.each do |role| %> + +<% end %>

+

<%= submit_tag l(:button_add) %>

+<% end %> +
+<% end %> diff --git a/app/views/messages/show.rhtml b/app/views/messages/show.rhtml index 5908700c..df5966b3 100644 --- a/app/views/messages/show.rhtml +++ b/app/views/messages/show.rhtml @@ -63,4 +63,14 @@ <%= stylesheet_link_tag 'scm' %> <% end %> +<% content_for :sidebar do %> + <% if User.current.allowed_to?(:add_message_watchers, @project) || + (@topic.watchers.present? && User.current.allowed_to?(:view_message_watchers, @project)) %> +
+ <%= render :partial => 'watchers/watchers', :locals => {:watched => @topic} %> +
+ <% end %> + +<% end %> + <% html_title h(@topic.subject) %> diff --git a/app/views/projects/index.rhtml b/app/views/projects/index.rhtml index 5f15f824..6a0e0522 100644 --- a/app/views/projects/index.rhtml +++ b/app/views/projects/index.rhtml @@ -2,7 +2,7 @@ <%= auto_discovery_link_tag(:atom, {:action => 'index', :format => 'atom', :key => User.current.rss_key}) %> <% end %> -
+ diff --git a/app/views/queries/_form.rhtml b/app/views/queries/_form.rhtml index 5db801a2..132b59e7 100644 --- a/app/views/queries/_form.rhtml +++ b/app/views/queries/_form.rhtml @@ -16,6 +16,18 @@ <%= check_box_tag 'query_is_for_all', 1, @query.project.nil?, :disabled => (!@query.new_record? && (@query.project.nil? || (@query.is_public? && !User.current.admin?))) %>

+

+ +
+ +

+

<%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns', :onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %>

diff --git a/app/views/queries/_new_issue_button.html.erb b/app/views/queries/_new_issue_button.html.erb new file mode 100644 index 00000000..8563319b --- /dev/null +++ b/app/views/queries/_new_issue_button.html.erb @@ -0,0 +1,5 @@ +<% if @project %> +
+ <%= link_to_if_authorized(l(:label_issue_new), {:controller => 'issues', :action => 'new', :project_id => @project}, :title => l(:label_issue_new), :class => '') %> +
+<% end %> diff --git a/app/views/search/_quick_search.html.erb b/app/views/search/_quick_search.html.erb new file mode 100644 index 00000000..4dd8c0d6 --- /dev/null +++ b/app/views/search/_quick_search.html.erb @@ -0,0 +1,5 @@ + <%= label_tag("q", l(:label_search), :class => "hidden-for-sighted") %> + <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get) do %> + <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %> + <%= text_field_tag 'q', search_term, :size => 20, :class => 'search_field', :placeholder => l(:search_input_placeholder), :accesskey => accesskey(:quick_search) %> + <% end %> diff --git a/app/views/settings/_mail_handler.rhtml b/app/views/settings/_mail_handler.rhtml index b5ed14ea..722ba617 100644 --- a/app/views/settings/_mail_handler.rhtml +++ b/app/views/settings/_mail_handler.rhtml @@ -5,6 +5,9 @@ <%= setting_text_area :mail_handler_body_delimiters, :rows => 5 %>
<%= l(:text_line_separated) %>

+ +

<%= setting_check_box :mail_handler_confirmation_on_success %>

+

<%= setting_check_box :mail_handler_confirmation_on_failure %>

diff --git a/app/views/users/_mail_notifications.html.erb b/app/views/users/_mail_notifications.html.erb index 92bfa065..6d93db0d 100644 --- a/app/views/users/_mail_notifications.html.erb +++ b/app/views/users/_mail_notifications.html.erb @@ -4,8 +4,9 @@ :onchange => 'if (this.value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>

<% content_tag 'div', :id => 'notified-projects', :style => (@user.mail_notification == 'selected' ? '' : 'display:none;') do %> -

<% @user.projects.each do |project| %> -
+

<% project_tree(@user.projects.each) do |project,level| %> + <% name_prefix = (level > 0 ? (' ' * 2 * level + '» ') : '') %> +
<% end %>

<%= l(:text_user_mail_option) %>

<% end %> diff --git a/app/views/users/_memberships.rhtml b/app/views/users/_memberships.rhtml index 7d97c5f9..46ac8940 100644 --- a/app/views/users/_memberships.rhtml +++ b/app/views/users/_memberships.rhtml @@ -47,16 +47,5 @@
-<% if projects.any? %> -
<%=l(:label_project_new)%> -<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => @user }) do %> -<%= select_tag 'membership[project_id]', options_for_membership_project_select(@user, projects) %> -

<%= l(:label_role_plural) %>: -<% roles.each do |role| %> - -<% end %>

-

<%= submit_tag l(:button_add) %>

-<% end %> -
-<% end %> +<%= render :partial => 'members/membership_assignment', :locals => {:principal => @user, :projects => projects - @user.projects, :roles => roles } %>
diff --git a/app/views/versions/index.html.erb b/app/views/versions/index.html.erb index 0d132a73..09c44ce3 100644 --- a/app/views/versions/index.html.erb +++ b/app/views/versions/index.html.erb @@ -47,6 +47,7 @@ <% @versions.each do |version| %> <%= link_to format_version_name(version), "##{version.name}" %>
<% end %> + <% end %> <% html_title(l(:label_roadmap)) %> diff --git a/app/views/watchers/_watchers.rhtml b/app/views/watchers/_watchers.rhtml index 4da8c0dd..e6aba3e8 100644 --- a/app/views/watchers/_watchers.rhtml +++ b/app/views/watchers/_watchers.rhtml @@ -1,24 +1,31 @@
-<%= link_to_remote l(:button_add), - :url => {:controller => 'watchers', - :action => 'new', - :object_type => watched.class.name.underscore, - :object_id => watched} if User.current.allowed_to?(:add_issue_watchers, @project) %> +<%= link_to_function(l(:button_add), "$('new-watcher-form').toggle();") if User.current.allowed_to?("add_#{watched.class.name.underscore}_watchers".to_sym, @project) %>

<%= l(:label_issue_watchers) %> (<%= watched.watcher_users.size %>)

-<% unless @watcher.nil? %> +<% if User.current.allowed_to?("add_#{watched.class.name.underscore}_watchers".to_sym, @project) %> <% remote_form_for(:watcher, @watcher, :url => {:controller => 'watchers', :action => 'new', :object_type => watched.class.name.underscore, :object_id => watched}, :method => :post, - :html => {:id => 'new-watcher-form'}) do |f| %> -

<%= f.select :user_id, (watched.addable_watcher_users.collect {|m| [m.name, m.id]}), :prompt => "--- #{l(:actionview_instancetag_blank_option)} ---" %> + :html => {:id => 'new-watcher-form', :style => 'display:none;'}) do |f| %> + <% users = Principal.active.find(:all, :limit => 25) - watched.watcher_users %> +

<%= label_tag "user_search", l(:label_user_search) %><%= text_field_tag 'user_search', nil, :style => "width:98%;" %>

+ <%= observe_field(:user_search, + :frequency => 0.5, + :update => :users, + :url => auto_complete_users_path(:remove_watchers => watched.id, :klass => watched.class, :include_groups => true), + :with => 'q') + %> - <%= submit_tag l(:button_add) %> +
+ <%= principals_check_box_tags 'user_ids[]', users %> +
+ +

<%= submit_tag l(:button_add) %> <%= toggle_link l(:button_cancel), 'new-watcher-form'%>

<% end %> <% end %> diff --git a/app/views/wiki/show.rhtml b/app/views/wiki/show.rhtml index c144fb1d..b2d7e2d2 100644 --- a/app/views/wiki/show.rhtml +++ b/app/views/wiki/show.rhtml @@ -59,6 +59,14 @@ <% content_for :sidebar do %> <%= render :partial => 'wiki/sidebar' %> + + <% if User.current.allowed_to?(:add_wiki_page_watchers, @project) || + (@page.watchers.present? && User.current.allowed_to?(:view_wiki_page_watchers, @project)) %> +
+ <%= render :partial => 'watchers/watchers', :locals => {:watched => @page} %> +
+ <% end %> + <% end %> <% html_title h(@page.pretty_title) %> diff --git a/config/environment.rb b/config/environment.rb index 57f43194..84d6d1ff 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -52,6 +52,9 @@ Rails::Initializer.run do |config| # (by default production uses :info, the others :debug) # config.log_level = :debug + # Liquid drops + config.autoload_paths += %W( #{RAILS_ROOT}/app/drops ) + # Enable page/fragment caching by setting a file-based store # (remember to create the caching directory and make it readable to the application) # config.action_controller.cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache" diff --git a/config/locales/bg.yml b/config/locales/bg.yml index cacd42f1..be7fa6e0 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -984,3 +984,16 @@ bg: description_date_from: Въведете начална дата label_deleted_custom_field: (изтрито потребителÑко поле) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/bs.yml b/config/locales/bs.yml index 90a3585a..e50b5175 100644 --- a/config/locales/bs.yml +++ b/config/locales/bs.yml @@ -998,3 +998,16 @@ bs: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 3f401c03..049fb152 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -987,3 +987,16 @@ ca: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 7e4878d1..5297e8bf 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1208,3 +1208,16 @@ cs: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/da.yml b/config/locales/da.yml index 9f31e524..5fcb9669 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1000,3 +1000,16 @@ da: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/de.yml b/config/locales/de.yml index 3dfcbd49..3654836a 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -658,6 +658,7 @@ de: label_roadmap_overdue: "%{value} verspätet" label_roadmap_no_issues: Keine Tickets für diese Version label_search: Suche + search_input_placeholder: Suche ... label_result_plural: Resultate label_all_words: Alle Wörter label_wiki: Wiki @@ -1001,3 +1002,15 @@ de: description_date_from: Startdatum eintragen description_date_to: Enddatum eintragen field_custom_filter: Custom LDAP filter + label_toc: "Inhaltsverzeichnis" + text_display_subprojects: Display subprojects + text_current_project: Current project + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/el.yml b/config/locales/el.yml index c263231c..d5ecb70d 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -984,3 +984,16 @@ el: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 258d9a3f..a6fae066 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -988,3 +988,16 @@ en-GB: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/en.yml b/config/locales/en.yml index f2281b20..adec4598 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -157,6 +157,7 @@ en: notice_file_not_found: The page you were trying to access doesn't exist or has been removed. notice_locking_conflict: Data has been updated by another user. notice_not_authorized: You are not authorized to access this page. + notice_not_authorized_action: You are not authorized to perform this action. notice_not_authorized_archived_project: The project you're trying to access has been archived. notice_email_sent: "An email was sent to %{value}" notice_email_error: "An error occurred while sending mail (%{value})" @@ -368,6 +369,8 @@ en: setting_commit_logtime_activity_id: Activity for logged time setting_gantt_items_limit: Maximum number of items displayed on the gantt chart setting_issue_startdate_is_adddate: Use current date as start date for new issues + setting_mail_handler_confirmation_on_success: "Send confirmation email on successful incoming email" + setting_mail_handler_confirmation_on_failure: "Send confirmation email on failed incoming email" permission_add_project: Create project permission_add_subprojects: Create subprojects @@ -610,6 +613,7 @@ en: label_in_more_than: in more than label_greater_or_equal: '>=' label_less_or_equal: '<=' + label_between: "between" label_in: in label_today: today label_all_time: all time @@ -657,6 +661,7 @@ en: label_roadmap_overdue: "%{value} late" label_roadmap_no_issues: No issues for this version label_search: Search + search_input_placeholder: search ... label_result_plural: Results label_all_words: All words label_wiki: Wiki @@ -772,6 +777,7 @@ en: label_incoming_emails: Incoming emails label_generate_key: Generate a key label_issue_watchers: Watchers + label_document_watchers: Watchers label_example: Example label_display: Display label_sort: Sort @@ -814,6 +820,10 @@ en: label_notify_member_plural: Email issue updates label_path_encoding: Path encoding label_deleted_custom_field: '(deleted custom field)' + label_toc: "Contents" + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_failure: "Failed email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" button_login: Login button_submit: Submit @@ -937,6 +947,9 @@ en: text_default_encoding: "Default: UTF-8" text_mercurial_repo_example: "local repository (e.g. /hgrepo, c:\\hgrepo)" text_git_repo_example: "a bare and local repository (e.g. /gitrepo, c:\\gitrepo)" + text_display_subprojects: Display subprojects + text_current_project: Current project + text_mail_handler_confirmation_successful: "Your email has been successful added at the following url" default_role_manager: Manager default_role_developer: Developer diff --git a/config/locales/es.yml b/config/locales/es.yml index 2f70a467..ad68362b 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1021,3 +1021,16 @@ es: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/eu.yml b/config/locales/eu.yml index e7cb35bc..066b76e7 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -988,3 +988,16 @@ eu: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 4cc99a89..7e464ed8 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -987,3 +987,16 @@ fa: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/fi.yml b/config/locales/fi.yml index d7cda5f5..f6cb63e4 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -1005,3 +1005,16 @@ fi: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 3e767836..068f8ebc 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1002,3 +1002,16 @@ fr: description_choose_project: Projects description_date_from: Enter start date field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 89e05b4f..ee45c8f5 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -996,3 +996,16 @@ gl: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/he.yml b/config/locales/he.yml index c938af6c..ac0593ed 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -989,3 +989,16 @@ he: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/hr.yml b/config/locales/hr.yml index e6ec5237..5822a9c6 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -991,3 +991,16 @@ hr: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 567cce57..8162d1fd 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1003,3 +1003,16 @@ description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/id.yml b/config/locales/id.yml index 353df4b5..4ec1f23f 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -992,3 +992,16 @@ id: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/it.yml b/config/locales/it.yml index 0f84d881..ab439f26 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -985,3 +985,16 @@ it: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 1f3ed5e1..a5ceb8db 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1006,3 +1006,16 @@ ja: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 7647ba75..d9438607 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1036,3 +1036,16 @@ ko: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/lt.yml b/config/locales/lt.yml index a111eabf..2c513406 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -1044,3 +1044,16 @@ lt: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 2a7f1618..25732caa 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -979,3 +979,16 @@ lv: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/mk.yml b/config/locales/mk.yml index 5440a94e..37cbe499 100644 --- a/config/locales/mk.yml +++ b/config/locales/mk.yml @@ -984,3 +984,16 @@ mk: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/mn.yml b/config/locales/mn.yml index e28035c4..bf9e6ea6 100644 --- a/config/locales/mn.yml +++ b/config/locales/mn.yml @@ -985,3 +985,16 @@ mn: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 85a12b4f..5ebc55e0 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -966,3 +966,16 @@ nl: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/no.yml b/config/locales/no.yml index adda841a..4b450821 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -971,3 +971,16 @@ description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 83036445..d0b998d7 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -1001,3 +1001,16 @@ pl: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 72d31b13..7e7dc6ce 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1008,3 +1008,16 @@ pt-BR: description_date_from: Digita a data inicial label_deleted_custom_field: (campo personalizado excluído) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 5a85439e..ce6433f3 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -988,3 +988,16 @@ pt: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/ro.yml b/config/locales/ro.yml index 37623bc0..ce825acb 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -977,3 +977,16 @@ ro: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/ru.yml b/config/locales/ru.yml index ebd712c4..791498ef 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1097,3 +1097,16 @@ ru: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 638f4729..6abc7f78 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -979,3 +979,16 @@ sk: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/sl.yml b/config/locales/sl.yml index cce87fdf..1de519dc 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -980,3 +980,16 @@ sl: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml index 11c305ae..b0fc6add 100644 --- a/config/locales/sr-YU.yml +++ b/config/locales/sr-YU.yml @@ -984,3 +984,16 @@ sr-YU: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/sr.yml b/config/locales/sr.yml index c32682ed..998e6f46 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -985,3 +985,16 @@ sr: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/sv.yml b/config/locales/sv.yml index bc8a6bca..154827e8 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -1026,3 +1026,16 @@ sv: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/th.yml b/config/locales/th.yml index 2cb9bdd4..49d8065f 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -981,3 +981,16 @@ th: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/tr.yml b/config/locales/tr.yml index d2d25e36..ca084ff7 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1003,3 +1003,16 @@ tr: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/uk.yml b/config/locales/uk.yml index a3e5168d..b7d0afc6 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -980,3 +980,16 @@ uk: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 6b4c7b55..cc3c077d 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1035,3 +1035,16 @@ vi: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 002bee22..a85fd15f 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1066,3 +1066,16 @@ description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 9ba28719..6cdb9f0c 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -998,3 +998,16 @@ zh: description_date_from: Enter start date label_deleted_custom_field: (deleted custom field) field_custom_filter: Custom LDAP filter + text_display_subprojects: Display subprojects + text_current_project: Current project + label_toc: Contents + search_input_placeholder: search ... + setting_mail_handler_confirmation_on_success: Send confirmation email on successful incoming email + label_mail_handler_confirmation: "Confirmation of email submission: %{subject}" + label_mail_handler_errors_with_submission: "There were errors with your email submission:" + label_document_watchers: Watchers + setting_mail_handler_confirmation_on_failure: Send confirmation email on failed incoming email + label_between: between + label_mail_handler_failure: "Failed email submission: %{subject}" + notice_not_authorized_action: You are not authorized to perform this action. + text_mail_handler_confirmation_successful: Your email has been successful added at the following url diff --git a/config/routes.rb b/config/routes.rb index 6b77ac28..860c41c9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -87,6 +87,7 @@ ActionController::Routing::Routes.draw do |map| # Misc issue routes. TODO: move into resources map.auto_complete_issues '/issues/auto_complete', :controller => 'auto_completes', :action => 'issues' + map.auto_complete_users '/users/auto_complete', :controller => 'auto_completes', :action => 'users' map.preview_issue '/issues/preview/:id', :controller => 'previews', :action => 'issue' # TODO: would look nicer as /issues/:id/preview map.issues_context_menu '/issues/context_menu', :controller => 'context_menus', :action => 'issues' map.issue_changes '/issues/changes', :controller => 'journals', :action => 'index' @@ -94,6 +95,7 @@ ActionController::Routing::Routes.draw do |map| map.bulk_update_issue 'issues/bulk_edit', :controller => 'issues', :action => 'bulk_update', :conditions => { :method => :post } map.quoted_issue '/issues/:id/quoted', :controller => 'journals', :action => 'new', :id => /\d+/, :conditions => { :method => :post } map.connect '/issues/:id/destroy', :controller => 'issues', :action => 'destroy', :conditions => { :method => :post } # legacy + map.journal_diff '/journals/:id/diff/:field', :controller => 'journals', :action => 'diff', :conditions => { :method => :get } map.resource :gantt, :path_prefix => '/issues', :controller => 'gantts', :only => [:show, :update] map.resource :gantt, :path_prefix => '/projects/:project_id/issues', :controller => 'gantts', :only => [:show, :update] diff --git a/config/settings.yml b/config/settings.yml index 5f708e2d..91139c14 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -178,5 +178,9 @@ default_notification_option: default: 'only_my_events' emails_header: default: '' +mail_handler_confirmation_on_success: + default: 1 +mail_handler_confirmation_on_failure: + default: 1 issue_startdate_is_adddate: - default: 1 \ No newline at end of file + default: 1 diff --git a/db/migrate/20111025231354_add_display_subprojects_to_query.rb b/db/migrate/20111025231354_add_display_subprojects_to_query.rb new file mode 100644 index 00000000..385c484e --- /dev/null +++ b/db/migrate/20111025231354_add_display_subprojects_to_query.rb @@ -0,0 +1,23 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class AddDisplaySubprojectsToQuery < ActiveRecord::Migration + def self.up + add_column :queries, :display_subprojects, :boolean + end + + def self.down + remove_column :queries, :display_subprojects + end +end diff --git a/db/migrate/20111125191432_acts_as_taggable_on_migration.rb b/db/migrate/20111125191432_acts_as_taggable_on_migration.rb new file mode 100644 index 00000000..b9536db0 --- /dev/null +++ b/db/migrate/20111125191432_acts_as_taggable_on_migration.rb @@ -0,0 +1,43 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +class ActsAsTaggableOnMigration < ActiveRecord::Migration + def self.up + create_table :tags do |t| + t.column :name, :string + end + + create_table :taggings do |t| + t.column :tag_id, :integer + t.column :taggable_id, :integer + t.column :tagger_id, :integer + t.column :tagger_type, :string + + # You should make sure that the column created is + # long enough to store the required class names. + t.column :taggable_type, :string + t.column :context, :string + + t.column :created_at, :datetime + end + + add_index :taggings, :tag_id + add_index :taggings, [:taggable_id, :taggable_type, :context] + end + + def self.down + drop_table :taggings + drop_table :tags + end +end diff --git a/doc/CHANGELOG.rdoc b/doc/CHANGELOG.rdoc index 89374a9f..945696e1 100644 --- a/doc/CHANGELOG.rdoc +++ b/doc/CHANGELOG.rdoc @@ -1,4 +1,52 @@ -= ChiliProject changelog += ChiliProject Changelog + +== 2012-02-06 v3.0.0 + +* Bug #826: Top right toolbar items overlap for custom issues query +* Feature #843: Add config/setup_load_paths.rb to .gitignore + +== 2012-01-18 v3.0.0-beta2 + +* Bug #558: Reduce version information from Help link +* Bug #774: Gravatar on issue#show is at a weird position +* Bug #778: Textile Caching breaks Liquid +* Bug #780: Setting Cache is not invalidated properly +* Bug #783: Link to new issue on issues list displayed although user is not allowed to create issue +* Bug #791: Allow SSL in POP3 in receive_pop3 task +* Bug #797: Wiki page list is shown as one long list and not a nested one +* Bug #798: Sidebar design looks bad +* Bug #807: History elements overlays revisions in Issues +* Bug #815: Inconsistent margin used for gravatars +* Bug #827: Group issues by the Status field +* Feature #672: Allow queries to include subproject issues +* Feature #674: Change outgoing email to be sent-per user and not as a single BCC email +* Feature #790: Allow plugins to register custom static and lazy evaluated variables +* Feature #792: Confirmation emails when an incoming email is submitted +* Feature #796: Filter issues based on a date range +* Feature #799: Watch documents +* Feature #800: Allow non-members to watch issues +* Feature #801: Bulk adding issue watchers +* Feature #802: Allow groups to watch issues +* Feature #805: Set watchers on a wiki page +* Feature #806: Set watchers on a Forum or Forum Thread +* Feature #808: Show description changes on issues in a diff +* Feature #809: Bulk add and search for projects when adding a member + +== 2011-12-18 v3.0.0-beta1 + +* Bug #653: Multiple query related models defined in one file +* Bug #734: Liquid integration breaks on 1.9.2 +* Bug #766: Broken formatting for TOCs +* Feature #263: New layout +* Feature #329: Forum replies poorly highlighted +* Feature #604: Liquid Markup on top of Textile +* Feature #649: Update CodeRay to 1.0.0 +* Feature #658: Include jquery and jquery ui +* Feature #672: Allow queries to include subproject issues +* Feature #692: new design with 263-new-layout-ready +* Feature #720: Tagging +* Feature #747: Include capybara for integration tests +* Feature #760: Update quick search to use a partial == 2012-02-06 v2.7.0 diff --git a/lib/chili_project/compatibility.rb b/lib/chili_project/compatibility.rb index 12048fca..0e4b162d 100644 --- a/lib/chili_project/compatibility.rb +++ b/lib/chili_project/compatibility.rb @@ -28,7 +28,14 @@ module ChiliProject # # Released: ChiliProject 2.5.0 def self.using_jquery? - false + true + end + + # Is Liquid markup available? + # + # Released: ChiliProject 3.0.0 + def self.using_liquid? + true end # Catch-all to be overwritten be future compatibility checks. diff --git a/lib/chili_project/info.rb b/lib/chili_project/info.rb new file mode 100644 index 00000000..02e14b35 --- /dev/null +++ b/lib/chili_project/info.rb @@ -0,0 +1,31 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject + module Info + class << self + def app_name; 'ChiliProject' end + def url; 'https://www.chiliproject.org/' end + def help_url + "https://www.chiliproject.org/help/v#{ChiliProject::VERSION::MAJOR.to_s}" + end + def versioned_name; "#{app_name} #{Redmine::VERSION}" end + + # Creates the url string to a specific Redmine issue + def issue(issue_id) + url + 'issues/' + issue_id.to_s + end + end + end +end \ No newline at end of file diff --git a/lib/chili_project/liquid.rb b/lib/chili_project/liquid.rb new file mode 100644 index 00000000..1c65fc1f --- /dev/null +++ b/lib/chili_project/liquid.rb @@ -0,0 +1,23 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require 'chili_project/liquid/liquid_ext' +require 'chili_project/liquid/filters' +require 'chili_project/liquid/tags' + +module ChiliProject + module Liquid + Liquid::Template.file_system = FileSystem.new + end +end \ No newline at end of file diff --git a/lib/chili_project/liquid/file_system.rb b/lib/chili_project/liquid/file_system.rb new file mode 100644 index 00000000..019e5189 --- /dev/null +++ b/lib/chili_project/liquid/file_system.rb @@ -0,0 +1,32 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject + module Liquid + class FileSystem + def read_template_file(template_name, context) + raise ::Liquid::FileSystemError.new("Page not found") if template_name.blank? + project = Project.find(context['project'].identifier) if context['project'].present? + + cross_project_page = template_name.include?(':') + page = Wiki.find_page(template_name.to_s.strip, :project => (cross_project_page ? nil : project)) + if page.nil? || !page.visible? + raise ::Liquid::FileSystemError.new("No such page '#{template_name}'") + end + + page.content + end + end + end +end \ No newline at end of file diff --git a/lib/chili_project/liquid/filters.rb b/lib/chili_project/liquid/filters.rb new file mode 100644 index 00000000..1dfcbfdd --- /dev/null +++ b/lib/chili_project/liquid/filters.rb @@ -0,0 +1,99 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject + module Liquid + module OverriddenFilters + # These filters are defined in liquid core but are overwritten here + # to improve on their implementation + + # Split input string into an array of substrings separated by given pattern. + # Default to whitespace + def split(input, pattern=nil) + input.split(pattern) + end + + def strip_newlines(input) + input.to_s.gsub(/\r?\n/, '') + end + + # Add
tags in front of all newlines in input string + def newline_to_br(input) + input.to_s.gsub(/(\r?\n)/, "
\1") + end + + # Use the block systax for sub and gsub to prevent interpretation of + # backreferences + # See https://gist.github.com/1491437 + def replace(input, string, replacement = '') + input.to_s.gsub(string){replacement} + end + + # Replace the first occurrences of a string with another + def replace_first(input, string, replacement = '') + input.to_s.sub(string){replacement} + end + + # Get the first element(s) of the passed in array + # Example: + # {{ product.images | first | to_img }} + def first(array, count=nil) + return array.first if count.nil? && array.respond_to?(:first) + if array.respond_to?(:[]) + count.to_i > 0 ? array[0..count.to_i-1] : [] + end + end + + # Get the last element(s) of the passed in array + # Example: + # {{ product.images | last | to_img }} + def last(array, count=nil) + array.last if count=nil? && array.respond_to?(:last) + if array.respond_to?(:[]) + count.to_i > 0 ? array[(count.to_i * -1)..-1] : [] + end + end + end + + module Filters + def default(input, default) + input.to_s.strip.present? ? input : default + end + + def strip(input) + input.to_s.strip + end + + def to_list(array, header_or_depth = nil) + result = [] + if header_or_depth.is_a?(String) + result << "\np. #{header_or_depth}\n" + depth = 1 + else + if header_or_depth.respond_to?(:to_i) + depth = [1, header_or_depth.to_i].max + else + depth = 1 + end + end + + result += (array || []).collect{|elm| "#{"*" * depth.to_i } #{elm.to_s}"} + result.join("\n") + end + end + + Template.register_filter(OverriddenFilters) + Template.register_filter(Filters) + end +end diff --git a/lib/chili_project/liquid/legacy.rb b/lib/chili_project/liquid/legacy.rb new file mode 100644 index 00000000..10d15c0e --- /dev/null +++ b/lib/chili_project/liquid/legacy.rb @@ -0,0 +1,87 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject + module Liquid + # Legacy is used to support older Redmine style macros by converting + # them to Liquid objects (tags, filters) on the fly by doing basic + # string substitution. This is done before the Liquid processing + # so the converted macros work like normal + # + module Legacy + # Holds the list of legacy macros + # + # @param [Regexp] :match The regex to match on the legacy macro + # @param [String] :replace The string to replace with. E.g. "%" converts + # "{{ }}" to "{% %}" + # @param [String] :new_name The new name of the Liquid object + def self.macros + @macros ||= {} + end + + # "Runs" legacy macros by doing a gsub of their values to the new Liquid ones + # + # @param [String] content The pre-Liquid content + def self.run_macros(content) + macros.each do |macro_name, macro| + next unless macro[:match].present? && macro[:replace].present? + content = content.gsub(macro[:match]) do |match| + # Use block form so $1 and $2 are set properly + args = " '#{$2}'" if $2 + "{#{macro[:replace]} #{macro[:new_name]}#{args} #{macro[:replace]}}" + end + end + content + end + + # Add support for a legacy macro syntax that was converted to liquid + # + # @param [String] name The legacy macro name + # @param [Symbol] liquid_type The type of Liquid object to use. Supported: :tag + # @param [optional, String] new_name The new name of the liquid object, used + # to rename a macro + def self.add(name, liquid_type, new_name=nil) + new_name = name unless new_name.present? + case liquid_type + when :tag + + macros[name.to_s] = { + # Example values the regex matches + # {{name}} + # {{ name }} + # {{ name 'arg' }} + # {{ name('arg') }} + :match => Regexp.new(/\{\{(#{name})(?:\(([^\}]*)\))?\}\}/), + :replace => "%", + :new_name => new_name + } + end + end + end + + # FIXME: remove the deprecated syntax for 4.0, provide a way to migrate + # existing pages to the new syntax. + # + # See ChiliProject::Liquid::Tags for the registration of the tags. + Legacy.add('child_pages', :tag) + Legacy.add('hello_world', :tag) + Legacy.add('include', :tag) + + # Transform the old textile TOC tags to syntax suported by liquid + Legacy.add('toc', :tag) + Legacy.add('>toc', :tag, "toc_right") + Legacy.add('' + html_result(html) + end + + def html_result(html) + key = nil + while key.nil? || html_results.has_key?(key) + random = ActiveSupport::SecureRandom.hex(10) + # This string must be passed untouched through Liquid and textile + # It mustn't be changed in any way by any rendering stage. + key = "!!html_results.#{random}!!" + end + html_results[key] = html + key + end + + def html_results + registers[:html_results] ||= {} + end + + def cacheable? + registers.has_key?(:cachable) ? !!registers[:cachable] : true + end + + def not_cachable! + registers[:cachable] = false + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/chili_project/liquid/liquid_ext/strainer.rb b/lib/chili_project/liquid/liquid_ext/strainer.rb new file mode 100644 index 00000000..f2b65c50 --- /dev/null +++ b/lib/chili_project/liquid/liquid_ext/strainer.rb @@ -0,0 +1,44 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +# Required until https://github.com/Shopify/liquid/pull/87 got merged upstream +module ChiliProject + module Liquid + module LiquidExt + module Strainer + def self.included(base) + base.extend(ClassMethods) + + base.class_attribute :filters, :instance_reader => false, :instance_writer => false + base.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + self.filters = @@filters.values + RUBY + end + + module ClassMethods + def global_filter(filter) + raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module) + filters += [filter] + end + + def create(context) + strainer = self.new(context) + filters.each { |filter| strainer.extend(filter) } + strainer + end + end + end + end + end +end diff --git a/lib/chili_project/liquid/tags.rb b/lib/chili_project/liquid/tags.rb new file mode 100644 index 00000000..be84568c --- /dev/null +++ b/lib/chili_project/liquid/tags.rb @@ -0,0 +1,54 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject::Liquid + module Tags + class TagError < StandardError; end + + def self.register_tag(name, klass, options={}) + if options[:html] + html_class = Class.new do + def render(context) + result = @tag.render(context) + context.html_result(result) + end + + def method_missing(*args, &block) + @tag.send(*args, &block) + end + end + html_class.send :define_method, :initialize do |*args| + @tag = klass.new(*args) + end + ::Liquid::Template.register_tag(name, html_class) + else + ::Liquid::Template.register_tag(name, klass) + end + end + + register_tag('child_pages', ChildPages, :html => true) + register_tag('hello_world', HelloWorld) + register_tag('include', Include, :html => true) + + # Output these tags again as they were typed + # These are to be handled later + register_tag('toc', Identity, :html => true) + register_tag('toc_left', Identity, :html => true) + register_tag('toc_right', Identity, :html => true) + + # See ChiliProject::Liquid::Legacy for the definition of legacy tags, + # most of which are also defined here + end +end + diff --git a/lib/chili_project/liquid/tags/child_pages.rb b/lib/chili_project/liquid/tags/child_pages.rb new file mode 100644 index 00000000..8bc06e86 --- /dev/null +++ b/lib/chili_project/liquid/tags/child_pages.rb @@ -0,0 +1,74 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject::Liquid::Tags + class ChildPages < Tag + def initialize(tag_name, markup, tokens) + markup = markup.strip.gsub(/["']/, '') + if markup.present? + tag_args = markup.split(',') + @args, @options = extract_macro_options(tag_args, :parent) + else + @args = [] + @options = {} + end + super + end + + def render(context) + # inside of a project + @project = Project.find(context['project'].identifier) if context['project'].present? + + if @args.present? + page_name = @args.first.to_s + cross_project_page = page_name.include?(':') + + page = Wiki.find_page(page_name, :project => (cross_project_page ? nil : @project)) + # FIXME: :object and :attribute should be variables, not registers + elsif context.registers[:object].is_a?(WikiContent) + page = context.registers[:object].page + page_name = page.title + elsif @project + return render_all_pages(context) + else + raise TagError.new('With no argument, this tag can be called from projects only.') + end + + raise TagError.new("No such page '#{page_name}'") if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project) + pages = ([page] + page.descendants).group_by(&:parent_id) + context.registers[:view].render_page_hierarchy(pages, @options[:parent] ? page.parent_id : page.id) + end + + private + def render_all_pages(context) + return '' unless @project.wiki.present? && @project.wiki.pages.present? + raise TagError.new('Page not found') if !User.current.allowed_to?(:view_wiki_pages, @project) + + context.registers[:view].render_page_hierarchy(@project.wiki.pages.group_by(&:parent_id)) + end + + # @param args [Array, String] An array of strings in "key=value" format + # @param keys [Hash, Symbol] List of keyword args to extract + def extract_macro_options(args, *keys) + options = {} + args.each do |arg| + if arg.to_s.gsub(/["']/,'').strip =~ %r{^(.+)\=(.+)$} && keys.include?($1.downcase.to_sym) + options[$1.downcase.to_sym] = $2 + args.pop + end + end + return [args, options] + end + end +end \ No newline at end of file diff --git a/lib/chili_project/liquid/tags/hello_world.rb b/lib/chili_project/liquid/tags/hello_world.rb new file mode 100644 index 00000000..f6dee72f --- /dev/null +++ b/lib/chili_project/liquid/tags/hello_world.rb @@ -0,0 +1,25 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject::Liquid::Tags + class HelloWorld < Tag + def initialize(tag_name, markup, tokens) + super + end + + def render(context) + "Hello world!" + end + end +end \ No newline at end of file diff --git a/lib/chili_project/liquid/tags/identity.rb b/lib/chili_project/liquid/tags/identity.rb new file mode 100644 index 00000000..36b93b51 --- /dev/null +++ b/lib/chili_project/liquid/tags/identity.rb @@ -0,0 +1,28 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject::Liquid::Tags + class Identity < Tag + def initialize(tag_name, markup, tokens) + @tag_name = tag_name + @markup = markup + @tokens = tokens + super + end + + def render(context) + "{% #{@tag_name} %}" + end + end +end \ No newline at end of file diff --git a/lib/chili_project/liquid/tags/include.rb b/lib/chili_project/liquid/tags/include.rb new file mode 100644 index 00000000..53c81fd6 --- /dev/null +++ b/lib/chili_project/liquid/tags/include.rb @@ -0,0 +1,83 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject::Liquid::Tags + class Include < ::Liquid::Include + + # This method follows the basic flow of the default include in liquid + # We just add some additional flexibility. This method can be removed once + # https://github.com/Shopify/liquid/pull/78 got accepted + def render(context) + context.stack do + template = _read_template_from_file_system(context) + partial = Liquid::Template.parse _template_source(template) + variable = context[@variable_name || @template_name[1..-2]] + + @attributes.each do |key, value| + context[key] = context[value] + end + + if variable.is_a?(Array) + variable.collect do |variable| + context[@template_name[1..-2]] = variable + _render_partial(partial, template, context) + end + else + context[@template_name[1..-2]] = variable + _render_partial(partial, template, context) + end + end + end + + private + def break_circle(context) + context.registers[:included_pages] ||= [] + + project = context['project'].identifier if context['project'].present? + template_name = context[@template_name] + cross_project_page = template_name.include?(':') + page_title = cross_project_page ? template_name : "#{project}:#{template_name}" + + raise ::Liquid::FileSystemError.new("Circular inclusion detected") if context.registers[:included_pages].include?(page_title) + context.registers[:included_pages] << page_title + + yield + ensure + context.registers[:included_pages].pop + end + + def _template_source(wiki_content) + wiki_content.text + end + + def _render_partial(partial, template, context) + break_circle(context) do + textile = partial.render(context) + + # Call textilizable on the view so all of the helpers are loaded + # based on the view and not this tag + context.registers[:view].textilizable(textile, :attachments => template.page.attachments, :headings => false, :object => template) + end + end + + def _read_template_from_file_system(context) + wiki_content = super + + # Set the new project to that additional includes use the correct + # base project + context['project'] = wiki_content.page.wiki.project + wiki_content + end + end +end diff --git a/lib/chili_project/liquid/tags/tag.rb b/lib/chili_project/liquid/tags/tag.rb new file mode 100644 index 00000000..361334d3 --- /dev/null +++ b/lib/chili_project/liquid/tags/tag.rb @@ -0,0 +1,18 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject::Liquid::Tags + class Tag < ::Liquid::Tag + end +end diff --git a/lib/chili_project/liquid/template.rb b/lib/chili_project/liquid/template.rb new file mode 100644 index 00000000..d4e4ad9f --- /dev/null +++ b/lib/chili_project/liquid/template.rb @@ -0,0 +1,137 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject + module Liquid + class Template < ::Liquid::Template + # creates a new Template object from liquid source code + def self.parse(source) + template = self.new + template.parse(source) + template + end + + + def context_from_render_options(*args) + # This method is pulled out straight from the original + # Liquid::Template#render + context = case args.first + when ::Liquid::Context + args.shift + when Hash + ::Liquid::Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors) + when nil + ::Liquid::Context.new(assigns, instance_assigns, registers, @rethrow_errors) + else + raise ArgumentError, "Expect Hash or Liquid::Context as parameter" + end + + case args.last + when Hash + options = args.pop + + if options[:registers].is_a?(Hash) + self.registers.merge!(options[:registers]) + end + + if options[:filters] + context.add_filters(options[:filters]) + end + + when Module + context.add_filters(args.pop) + when Array + context.add_filters(args.pop) + end + context + end + + # Render takes a hash with local variables. + # + # if you use the same filters over and over again consider registering them globally + # with Template.register_filter + # + # Following options can be passed: + # + # * filters : array with local filters + # * registers : hash with register variables. Those can be accessed from + # filters and tags and might be useful to integrate liquid more with its host application + # + def render(*args) + return '' if @root.nil? + + context = context_from_render_options(*args) + context.registers[:html_results] ||= {} + + obj = context.registers[:object] + attribute = context.registers[:attribute] + + if Setting.cache_formatted_text? && cache_key = cache_key_for(Setting.text_formatting, obj, attribute) + # Text retrieved from the cache store may be frozen + # We need to dup it so we can do in-place substitutions with gsub! + result = Rails.cache.fetch(cache_key) + result ||= begin + result = render_context(context) + Rails.cache.write(cache_key, result) if context.cacheable? + result + end.dup + else + render_context(context) + end + end + + def render_context(context) + return '' if @root.nil? + + # ENTER THE RENDERING STAGE + + # 1. Render the input as Liquid + # Some tags might register final HTML output in this stage. + begin + # for performance reasons we get a array back here. join will make a string out of it + result = @root.render(context) + result.respond_to?(:join) ? result.join : result + ensure + @errors = context.errors + end + + # 2. Perform the Wiki markup transformation (e.g. Textile) + obj = context.registers[:object] + attribute = context.registers[:attribute] + result = Redmine::WikiFormatting.to_html(Setting.text_formatting, result, :object => obj, :attribute => attribute) + + # 3. Now finally, replace the captured raw HTML bits in the final content + length = nil + # replace HTML results until we can find no additional variables + while length != context.registers[:html_results].length do + length = context.registers[:html_results].length + context.registers[:html_results].delete_if do |key, value| + # We use the block variant to avoid the evaluation of escaped + # characters in +value+ during substitution. + result.sub!(key) { |match| value } + end + end + + result + end + + private + def cache_key_for(format, object, attribute) + if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank? + "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}" + end + end + end + end +end diff --git a/lib/chili_project/liquid/variables.rb b/lib/chili_project/liquid/variables.rb new file mode 100644 index 00000000..b9c19323 --- /dev/null +++ b/lib/chili_project/liquid/variables.rb @@ -0,0 +1,61 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module ChiliProject + module Liquid + module Variables + class LazyVariable + def initialize(context, &block) + @block = block + @context = context + end + + def to_liquid() + (@block.arity == 0) ? @block.call : @block.call(@context) + end + end + + # Register a Liquid variable. + # Pass a value to register a fixed variable or a block to create a lazy + # evaluated variable. The block can take the current context + def self.register(name, value=nil, &block) + var = block_given? ? Proc.new(){|ctx| LazyVariable.new(ctx, &block)} : value + all[name.to_s] = var + end + + def self.all + @variables ||= {} + end + + register "tags" do + ::Liquid::Template.tags.keys.sort + end + + register "variables" do |context| + vars = [] + + vars = context.environments.first.keys.reject do |var| + # internal variable + var == "text" + end if context.environments.present? + vars += context.scopes.collect(&:keys).flatten + vars.uniq.sort + end + + # DEPRACATED: This is just a hint on how to use Liquid introspection + register "macro_list", + "Use '{{ variables | to_list: \"Variables:\" }}' to see all Liquid variables and '{{ tags | to_list: \"Tags:\" }}' to see all of the Liquid tags." + end + end +end diff --git a/lib/chili_project/version.rb b/lib/chili_project/version.rb index d0e9a7ba..80654518 100644 --- a/lib/chili_project/version.rb +++ b/lib/chili_project/version.rb @@ -17,8 +17,8 @@ require 'rexml/document' module ChiliProject module VERSION #:nodoc: - MAJOR = 2 - MINOR = 7 + MAJOR = 3 + MINOR = 0 PATCH = 0 TINY = PATCH # Redmine compat diff --git a/lib/redmine.rb b/lib/redmine.rb index 78a0c892..832dd56f 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -116,6 +116,9 @@ Redmine::AccessControl.map do |map| map.project_module :documents do |map| map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment]}, :require => :loggedin map.permission :view_documents, :documents => [:index, :show, :download] + map.permission :view_document_watchers, {} + map.permission :add_document_watchers, {:watchers => :new} + map.permission :delete_document_watchers, {:watchers => :destroy} end map.project_module :files do |map| @@ -133,6 +136,9 @@ Redmine::AccessControl.map do |map| map.permission :edit_wiki_pages, :wiki => [:edit, :update, :preview, :add_attachment] map.permission :delete_wiki_pages_attachments, {} map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member + map.permission :view_wiki_page_watchers, {} + map.permission :add_wiki_page_watchers, {:watchers => :new} + map.permission :delete_wiki_page_watchers, {:watchers => :destroy} end map.project_module :repository do |map| @@ -150,6 +156,14 @@ Redmine::AccessControl.map do |map| map.permission :edit_own_messages, {:messages => :edit}, :require => :loggedin map.permission :delete_messages, {:messages => :destroy}, :require => :member map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin + map.permission :view_board_watchers, {} + map.permission :add_board_watchers, {:watchers => :new} + map.permission :delete_board_watchers, {:watchers => :destroy} + + map.permission :view_message_watchers, {} + map.permission :add_message_watchers, {:watchers => :new} + map.permission :delete_message_watchers, {:watchers => :destroy} + end map.project_module :calendar do |map| @@ -166,12 +180,10 @@ Redmine::MenuManager.map :top_menu do |menu| menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? } menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true - menu.push :help, Redmine::Info.help_url, :last => true + menu.push :help, Redmine::Info.help_url, :last => true, :caption => "?" end Redmine::MenuManager.map :account_menu do |menu| - menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? } - menu.push :register, { :controller => 'account', :action => 'register' }, :if => Proc.new { !User.current.logged? && Setting.self_registration? } menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? } menu.push :logout, :signout_path, :if => Proc.new { User.current.logged? } end diff --git a/lib/redmine/info.rb b/lib/redmine/info.rb index 8d1b4c09..94d2a276 100644 --- a/lib/redmine/info.rb +++ b/lib/redmine/info.rb @@ -13,19 +13,6 @@ #++ module Redmine - module Info - class << self - def app_name; 'ChiliProject' end - def url; 'https://www.chiliproject.org/' end - def help_url - "https://www.chiliproject.org/help/v#{Redmine::VERSION.to_semver}" - end - def versioned_name; "#{app_name} #{Redmine::VERSION}" end - - # Creates the url string to a specific Redmine issue - def issue(issue_id) - url + 'issues/' + issue_id.to_s - end - end - end + # THIS IS A REDMINE COMPATIBILITY INTERFACE + Info = ChiliProject::Info end diff --git a/lib/redmine/pop3.rb b/lib/redmine/pop3.rb index ac33b427..b0a1e162 100644 --- a/lib/redmine/pop3.rb +++ b/lib/redmine/pop3.rb @@ -18,8 +18,20 @@ module Redmine module POP3 class << self def check(pop_options={}, options={}) + if pop_options[:ssl] + ssl = true + if pop_options[:ssl] == 'force' + Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE) + else + Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_PEER) + end + else + ssl = false + end + host = pop_options[:host] || '127.0.0.1' - port = pop_options[:port] || '110' + port = pop_options[:port] + port ||= ssl ? '995' : '110' apop = (pop_options[:apop].to_s == '1') delete_unprocessed = (pop_options[:delete_unprocessed].to_s == '1') diff --git a/lib/redmine/syntax_highlighting.rb b/lib/redmine/syntax_highlighting.rb index 36bb9f8b..5b8fa265 100644 --- a/lib/redmine/syntax_highlighting.rb +++ b/lib/redmine/syntax_highlighting.rb @@ -43,7 +43,7 @@ module Redmine # Highlights +text+ using +language+ syntax # Should not return outer pre tag def highlight_by_language(text, language) - ::CodeRay.scan(text, language).html(:line_numbers => :inline, :wrap => :span) + ::CodeRay.scan(text, language).html(:line_numbers => :inline, :line_number_anchors => false, :wrap => :span) end end end diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb index 1ffd7290..7a78fbcb 100644 --- a/lib/redmine/wiki_formatting.rb +++ b/lib/redmine/wiki_formatting.rb @@ -41,58 +41,7 @@ module Redmine end def to_html(format, text, options = {}, &block) - text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute]) - # Text retrieved from the cache store may be frozen - # We need to dup it so we can do in-place substitutions with gsub! - cache_store.fetch cache_key do - formatter_for(format).new(text).to_html - end.dup - else - formatter_for(format).new(text).to_html - end - if block_given? - execute_macros(text, block) - end - text - end - - # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done - def cache_key_for(format, object, attribute) - if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank? - "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}" - end - end - - # Returns the cache store used to cache HTML output - def cache_store - ActionController::Base.cache_store - end - - MACROS_RE = / - (!)? # escaping - ( - \{\{ # opening tag - ([\w]+) # macro name - (\(([^\}]*)\))? # optional arguments - \}\} # closing tag - ) - /x unless const_defined?(:MACROS_RE) - - # Macros substitution - def execute_macros(text, macros_runner) - text.gsub!(MACROS_RE) do - esc, all, macro = $1, $2, $3.downcase - args = ($5 || '').split(',').each(&:strip) - if esc.nil? - begin - macros_runner.call(macro, args) - rescue => e - "
Error executing the #{macro} macro (#{e})
" - end || all - else - all - end - end + formatter_for(format).new(text).to_html end end end diff --git a/lib/redmine/wiki_formatting/macros.rb b/lib/redmine/wiki_formatting/macros.rb index 8830c39f..3b7468bf 100644 --- a/lib/redmine/wiki_formatting/macros.rb +++ b/lib/redmine/wiki_formatting/macros.rb @@ -12,106 +12,55 @@ # See doc/COPYRIGHT.rdoc for more details. #++ +# DECREACATED SINCE 3.0 - TO BE REMOVED IN 4.0 +# The whole macro concept is deprecated. It is to be completely replaced by +# Liquid tags and variables. + +require 'dispatcher' + module Redmine module WikiFormatting module Macros - module Definitions - def exec_macro(name, obj, args) - method_name = "macro_#{name}" - send(method_name, obj, args) if respond_to?(method_name) - end - - def extract_macro_options(args, *keys) - options = {} - while args.last.to_s.strip =~ %r{^(.+)\=(.+)$} && keys.include?($1.downcase.to_sym) - options[$1.downcase.to_sym] = $2 - args.pop - end - return [args, options] - end - end - - @@available_macros = {} + @available_macros = {} class << self - # Called with a block to define additional macros. - # Macro blocks accept 2 arguments: - # * obj: the object that is rendered - # * args: macro arguments - # - # Plugins can use this method to define new macros: - # - # Redmine::WikiFormatting::Macros.register do - # desc "This is my macro" - # macro :my_macro do |obj, args| - # "My macro output" - # end - # end def register(&block) + ActiveSupport::Deprecation.warn("Macros are deprecated. Use Liquid filters and tags instead", caller.drop(3)) class_eval(&block) if block_given? end private + # Sets description for the next macro to be defined + def desc(txt) + @desc = txt + end + # Defines a new macro with the given name and block. def macro(name, &block) name = name.to_sym if name.is_a?(String) - @@available_macros[name] = @@desc || '' - @@desc = nil + @available_macros[name] = @desc || '' + @desc = nil raise "Can not create a macro without a block!" unless block_given? - Definitions.send :define_method, "macro_#{name}".downcase, &block + + tag = Class.new(::Liquid::Tag) do + def initialize(tag_name, markup, tokens) + if markup =~ self.class::Syntax + @args = $1[1..-2].split(',').collect(&:strip) + else + raise ::Liquid::SyntaxError.new("Syntax error in tag '#{name}'") + end + end + end + tag.send :define_method, :render do |context| + context.registers[:view].instance_exec context.registers[:object], @args, &block + end + tag.const_set 'Syntax', /(#{::Liquid::QuotedFragment})/ + + Dispatcher.to_prepare do + ChiliProject::Liquid::Tags.register_tag(name, tag, :html => true) + ChiliProject::Liquid::Legacy.add(name, :tag) + end end - - # Sets description for the next macro to be defined - def desc(txt) - @@desc = txt - end - end - - # Builtin macros - desc "Sample macro." - macro :hello_world do |obj, args| - "Hello world! Object: #{obj.class.name}, " + (args.empty? ? "Called with no argument." : "Arguments: #{args.join(', ')}") - end - - desc "Displays a list of all available macros, including description if available." - macro :macro_list do - out = '' - @@available_macros.keys.collect(&:to_s).sort.each do |macro| - out << content_tag('dt', content_tag('code', macro)) - out << content_tag('dd', textilizable(@@available_macros[macro.to_sym])) - end - content_tag('dl', out) - end - - desc "Displays a list of child pages. With no argument, it displays the child pages of the current wiki page. Examples:\n\n" + - " !{{child_pages}} -- can be used from a wiki page only\n" + - " !{{child_pages(Foo)}} -- lists all children of page Foo\n" + - " !{{child_pages(Foo, parent=1)}} -- same as above with a link to page Foo" - macro :child_pages do |obj, args| - args, options = extract_macro_options(args, :parent) - page = nil - if args.size > 0 - page = Wiki.find_page(args.first.to_s, :project => @project) - elsif obj.is_a?(WikiContent) - page = obj.page - else - raise 'With no argument, this macro can be called from wiki pages only.' - end - raise 'Page not found' if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project) - pages = ([page] + page.descendants).group_by(&:parent_id) - render_page_hierarchy(pages, options[:parent] ? page.parent_id : page.id) - end - - desc "Include a wiki page. Example:\n\n !{{include(Foo)}}\n\nor to include a page of a specific project wiki:\n\n !{{include(projectname:Foo)}}" - macro :include do |obj, args| - page = Wiki.find_page(args.first.to_s, :project => @project) - raise 'Page not found' if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project) - @included_wiki_pages ||= [] - raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.title) - @included_wiki_pages << page.title - out = textilizable(page.content, :text, :attachments => page.attachments, :headings => false) - @included_wiki_pages.pop - out end end end diff --git a/lib/tasks/email.rake b/lib/tasks/email.rake index 9c264e03..c61f853b 100644 --- a/lib/tasks/email.rake +++ b/lib/tasks/email.rake @@ -140,6 +140,7 @@ Available POP3 options: username=USERNAME POP3 account password=PASSWORD POP3 password apop=1 use APOP authentication (default: false) + ssl=SSL Use SSL? (default: false) delete_unprocessed=1 delete messages that could not be processed successfully from the server (default behaviour is to leave them on the server) @@ -151,6 +152,7 @@ END_DESC pop_options = {:host => ENV['host'], :port => ENV['port'], :apop => ENV['apop'], + :ssl => ENV['ssl'], :username => ENV['username'], :password => ENV['password'], :delete_unprocessed => ENV['delete_unprocessed']} diff --git a/public/images/add.png b/public/images/add.png index d6d26db7..c7708e8f 100644 Binary files a/public/images/add.png and b/public/images/add.png differ diff --git a/public/images/arrow-bottom-right.png b/public/images/arrow-bottom-right.png new file mode 100644 index 00000000..06e3b546 Binary files /dev/null and b/public/images/arrow-bottom-right.png differ diff --git a/public/images/arrow-down-2.png b/public/images/arrow-down-2.png new file mode 100644 index 00000000..fc7428f3 Binary files /dev/null and b/public/images/arrow-down-2.png differ diff --git a/public/images/arrow-down-3.png b/public/images/arrow-down-3.png new file mode 100644 index 00000000..c1439e3c Binary files /dev/null and b/public/images/arrow-down-3.png differ diff --git a/public/images/arrow-down-grey.png b/public/images/arrow-down-grey.png new file mode 100644 index 00000000..8728d5eb Binary files /dev/null and b/public/images/arrow-down-grey.png differ diff --git a/public/images/arrow-down-white.png b/public/images/arrow-down-white.png new file mode 100644 index 00000000..620a3a62 Binary files /dev/null and b/public/images/arrow-down-white.png differ diff --git a/public/images/arrow-down.png b/public/images/arrow-down.png new file mode 100644 index 00000000..f9682d56 Binary files /dev/null and b/public/images/arrow-down.png differ diff --git a/public/images/arrow-right.png b/public/images/arrow-right.png new file mode 100644 index 00000000..089b63fb Binary files /dev/null and b/public/images/arrow-right.png differ diff --git a/public/images/arrow-up-white.png b/public/images/arrow-up-white.png new file mode 100644 index 00000000..0380bcf9 Binary files /dev/null and b/public/images/arrow-up-white.png differ diff --git a/public/images/arrow_breadcrumb.png b/public/images/arrow_breadcrumb.png new file mode 100644 index 00000000..3a239244 Binary files /dev/null and b/public/images/arrow_breadcrumb.png differ diff --git a/public/images/arrow_grey_top_navigation.png b/public/images/arrow_grey_top_navigation.png new file mode 100644 index 00000000..5f2b81bf Binary files /dev/null and b/public/images/arrow_grey_top_navigation.png differ diff --git a/public/images/arrow_white_top_navigation.png b/public/images/arrow_white_top_navigation.png new file mode 100644 index 00000000..2033a256 Binary files /dev/null and b/public/images/arrow_white_top_navigation.png differ diff --git a/public/images/background_active_button.png b/public/images/background_active_button.png new file mode 100644 index 00000000..89d0d21e Binary files /dev/null and b/public/images/background_active_button.png differ diff --git a/public/images/background_breadcrumb.png b/public/images/background_breadcrumb.png new file mode 100644 index 00000000..53d9762c Binary files /dev/null and b/public/images/background_breadcrumb.png differ diff --git a/public/images/background_header.png b/public/images/background_header.png new file mode 100644 index 00000000..be3c8140 Binary files /dev/null and b/public/images/background_header.png differ diff --git a/public/images/background_search.png b/public/images/background_search.png new file mode 100644 index 00000000..6e75056a Binary files /dev/null and b/public/images/background_search.png differ diff --git a/public/images/background_top_navigation.png b/public/images/background_top_navigation.png new file mode 100644 index 00000000..d65969cd Binary files /dev/null and b/public/images/background_top_navigation.png differ diff --git a/public/images/background_widgets.png b/public/images/background_widgets.png new file mode 100644 index 00000000..e1e05277 Binary files /dev/null and b/public/images/background_widgets.png differ diff --git a/public/images/blockquote-bg.png b/public/images/blockquote-bg.png new file mode 100644 index 00000000..69685c6f Binary files /dev/null and b/public/images/blockquote-bg.png differ diff --git a/public/images/calendar.png b/public/images/calendar.png index 62df31c9..619172a9 100644 Binary files a/public/images/calendar.png and b/public/images/calendar.png differ diff --git a/public/images/check.png b/public/images/check.png new file mode 100644 index 00000000..5677d466 Binary files /dev/null and b/public/images/check.png differ diff --git a/public/images/clock.png b/public/images/clock.png new file mode 100644 index 00000000..f6574af5 Binary files /dev/null and b/public/images/clock.png differ diff --git a/public/images/comment.png b/public/images/comment.png index 0e848cc0..7bc9233e 100644 Binary files a/public/images/comment.png and b/public/images/comment.png differ diff --git a/public/images/copy.png b/public/images/copy.png index 689f251c..3c0dad92 100644 Binary files a/public/images/copy.png and b/public/images/copy.png differ diff --git a/public/images/delete.png b/public/images/delete.png index ba6256dd..08f24936 100644 Binary files a/public/images/delete.png and b/public/images/delete.png differ diff --git a/public/images/delete.png.oxygen b/public/images/delete.png.oxygen new file mode 100644 index 00000000..d04a554e Binary files /dev/null and b/public/images/delete.png.oxygen differ diff --git a/public/images/disk.png b/public/images/disk.png new file mode 100644 index 00000000..a7c35db1 Binary files /dev/null and b/public/images/disk.png differ diff --git a/public/images/dot-blue.png b/public/images/dot-blue.png new file mode 100644 index 00000000..9dca2b3c Binary files /dev/null and b/public/images/dot-blue.png differ diff --git a/public/images/double_arrow_toggle_down.png b/public/images/double_arrow_toggle_down.png new file mode 100644 index 00000000..0cc71a52 Binary files /dev/null and b/public/images/double_arrow_toggle_down.png differ diff --git a/public/images/double_arrow_toggle_down_white.png b/public/images/double_arrow_toggle_down_white.png new file mode 100644 index 00000000..bd7fda4f Binary files /dev/null and b/public/images/double_arrow_toggle_down_white.png differ diff --git a/public/images/double_arrow_toggle_up.png b/public/images/double_arrow_toggle_up.png new file mode 100644 index 00000000..9965808a Binary files /dev/null and b/public/images/double_arrow_toggle_up.png differ diff --git a/public/images/double_arrow_toggle_up_white.png b/public/images/double_arrow_toggle_up_white.png new file mode 100644 index 00000000..bab9e194 Binary files /dev/null and b/public/images/double_arrow_toggle_up_white.png differ diff --git a/public/images/edit.png b/public/images/edit.png index 048efd2c..1b6a9e31 100644 Binary files a/public/images/edit.png and b/public/images/edit.png differ diff --git a/public/images/files-showhide.png b/public/images/files-showhide.png new file mode 100644 index 00000000..603f6856 Binary files /dev/null and b/public/images/files-showhide.png differ diff --git a/public/themes/chiliproject/images/fugue/arrow.png b/public/images/fugue/arrow.png similarity index 100% rename from public/themes/chiliproject/images/fugue/arrow.png rename to public/images/fugue/arrow.png diff --git a/public/themes/chiliproject/images/fugue/balloons.png b/public/images/fugue/balloons.png similarity index 100% rename from public/themes/chiliproject/images/fugue/balloons.png rename to public/images/fugue/balloons.png diff --git a/public/themes/chiliproject/images/fugue/books-stack.png b/public/images/fugue/books-stack.png similarity index 100% rename from public/themes/chiliproject/images/fugue/books-stack.png rename to public/images/fugue/books-stack.png diff --git a/public/themes/chiliproject/images/fugue/burn.png b/public/images/fugue/burn.png similarity index 100% rename from public/themes/chiliproject/images/fugue/burn.png rename to public/images/fugue/burn.png diff --git a/public/themes/chiliproject/images/fugue/calendar-month.png b/public/images/fugue/calendar-month.png similarity index 100% rename from public/themes/chiliproject/images/fugue/calendar-month.png rename to public/images/fugue/calendar-month.png diff --git a/public/themes/chiliproject/images/fugue/clock--plus.png b/public/images/fugue/clock--plus.png similarity index 100% rename from public/themes/chiliproject/images/fugue/clock--plus.png rename to public/images/fugue/clock--plus.png diff --git a/public/themes/chiliproject/images/fugue/clock.png b/public/images/fugue/clock.png similarity index 100% rename from public/themes/chiliproject/images/fugue/clock.png rename to public/images/fugue/clock.png diff --git a/public/themes/chiliproject/images/fugue/dashboard--pencil.png b/public/images/fugue/dashboard--pencil.png similarity index 100% rename from public/themes/chiliproject/images/fugue/dashboard--pencil.png rename to public/images/fugue/dashboard--pencil.png diff --git a/public/themes/chiliproject/images/fugue/disk-black.png b/public/images/fugue/disk-black.png similarity index 100% rename from public/themes/chiliproject/images/fugue/disk-black.png rename to public/images/fugue/disk-black.png diff --git a/public/themes/chiliproject/images/fugue/document-horizontal-text.png b/public/images/fugue/document-horizontal-text.png similarity index 100% rename from public/themes/chiliproject/images/fugue/document-horizontal-text.png rename to public/images/fugue/document-horizontal-text.png diff --git a/public/themes/chiliproject/images/fugue/document-text-image.png b/public/images/fugue/document-text-image.png similarity index 100% rename from public/themes/chiliproject/images/fugue/document-text-image.png rename to public/images/fugue/document-text-image.png diff --git a/public/themes/chiliproject/images/fugue/document-zipper.png b/public/images/fugue/document-zipper.png similarity index 100% rename from public/themes/chiliproject/images/fugue/document-zipper.png rename to public/images/fugue/document-zipper.png diff --git a/public/themes/chiliproject/images/fugue/documents-text.png b/public/images/fugue/documents-text.png similarity index 100% rename from public/themes/chiliproject/images/fugue/documents-text.png rename to public/images/fugue/documents-text.png diff --git a/public/themes/chiliproject/images/fugue/documents.png b/public/images/fugue/documents.png similarity index 100% rename from public/themes/chiliproject/images/fugue/documents.png rename to public/images/fugue/documents.png diff --git a/public/themes/chiliproject/images/fugue/equalizer.png b/public/images/fugue/equalizer.png similarity index 100% rename from public/themes/chiliproject/images/fugue/equalizer.png rename to public/images/fugue/equalizer.png diff --git a/public/themes/chiliproject/images/fugue/hammer--arrow.png b/public/images/fugue/hammer--arrow.png similarity index 100% rename from public/themes/chiliproject/images/fugue/hammer--arrow.png rename to public/images/fugue/hammer--arrow.png diff --git a/public/themes/chiliproject/images/fugue/history.txt b/public/images/fugue/history.txt similarity index 100% rename from public/themes/chiliproject/images/fugue/history.txt rename to public/images/fugue/history.txt diff --git a/public/themes/chiliproject/images/fugue/layout-2.png b/public/images/fugue/layout-2.png similarity index 100% rename from public/themes/chiliproject/images/fugue/layout-2.png rename to public/images/fugue/layout-2.png diff --git a/public/themes/chiliproject/images/fugue/layout-select-content.png b/public/images/fugue/layout-select-content.png similarity index 100% rename from public/themes/chiliproject/images/fugue/layout-select-content.png rename to public/images/fugue/layout-select-content.png diff --git a/public/themes/chiliproject/images/fugue/lightning.png b/public/images/fugue/lightning.png similarity index 100% rename from public/themes/chiliproject/images/fugue/lightning.png rename to public/images/fugue/lightning.png diff --git a/public/themes/chiliproject/images/fugue/magnifier-left.png b/public/images/fugue/magnifier-left.png similarity index 100% rename from public/themes/chiliproject/images/fugue/magnifier-left.png rename to public/images/fugue/magnifier-left.png diff --git a/public/themes/chiliproject/images/fugue/map-pin.png b/public/images/fugue/map-pin.png similarity index 100% rename from public/themes/chiliproject/images/fugue/map-pin.png rename to public/images/fugue/map-pin.png diff --git a/public/themes/chiliproject/images/fugue/money--pencil.png b/public/images/fugue/money--pencil.png similarity index 100% rename from public/themes/chiliproject/images/fugue/money--pencil.png rename to public/images/fugue/money--pencil.png diff --git a/public/themes/chiliproject/images/fugue/monitor.png b/public/images/fugue/monitor.png similarity index 100% rename from public/themes/chiliproject/images/fugue/monitor.png rename to public/images/fugue/monitor.png diff --git a/public/themes/chiliproject/images/fugue/newspaper.png b/public/images/fugue/newspaper.png similarity index 100% rename from public/themes/chiliproject/images/fugue/newspaper.png rename to public/images/fugue/newspaper.png diff --git a/public/themes/chiliproject/images/fugue/notebooks--pencil.png b/public/images/fugue/notebooks--pencil.png similarity index 100% rename from public/themes/chiliproject/images/fugue/notebooks--pencil.png rename to public/images/fugue/notebooks--pencil.png diff --git a/public/themes/chiliproject/images/fugue/pencil-small.png b/public/images/fugue/pencil-small.png similarity index 100% rename from public/themes/chiliproject/images/fugue/pencil-small.png rename to public/images/fugue/pencil-small.png diff --git a/public/themes/chiliproject/images/fugue/pill--exclamation.png b/public/images/fugue/pill--exclamation.png similarity index 100% rename from public/themes/chiliproject/images/fugue/pill--exclamation.png rename to public/images/fugue/pill--exclamation.png diff --git a/public/themes/chiliproject/images/fugue/plus-small.png b/public/images/fugue/plus-small.png similarity index 100% rename from public/themes/chiliproject/images/fugue/plus-small.png rename to public/images/fugue/plus-small.png diff --git a/public/themes/chiliproject/images/fugue/projection-screen--pencil.png b/public/images/fugue/projection-screen--pencil.png similarity index 100% rename from public/themes/chiliproject/images/fugue/projection-screen--pencil.png rename to public/images/fugue/projection-screen--pencil.png diff --git a/public/themes/chiliproject/images/fugue/question-balloon.png b/public/images/fugue/question-balloon.png similarity index 100% rename from public/themes/chiliproject/images/fugue/question-balloon.png rename to public/images/fugue/question-balloon.png diff --git a/public/themes/chiliproject/images/fugue/readme.txt b/public/images/fugue/readme.txt similarity index 100% rename from public/themes/chiliproject/images/fugue/readme.txt rename to public/images/fugue/readme.txt diff --git a/public/themes/chiliproject/images/fugue/report--exclamation.png b/public/images/fugue/report--exclamation.png similarity index 100% rename from public/themes/chiliproject/images/fugue/report--exclamation.png rename to public/images/fugue/report--exclamation.png diff --git a/public/themes/chiliproject/images/fugue/ruler--pencil.png b/public/images/fugue/ruler--pencil.png similarity index 100% rename from public/themes/chiliproject/images/fugue/ruler--pencil.png rename to public/images/fugue/ruler--pencil.png diff --git a/public/themes/chiliproject/images/fugue/safe.png b/public/images/fugue/safe.png similarity index 100% rename from public/themes/chiliproject/images/fugue/safe.png rename to public/images/fugue/safe.png diff --git a/public/themes/chiliproject/images/fugue/star-empty.png b/public/images/fugue/star-empty.png similarity index 100% rename from public/themes/chiliproject/images/fugue/star-empty.png rename to public/images/fugue/star-empty.png diff --git a/public/themes/chiliproject/images/fugue/star.png b/public/images/fugue/star.png similarity index 100% rename from public/themes/chiliproject/images/fugue/star.png rename to public/images/fugue/star.png diff --git a/public/themes/chiliproject/images/fugue/sticky-note.png b/public/images/fugue/sticky-note.png similarity index 100% rename from public/themes/chiliproject/images/fugue/sticky-note.png rename to public/images/fugue/sticky-note.png diff --git a/public/themes/chiliproject/images/fugue/tags-label.png b/public/images/fugue/tags-label.png similarity index 100% rename from public/themes/chiliproject/images/fugue/tags-label.png rename to public/images/fugue/tags-label.png diff --git a/public/themes/chiliproject/images/fugue/tick-shield.png b/public/images/fugue/tick-shield.png similarity index 100% rename from public/themes/chiliproject/images/fugue/tick-shield.png rename to public/images/fugue/tick-shield.png diff --git a/public/themes/chiliproject/images/fugue/ticket--arrow.png b/public/images/fugue/ticket--arrow.png similarity index 100% rename from public/themes/chiliproject/images/fugue/ticket--arrow.png rename to public/images/fugue/ticket--arrow.png diff --git a/public/themes/chiliproject/images/fugue/ticket--minus.png b/public/images/fugue/ticket--minus.png similarity index 100% rename from public/themes/chiliproject/images/fugue/ticket--minus.png rename to public/images/fugue/ticket--minus.png diff --git a/public/themes/chiliproject/images/fugue/ticket--plus.png b/public/images/fugue/ticket--plus.png similarity index 100% rename from public/themes/chiliproject/images/fugue/ticket--plus.png rename to public/images/fugue/ticket--plus.png diff --git a/public/themes/chiliproject/images/fugue/ticket.png b/public/images/fugue/ticket.png similarity index 100% rename from public/themes/chiliproject/images/fugue/ticket.png rename to public/images/fugue/ticket.png diff --git a/public/themes/chiliproject/images/fugue/trophy.png b/public/images/fugue/trophy.png similarity index 100% rename from public/themes/chiliproject/images/fugue/trophy.png rename to public/images/fugue/trophy.png diff --git a/public/themes/chiliproject/images/fugue/ui-progress-bar.png b/public/images/fugue/ui-progress-bar.png similarity index 100% rename from public/themes/chiliproject/images/fugue/ui-progress-bar.png rename to public/images/fugue/ui-progress-bar.png diff --git a/public/themes/chiliproject/images/fugue/user-business.png b/public/images/fugue/user-business.png similarity index 100% rename from public/themes/chiliproject/images/fugue/user-business.png rename to public/images/fugue/user-business.png diff --git a/public/images/gradient-down.png b/public/images/gradient-down.png new file mode 100644 index 00000000..8bcaac71 Binary files /dev/null and b/public/images/gradient-down.png differ diff --git a/public/images/gradient-up.png b/public/images/gradient-up.png new file mode 100644 index 00000000..2e1fb41a Binary files /dev/null and b/public/images/gradient-up.png differ diff --git a/public/images/icon_help.png b/public/images/icon_help.png new file mode 100644 index 00000000..bead744d Binary files /dev/null and b/public/images/icon_help.png differ diff --git a/public/images/icon_help_grey.png b/public/images/icon_help_grey.png new file mode 100644 index 00000000..f5e51fb9 Binary files /dev/null and b/public/images/icon_help_grey.png differ diff --git a/public/images/icon_home.png b/public/images/icon_home.png new file mode 100644 index 00000000..27faf38f Binary files /dev/null and b/public/images/icon_home.png differ diff --git a/public/images/icon_home_grey.png b/public/images/icon_home_grey.png new file mode 100644 index 00000000..979bd5b9 Binary files /dev/null and b/public/images/icon_home_grey.png differ diff --git a/public/images/loadingAnimation.gif b/public/images/loadingAnimation.gif new file mode 100644 index 00000000..82290f48 Binary files /dev/null and b/public/images/loadingAnimation.gif differ diff --git a/public/images/macFFBgHack.png b/public/images/macFFBgHack.png new file mode 100644 index 00000000..c6473b32 Binary files /dev/null and b/public/images/macFFBgHack.png differ diff --git a/public/images/mimetypes/applix.png b/public/images/mimetypes/applix.png new file mode 100644 index 00000000..106f3e13 Binary files /dev/null and b/public/images/mimetypes/applix.png differ diff --git a/public/images/mimetypes/ascii.png b/public/images/mimetypes/ascii.png new file mode 100644 index 00000000..a26b520f Binary files /dev/null and b/public/images/mimetypes/ascii.png differ diff --git a/public/images/mimetypes/binary.png b/public/images/mimetypes/binary.png new file mode 100644 index 00000000..52c2ec6c Binary files /dev/null and b/public/images/mimetypes/binary.png differ diff --git a/public/images/mimetypes/cdbo_list.png b/public/images/mimetypes/cdbo_list.png new file mode 100644 index 00000000..cb5523b1 Binary files /dev/null and b/public/images/mimetypes/cdbo_list.png differ diff --git a/public/images/mimetypes/cdimage.png b/public/images/mimetypes/cdimage.png new file mode 100644 index 00000000..f63d20f7 Binary files /dev/null and b/public/images/mimetypes/cdimage.png differ diff --git a/public/images/mimetypes/cdr.png b/public/images/mimetypes/cdr.png new file mode 100644 index 00000000..5523c404 Binary files /dev/null and b/public/images/mimetypes/cdr.png differ diff --git a/public/images/mimetypes/cdtrack.png b/public/images/mimetypes/cdtrack.png new file mode 100644 index 00000000..d016a64d Binary files /dev/null and b/public/images/mimetypes/cdtrack.png differ diff --git a/public/images/mimetypes/colorscm.png b/public/images/mimetypes/colorscm.png new file mode 100644 index 00000000..9e4ded8e Binary files /dev/null and b/public/images/mimetypes/colorscm.png differ diff --git a/public/images/mimetypes/core.png b/public/images/mimetypes/core.png new file mode 100644 index 00000000..f724f52a Binary files /dev/null and b/public/images/mimetypes/core.png differ diff --git a/public/images/mimetypes/deb.png b/public/images/mimetypes/deb.png new file mode 100644 index 00000000..e56802b0 Binary files /dev/null and b/public/images/mimetypes/deb.png differ diff --git a/public/images/mimetypes/document.png b/public/images/mimetypes/document.png new file mode 100644 index 00000000..4b657bc3 Binary files /dev/null and b/public/images/mimetypes/document.png differ diff --git a/public/images/mimetypes/document2.png b/public/images/mimetypes/document2.png new file mode 100644 index 00000000..39b547c7 Binary files /dev/null and b/public/images/mimetypes/document2.png differ diff --git a/public/images/mimetypes/dvi.png b/public/images/mimetypes/dvi.png new file mode 100644 index 00000000..e9bb69ee Binary files /dev/null and b/public/images/mimetypes/dvi.png differ diff --git a/public/images/mimetypes/empty.png b/public/images/mimetypes/empty.png new file mode 100644 index 00000000..4ddc3293 Binary files /dev/null and b/public/images/mimetypes/empty.png differ diff --git a/public/images/mimetypes/encrypted.png b/public/images/mimetypes/encrypted.png new file mode 100644 index 00000000..c9dfcb14 Binary files /dev/null and b/public/images/mimetypes/encrypted.png differ diff --git a/public/images/mimetypes/exec_wine.png b/public/images/mimetypes/exec_wine.png new file mode 100644 index 00000000..13f2a5d7 Binary files /dev/null and b/public/images/mimetypes/exec_wine.png differ diff --git a/public/images/mimetypes/file_locked.png b/public/images/mimetypes/file_locked.png new file mode 100644 index 00000000..24305b2c Binary files /dev/null and b/public/images/mimetypes/file_locked.png differ diff --git a/public/images/mimetypes/file_temporary.png b/public/images/mimetypes/file_temporary.png new file mode 100644 index 00000000..4061f353 Binary files /dev/null and b/public/images/mimetypes/file_temporary.png differ diff --git a/public/images/mimetypes/font.png b/public/images/mimetypes/font.png new file mode 100644 index 00000000..93079e39 Binary files /dev/null and b/public/images/mimetypes/font.png differ diff --git a/public/images/mimetypes/font_bitmap.png b/public/images/mimetypes/font_bitmap.png new file mode 100644 index 00000000..157e9146 Binary files /dev/null and b/public/images/mimetypes/font_bitmap.png differ diff --git a/public/images/mimetypes/font_truetype.png b/public/images/mimetypes/font_truetype.png new file mode 100644 index 00000000..bc902e4c Binary files /dev/null and b/public/images/mimetypes/font_truetype.png differ diff --git a/public/images/mimetypes/font_type1.png b/public/images/mimetypes/font_type1.png new file mode 100644 index 00000000..95bd44bf Binary files /dev/null and b/public/images/mimetypes/font_type1.png differ diff --git a/public/images/mimetypes/gf.png b/public/images/mimetypes/gf.png new file mode 100644 index 00000000..a3d4a0ec Binary files /dev/null and b/public/images/mimetypes/gf.png differ diff --git a/public/images/mimetypes/html.png b/public/images/mimetypes/html.png new file mode 100644 index 00000000..993fbcf7 Binary files /dev/null and b/public/images/mimetypes/html.png differ diff --git a/public/images/mimetypes/image.png b/public/images/mimetypes/image.png new file mode 100644 index 00000000..78f19fa2 Binary files /dev/null and b/public/images/mimetypes/image.png differ diff --git a/public/images/mimetypes/image2.png b/public/images/mimetypes/image2.png new file mode 100644 index 00000000..043c087d Binary files /dev/null and b/public/images/mimetypes/image2.png differ diff --git a/public/images/mimetypes/info.png b/public/images/mimetypes/info.png new file mode 100644 index 00000000..514d3fbc Binary files /dev/null and b/public/images/mimetypes/info.png differ diff --git a/public/images/mimetypes/karbon.png b/public/images/mimetypes/karbon.png new file mode 100644 index 00000000..771e6a04 Binary files /dev/null and b/public/images/mimetypes/karbon.png differ diff --git a/public/images/mimetypes/karbon_karbon.png b/public/images/mimetypes/karbon_karbon.png new file mode 100644 index 00000000..f4d0a3c3 Binary files /dev/null and b/public/images/mimetypes/karbon_karbon.png differ diff --git a/public/images/mimetypes/kchart_chrt.png b/public/images/mimetypes/kchart_chrt.png new file mode 100644 index 00000000..a771fc16 Binary files /dev/null and b/public/images/mimetypes/kchart_chrt.png differ diff --git a/public/images/mimetypes/kformula_kfo.png b/public/images/mimetypes/kformula_kfo.png new file mode 100644 index 00000000..4b6f2f8d Binary files /dev/null and b/public/images/mimetypes/kformula_kfo.png differ diff --git a/public/images/mimetypes/kivio_flw.png b/public/images/mimetypes/kivio_flw.png new file mode 100644 index 00000000..52aec149 Binary files /dev/null and b/public/images/mimetypes/kivio_flw.png differ diff --git a/public/images/mimetypes/kmultiple.png b/public/images/mimetypes/kmultiple.png new file mode 100644 index 00000000..58410231 Binary files /dev/null and b/public/images/mimetypes/kmultiple.png differ diff --git a/public/images/mimetypes/koffice.png b/public/images/mimetypes/koffice.png new file mode 100644 index 00000000..2e18a88e Binary files /dev/null and b/public/images/mimetypes/koffice.png differ diff --git a/public/images/mimetypes/kpresenter_kpr.png b/public/images/mimetypes/kpresenter_kpr.png new file mode 100644 index 00000000..8ab1fd95 Binary files /dev/null and b/public/images/mimetypes/kpresenter_kpr.png differ diff --git a/public/images/mimetypes/krita_kra.png b/public/images/mimetypes/krita_kra.png new file mode 100644 index 00000000..2cb6fbef Binary files /dev/null and b/public/images/mimetypes/krita_kra.png differ diff --git a/public/images/mimetypes/kspread_ksp.png b/public/images/mimetypes/kspread_ksp.png new file mode 100644 index 00000000..f4e234d6 Binary files /dev/null and b/public/images/mimetypes/kspread_ksp.png differ diff --git a/public/images/mimetypes/kugar_kud.png b/public/images/mimetypes/kugar_kud.png new file mode 100644 index 00000000..7b627da5 Binary files /dev/null and b/public/images/mimetypes/kugar_kud.png differ diff --git a/public/images/mimetypes/kugardata.png b/public/images/mimetypes/kugardata.png new file mode 100644 index 00000000..10806bc8 Binary files /dev/null and b/public/images/mimetypes/kugardata.png differ diff --git a/public/images/mimetypes/kword_kwd.png b/public/images/mimetypes/kword_kwd.png new file mode 100644 index 00000000..270ced99 Binary files /dev/null and b/public/images/mimetypes/kword_kwd.png differ diff --git a/public/images/mimetypes/log.png b/public/images/mimetypes/log.png new file mode 100644 index 00000000..29996058 Binary files /dev/null and b/public/images/mimetypes/log.png differ diff --git a/public/images/mimetypes/make.png b/public/images/mimetypes/make.png new file mode 100644 index 00000000..cbb4bfb4 Binary files /dev/null and b/public/images/mimetypes/make.png differ diff --git a/public/images/mimetypes/man.png b/public/images/mimetypes/man.png new file mode 100644 index 00000000..7212c509 Binary files /dev/null and b/public/images/mimetypes/man.png differ diff --git a/public/images/mimetypes/message.png b/public/images/mimetypes/message.png new file mode 100644 index 00000000..cf939dcf Binary files /dev/null and b/public/images/mimetypes/message.png differ diff --git a/public/images/mimetypes/message2.png b/public/images/mimetypes/message2.png new file mode 100644 index 00000000..05f0225a Binary files /dev/null and b/public/images/mimetypes/message2.png differ diff --git a/public/images/mimetypes/metafont.png b/public/images/mimetypes/metafont.png new file mode 100644 index 00000000..07745313 Binary files /dev/null and b/public/images/mimetypes/metafont.png differ diff --git a/public/images/mimetypes/midi.png b/public/images/mimetypes/midi.png new file mode 100644 index 00000000..d4b08e89 Binary files /dev/null and b/public/images/mimetypes/midi.png differ diff --git a/public/images/mimetypes/mime-cdr.png b/public/images/mimetypes/mime-cdr.png new file mode 100644 index 00000000..5523c404 Binary files /dev/null and b/public/images/mimetypes/mime-cdr.png differ diff --git a/public/images/mimetypes/mime-colorset.png b/public/images/mimetypes/mime-colorset.png new file mode 100644 index 00000000..60bf8027 Binary files /dev/null and b/public/images/mimetypes/mime-colorset.png differ diff --git a/public/images/mimetypes/mime-postscript.png b/public/images/mimetypes/mime-postscript.png new file mode 100644 index 00000000..b346e9f6 Binary files /dev/null and b/public/images/mimetypes/mime-postscript.png differ diff --git a/public/images/mimetypes/mime-resource.png b/public/images/mimetypes/mime-resource.png new file mode 100644 index 00000000..76185384 Binary files /dev/null and b/public/images/mimetypes/mime-resource.png differ diff --git a/public/images/mimetypes/mime-template_source.png b/public/images/mimetypes/mime-template_source.png new file mode 100644 index 00000000..399e4031 Binary files /dev/null and b/public/images/mimetypes/mime-template_source.png differ diff --git a/public/images/mimetypes/mime_ascii.png b/public/images/mimetypes/mime_ascii.png new file mode 100644 index 00000000..a26b520f Binary files /dev/null and b/public/images/mimetypes/mime_ascii.png differ diff --git a/public/images/mimetypes/mime_colorset.png b/public/images/mimetypes/mime_colorset.png new file mode 100644 index 00000000..5cd864a5 Binary files /dev/null and b/public/images/mimetypes/mime_colorset.png differ diff --git a/public/images/mimetypes/mime_empty.png b/public/images/mimetypes/mime_empty.png new file mode 100644 index 00000000..4ddc3293 Binary files /dev/null and b/public/images/mimetypes/mime_empty.png differ diff --git a/public/images/mimetypes/mime_koffice.png b/public/images/mimetypes/mime_koffice.png new file mode 100644 index 00000000..441fb65c Binary files /dev/null and b/public/images/mimetypes/mime_koffice.png differ diff --git a/public/images/mimetypes/misc.png b/public/images/mimetypes/misc.png new file mode 100644 index 00000000..699f7343 Binary files /dev/null and b/public/images/mimetypes/misc.png differ diff --git a/public/images/mimetypes/mozilla_doc.png b/public/images/mimetypes/mozilla_doc.png new file mode 100644 index 00000000..392b3c85 Binary files /dev/null and b/public/images/mimetypes/mozilla_doc.png differ diff --git a/public/images/mimetypes/netscape_doc.png b/public/images/mimetypes/netscape_doc.png new file mode 100644 index 00000000..14179118 Binary files /dev/null and b/public/images/mimetypes/netscape_doc.png differ diff --git a/public/images/mimetypes/pdf.png b/public/images/mimetypes/pdf.png new file mode 100644 index 00000000..dd83903f Binary files /dev/null and b/public/images/mimetypes/pdf.png differ diff --git a/public/images/mimetypes/php.png b/public/images/mimetypes/php.png new file mode 100644 index 00000000..0e5d6953 Binary files /dev/null and b/public/images/mimetypes/php.png differ diff --git a/public/images/mimetypes/pk.png b/public/images/mimetypes/pk.png new file mode 100644 index 00000000..5e59e068 Binary files /dev/null and b/public/images/mimetypes/pk.png differ diff --git a/public/images/mimetypes/postscript.png b/public/images/mimetypes/postscript.png new file mode 100644 index 00000000..f6111cdd Binary files /dev/null and b/public/images/mimetypes/postscript.png differ diff --git a/public/images/mimetypes/ps.png b/public/images/mimetypes/ps.png new file mode 100644 index 00000000..789343d9 Binary files /dev/null and b/public/images/mimetypes/ps.png differ diff --git a/public/images/mimetypes/quicktime.png b/public/images/mimetypes/quicktime.png new file mode 100644 index 00000000..55db9ead Binary files /dev/null and b/public/images/mimetypes/quicktime.png differ diff --git a/public/images/mimetypes/readme.png b/public/images/mimetypes/readme.png new file mode 100644 index 00000000..ac77dc7f Binary files /dev/null and b/public/images/mimetypes/readme.png differ diff --git a/public/images/mimetypes/real.png b/public/images/mimetypes/real.png new file mode 100644 index 00000000..aa854354 Binary files /dev/null and b/public/images/mimetypes/real.png differ diff --git a/public/images/mimetypes/real_doc.png b/public/images/mimetypes/real_doc.png new file mode 100644 index 00000000..0a9386cd Binary files /dev/null and b/public/images/mimetypes/real_doc.png differ diff --git a/public/images/mimetypes/recycled.png b/public/images/mimetypes/recycled.png new file mode 100644 index 00000000..2e860cbd Binary files /dev/null and b/public/images/mimetypes/recycled.png differ diff --git a/public/images/mimetypes/resource.png b/public/images/mimetypes/resource.png new file mode 100644 index 00000000..76185384 Binary files /dev/null and b/public/images/mimetypes/resource.png differ diff --git a/public/images/mimetypes/rpm.png b/public/images/mimetypes/rpm.png new file mode 100644 index 00000000..a8bcc4f9 Binary files /dev/null and b/public/images/mimetypes/rpm.png differ diff --git a/public/images/mimetypes/schedule.png b/public/images/mimetypes/schedule.png new file mode 100644 index 00000000..73202fe8 Binary files /dev/null and b/public/images/mimetypes/schedule.png differ diff --git a/public/images/mimetypes/shellscript.png b/public/images/mimetypes/shellscript.png new file mode 100644 index 00000000..2ddce82d Binary files /dev/null and b/public/images/mimetypes/shellscript.png differ diff --git a/public/images/mimetypes/soffice.png b/public/images/mimetypes/soffice.png new file mode 100644 index 00000000..972dd7b1 Binary files /dev/null and b/public/images/mimetypes/soffice.png differ diff --git a/public/images/mimetypes/sound.png b/public/images/mimetypes/sound.png new file mode 100644 index 00000000..a7496aff Binary files /dev/null and b/public/images/mimetypes/sound.png differ diff --git a/public/images/mimetypes/source.png b/public/images/mimetypes/source.png new file mode 100644 index 00000000..6ea5b6da Binary files /dev/null and b/public/images/mimetypes/source.png differ diff --git a/public/images/mimetypes/source_c.png b/public/images/mimetypes/source_c.png new file mode 100644 index 00000000..8244b717 Binary files /dev/null and b/public/images/mimetypes/source_c.png differ diff --git a/public/images/mimetypes/source_cpp.png b/public/images/mimetypes/source_cpp.png new file mode 100644 index 00000000..74def256 Binary files /dev/null and b/public/images/mimetypes/source_cpp.png differ diff --git a/public/images/mimetypes/source_f.png b/public/images/mimetypes/source_f.png new file mode 100644 index 00000000..ee121645 Binary files /dev/null and b/public/images/mimetypes/source_f.png differ diff --git a/public/images/mimetypes/source_h.png b/public/images/mimetypes/source_h.png new file mode 100644 index 00000000..aaf61a8f Binary files /dev/null and b/public/images/mimetypes/source_h.png differ diff --git a/public/images/mimetypes/source_j.png b/public/images/mimetypes/source_j.png new file mode 100644 index 00000000..05040512 Binary files /dev/null and b/public/images/mimetypes/source_j.png differ diff --git a/public/images/mimetypes/source_java.png b/public/images/mimetypes/source_java.png new file mode 100644 index 00000000..02b63d5c Binary files /dev/null and b/public/images/mimetypes/source_java.png differ diff --git a/public/images/mimetypes/source_l.png b/public/images/mimetypes/source_l.png new file mode 100644 index 00000000..38e09ba9 Binary files /dev/null and b/public/images/mimetypes/source_l.png differ diff --git a/public/images/mimetypes/source_moc.png b/public/images/mimetypes/source_moc.png new file mode 100644 index 00000000..8ee40b94 Binary files /dev/null and b/public/images/mimetypes/source_moc.png differ diff --git a/public/images/mimetypes/source_o.png b/public/images/mimetypes/source_o.png new file mode 100644 index 00000000..5cf084e3 Binary files /dev/null and b/public/images/mimetypes/source_o.png differ diff --git a/public/images/mimetypes/source_p.png b/public/images/mimetypes/source_p.png new file mode 100644 index 00000000..20616ce0 Binary files /dev/null and b/public/images/mimetypes/source_p.png differ diff --git a/public/images/mimetypes/source_php.png b/public/images/mimetypes/source_php.png new file mode 100644 index 00000000..93582511 Binary files /dev/null and b/public/images/mimetypes/source_php.png differ diff --git a/public/images/mimetypes/source_pl.png b/public/images/mimetypes/source_pl.png new file mode 100644 index 00000000..ee5d97ee Binary files /dev/null and b/public/images/mimetypes/source_pl.png differ diff --git a/public/images/mimetypes/source_py.png b/public/images/mimetypes/source_py.png new file mode 100644 index 00000000..c0361fca Binary files /dev/null and b/public/images/mimetypes/source_py.png differ diff --git a/public/images/mimetypes/source_s.png b/public/images/mimetypes/source_s.png new file mode 100644 index 00000000..4ab913a3 Binary files /dev/null and b/public/images/mimetypes/source_s.png differ diff --git a/public/images/mimetypes/source_y.png b/public/images/mimetypes/source_y.png new file mode 100644 index 00000000..80b004f3 Binary files /dev/null and b/public/images/mimetypes/source_y.png differ diff --git a/public/images/mimetypes/sownd.png b/public/images/mimetypes/sownd.png new file mode 100644 index 00000000..5748f061 Binary files /dev/null and b/public/images/mimetypes/sownd.png differ diff --git a/public/images/mimetypes/spreadsheet.png b/public/images/mimetypes/spreadsheet.png new file mode 100644 index 00000000..685cf492 Binary files /dev/null and b/public/images/mimetypes/spreadsheet.png differ diff --git a/public/images/mimetypes/swf.png b/public/images/mimetypes/swf.png new file mode 100644 index 00000000..b8151156 Binary files /dev/null and b/public/images/mimetypes/swf.png differ diff --git a/public/images/mimetypes/tar.png b/public/images/mimetypes/tar.png new file mode 100644 index 00000000..65e4dc66 Binary files /dev/null and b/public/images/mimetypes/tar.png differ diff --git a/public/images/mimetypes/template_source.png b/public/images/mimetypes/template_source.png new file mode 100644 index 00000000..399e4031 Binary files /dev/null and b/public/images/mimetypes/template_source.png differ diff --git a/public/images/mimetypes/templates.png b/public/images/mimetypes/templates.png new file mode 100644 index 00000000..83b7fc1a Binary files /dev/null and b/public/images/mimetypes/templates.png differ diff --git a/public/images/mimetypes/tex.png b/public/images/mimetypes/tex.png new file mode 100644 index 00000000..4ae7fad9 Binary files /dev/null and b/public/images/mimetypes/tex.png differ diff --git a/public/images/mimetypes/tgz.png b/public/images/mimetypes/tgz.png new file mode 100644 index 00000000..65e4dc66 Binary files /dev/null and b/public/images/mimetypes/tgz.png differ diff --git a/public/images/mimetypes/txt.png b/public/images/mimetypes/txt.png new file mode 100644 index 00000000..cdb53ed6 Binary files /dev/null and b/public/images/mimetypes/txt.png differ diff --git a/public/images/mimetypes/txt2.png b/public/images/mimetypes/txt2.png new file mode 100644 index 00000000..0bdb4e10 Binary files /dev/null and b/public/images/mimetypes/txt2.png differ diff --git a/public/images/mimetypes/unknown.png b/public/images/mimetypes/unknown.png new file mode 100644 index 00000000..4ddc3293 Binary files /dev/null and b/public/images/mimetypes/unknown.png differ diff --git a/public/images/mimetypes/vcalendar.png b/public/images/mimetypes/vcalendar.png new file mode 100644 index 00000000..31484c2d Binary files /dev/null and b/public/images/mimetypes/vcalendar.png differ diff --git a/public/images/mimetypes/vcard.png b/public/images/mimetypes/vcard.png new file mode 100644 index 00000000..2402f7f0 Binary files /dev/null and b/public/images/mimetypes/vcard.png differ diff --git a/public/images/mimetypes/vectorgfx.png b/public/images/mimetypes/vectorgfx.png new file mode 100644 index 00000000..b8151156 Binary files /dev/null and b/public/images/mimetypes/vectorgfx.png differ diff --git a/public/images/mimetypes/video.png b/public/images/mimetypes/video.png new file mode 100644 index 00000000..f7135147 Binary files /dev/null and b/public/images/mimetypes/video.png differ diff --git a/public/images/mimetypes/video2.png b/public/images/mimetypes/video2.png new file mode 100644 index 00000000..95993d26 Binary files /dev/null and b/public/images/mimetypes/video2.png differ diff --git a/public/images/mimetypes/widget_doc.png b/public/images/mimetypes/widget_doc.png new file mode 100644 index 00000000..db777128 Binary files /dev/null and b/public/images/mimetypes/widget_doc.png differ diff --git a/public/images/mimetypes/wordprocessing.png b/public/images/mimetypes/wordprocessing.png new file mode 100644 index 00000000..4b657bc3 Binary files /dev/null and b/public/images/mimetypes/wordprocessing.png differ diff --git a/public/images/mimetypes/zip.png b/public/images/mimetypes/zip.png new file mode 100644 index 00000000..59ba42b1 Binary files /dev/null and b/public/images/mimetypes/zip.png differ diff --git a/public/images/move.png b/public/images/move.png index 7f269c36..f51787b4 100644 Binary files a/public/images/move.png and b/public/images/move.png differ diff --git a/public/images/pdf.png b/public/images/pdf.png new file mode 100644 index 00000000..31e4a04f Binary files /dev/null and b/public/images/pdf.png differ diff --git a/public/images/pencil.png b/public/images/pencil.png new file mode 100644 index 00000000..0bfecd50 Binary files /dev/null and b/public/images/pencil.png differ diff --git a/public/images/profile-arrow-down.png b/public/images/profile-arrow-down.png new file mode 100644 index 00000000..82863332 Binary files /dev/null and b/public/images/profile-arrow-down.png differ diff --git a/public/images/profile-arrow-up.png b/public/images/profile-arrow-up.png new file mode 100644 index 00000000..241fb360 Binary files /dev/null and b/public/images/profile-arrow-up.png differ diff --git a/public/images/refresh.png b/public/images/refresh.png new file mode 100644 index 00000000..eb12b854 Binary files /dev/null and b/public/images/refresh.png differ diff --git a/public/images/search.png b/public/images/search.png new file mode 100644 index 00000000..434b426e Binary files /dev/null and b/public/images/search.png differ diff --git a/public/images/shadow-down.png b/public/images/shadow-down.png new file mode 100644 index 00000000..3fc9a27d Binary files /dev/null and b/public/images/shadow-down.png differ diff --git a/public/images/star.png b/public/images/star.png new file mode 100644 index 00000000..6187faf5 Binary files /dev/null and b/public/images/star.png differ diff --git a/public/images/tag.png b/public/images/tag.png new file mode 100644 index 00000000..b25c5c96 Binary files /dev/null and b/public/images/tag.png differ diff --git a/public/images/thumb-arrow-right.png b/public/images/thumb-arrow-right.png new file mode 100644 index 00000000..47975662 Binary files /dev/null and b/public/images/thumb-arrow-right.png differ diff --git a/public/images/tooltip-arrow.png b/public/images/tooltip-arrow.png new file mode 100644 index 00000000..4cdf5f88 Binary files /dev/null and b/public/images/tooltip-arrow.png differ diff --git a/public/images/top_navigation.png b/public/images/top_navigation.png new file mode 100644 index 00000000..c5cd03ba Binary files /dev/null and b/public/images/top_navigation.png differ diff --git a/public/images/tr-hover.png b/public/images/tr-hover.png new file mode 100644 index 00000000..a623344e Binary files /dev/null and b/public/images/tr-hover.png differ diff --git a/public/images/user.png b/public/images/user.png index 264381ed..4e4dcf1c 100644 Binary files a/public/images/user.png and b/public/images/user.png differ diff --git a/public/images/vcard.png b/public/images/vcard.png new file mode 100644 index 00000000..c02f315d Binary files /dev/null and b/public/images/vcard.png differ diff --git a/public/themes/chiliproject/images/wiki_styles/caution.png b/public/images/wiki_styles/caution.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/caution.png rename to public/images/wiki_styles/caution.png diff --git a/public/themes/chiliproject/images/wiki_styles/caution_small.png b/public/images/wiki_styles/caution_small.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/caution_small.png rename to public/images/wiki_styles/caution_small.png diff --git a/public/themes/chiliproject/images/wiki_styles/important.png b/public/images/wiki_styles/important.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/important.png rename to public/images/wiki_styles/important.png diff --git a/public/themes/chiliproject/images/wiki_styles/important_small.png b/public/images/wiki_styles/important_small.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/important_small.png rename to public/images/wiki_styles/important_small.png diff --git a/public/themes/chiliproject/images/wiki_styles/info.png b/public/images/wiki_styles/info.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/info.png rename to public/images/wiki_styles/info.png diff --git a/public/themes/chiliproject/images/wiki_styles/info_small.png b/public/images/wiki_styles/info_small.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/info_small.png rename to public/images/wiki_styles/info_small.png diff --git a/public/themes/chiliproject/images/wiki_styles/note.png b/public/images/wiki_styles/note.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/note.png rename to public/images/wiki_styles/note.png diff --git a/public/themes/chiliproject/images/wiki_styles/note_small.png b/public/images/wiki_styles/note_small.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/note_small.png rename to public/images/wiki_styles/note_small.png diff --git a/public/themes/chiliproject/images/wiki_styles/see-also.png b/public/images/wiki_styles/see-also.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/see-also.png rename to public/images/wiki_styles/see-also.png diff --git a/public/themes/chiliproject/images/wiki_styles/see-also_small.png b/public/images/wiki_styles/see-also_small.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/see-also_small.png rename to public/images/wiki_styles/see-also_small.png diff --git a/public/themes/chiliproject/images/wiki_styles/tip.png b/public/images/wiki_styles/tip.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/tip.png rename to public/images/wiki_styles/tip.png diff --git a/public/themes/chiliproject/images/wiki_styles/tip_small.png b/public/images/wiki_styles/tip_small.png similarity index 100% rename from public/themes/chiliproject/images/wiki_styles/tip_small.png rename to public/images/wiki_styles/tip_small.png diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 81ecda1e..b62e0f8e 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -453,3 +453,195 @@ function hideOnLoad() { } Event.observe(window, 'load', hideOnLoad); + +// a few constants for animations speeds, etc. +var animationRate = 100; + +/* jQuery code from #263 */ +// returns viewport height +jQuery.viewportHeight = function() { + return self.innerHeight || + jQuery.boxModel && document.documentElement.clientHeight || + document.body.clientHeight; +}; + +// Automatically use format.js for jQuery Ajax +jQuery.ajaxSetup({ + 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")} +}) + +// Show/hide the ajax indicators +jQuery("#ajax-indicator").ajaxStart(function(){ jQuery(this).show().css('z-index', '9999'); }); +jQuery("#ajax-indicator").ajaxStop(function(){ jQuery(this).hide(); }); + +/* TODO: integrate with existing code and/or refactor */ +jQuery(document).ready(function($) { + + + // file table thumbnails + $("table a.has-thumb").hover(function() { + $(this).removeAttr("title").toggleClass("active"); + + // grab the image dimensions to position it properly + var thumbImg = $(this).find("img"); + var thumbImgLeft = -(thumbImg.outerWidth() ); + var thumbImgTop = -(thumbImg.height() / 2 ); + thumbImg.css({top: thumbImgTop, left: thumbImgLeft}).show(); + + }, function() { + $(this).toggleClass("active").find("img").hide(); + }); + + // show/hide the files table + $(".attachments h4").click(function() { + $(this).toggleClass("closed").next().slideToggle(animationRate); + }); + + // custom function for sliding the main-menu. IE6 & IE7 don't handle sliding very well + $.fn.mySlide = function() { + if (parseInt($.browser.version, 10) < 8 && $.browser.msie) { + // no animations, just toggle + this.toggle(); + // this forces IE to redraw the menu area, un-bollocksing things + $("#main-menu").css({paddingBottom:5}).animate({paddingBottom:0}, 10); + } else { + this.slideToggle(animationRate); + } + + return this; + }; + + // open and close the main-menu sub-menus + $("#main-menu li:has(ul) > a").not("ul ul a") + .append("") + .click(function() { + + $(this).toggleClass("open").parent().find("ul").not("ul ul ul").mySlide(); + + return false; + }); + + // submenu flyouts + $("#main-menu li li:has(ul)").hover(function() { + $(this).find(".profile-box").show(); + $(this).find("ul").slideDown(animationRate); + }, function() { + $(this).find("ul").slideUp(animationRate); + }); + + // add filter dropdown menu + $(".button-large:has(ul) > a").click(function(event) { + var tgt = $(event.target); + + // is this inside the title bar? + if (tgt.parents().is(".title-bar")) { + $(".title-bar-extras:hidden").slideDown(animationRate); + } + + $(this).parent().find("ul").slideToggle(animationRate); + + return false; + }); + + // Toggle a top menu item open or closed, showing or hiding its submenu + function toggleTopMenu(menuItem) { + menuItem.toggleClass("open").find('ul').mySlide(); + }; + + // Handle a single click event on the page to close an open menu item + + function handleClickEventOnPageToCloseOpenMenu(openMenuItem) { + $('html').one("click", function(htmlEvent) { + if (openMenuItem.has(htmlEvent.target).length > 0) { + // Clicked on the open menu, let it bubble up + } else { + // Clicked elsewhere, close menu + toggleTopMenu(openMenuItem); + } + }); + }; + + // Click on the menu header with a dropdown menu + $('#account-nav .drop-down').live('click', function(event) { + var menuItem = $(this); + + toggleTopMenu(menuItem); + + if (menuItem.hasClass('open')) { + handleClickEventOnPageToCloseOpenMenu(menuItem); + } + return false; + }); + + // Click on an actual item + $('#account-nav .drop-down ul a').live('click', function(event) { + event.stopPropagation(); + }); + + // show/hide login box + $("#account-nav a.login").click(function() { + $(this).parent().toggleClass("open"); + // Focus the username field if the login field has opened + $("#nav-login").slideToggle(animationRate, function () { + if ($(this).parent().hasClass("open")) { + $("input#username-pulldown").focus() + } + }); + + return false; + }); + + // deal with potentially problematic super-long titles + $(".title-bar h2").css({paddingRight: $(".title-bar-actions").outerWidth() + 15 }); + + // rejigger the main-menu sub-menu functionality. + $("#main-menu .toggler").remove(); // remove the togglers so they're inserted properly later. + + $("#main-menu li:has(ul) > a").not("ul ul a") + // 1. unbind the current click functions + .unbind("click") + // 2. wrap each in a span that we'll use for the new click element + .wrapInner("") + // 3. reinsert the so that it sits outside of the above + .append("") + // 4. attach a new click function that will follow the link if you clicked on the span itself and toggle if not + .click(function(event) { + + if (!$(event.target).hasClass("toggle-follow") ) { + $(this).toggleClass("open").parent().find("ul").not("ul ul ul").mySlide(); + return false; + } + }); + + // Do not close the login window when using it + $('#nav-login-content').click(function(event){ + event.stopPropagation(); + }); + + $('.lightbox-ajax').click(function(event) { + $('#dialog-window'). + html(''). + load(this.href, function() { + // Set width to the content width + var width = $('#content').width(); + $(this).dialog("option", "width", width).dialog('open'); + }); + + event.preventDefault(); + return false; + + }); + + // Configures the default dialog window + function setUpDialogWindow() { + $('#dialog-window'). + dialog({ + autoOpen: false, + minWidth: 400, + width: 800 + }); + + } + + setUpDialogWindow(); +}); diff --git a/public/javascripts/jquery-ui.min.js b/public/javascripts/jquery-ui.min.js new file mode 100644 index 00000000..14c9064f --- /dev/null +++ b/public/javascripts/jquery-ui.min.js @@ -0,0 +1,791 @@ +/*! + * jQuery UI 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16", +keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d= +this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this, +"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart": +"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight, +outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a, +"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&& +a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted= +false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); +;/* + * jQuery UI Position 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Position + */ +(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY, +left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+= +k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-= +m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left= +d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+= +a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b), +g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); +;/* + * jQuery UI Draggable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== +"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= +this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options; +this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}); +this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true}, +_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b= +false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration, +10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle|| +!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&& +a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent= +this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"), +10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"), +10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top, +(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!= +"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"), +10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+ +this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&& +!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.leftg[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.topg[3])?h:!(h-this.offset.click.topg[2])?e:!(e-this.offset.click.left=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= +i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f
').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), +top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= +this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", +nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== +String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection(); +this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy(); +var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a= +false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"}); +this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff= +{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis]; +if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false}, +_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f, +{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight: +Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(cb.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left= +null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+ +a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+ +c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]); +b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.16"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(), +10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top- +f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType? +e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a= +e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing, +step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement= +e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset; +var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left: +a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top- +d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition, +f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25, +display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b= +e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height= +d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery); +;/* + * jQuery UI Selectable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"), +selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("
")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX, +c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting", +c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d= +this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable"); +this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a=== +"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&& +!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top, +left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]}; +this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!= +document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a); +return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0], +e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset(); +c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"): +this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null, +dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")}, +toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith(); +if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), +this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b= +this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f= +d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")|| +0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out", +a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h- +f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g- +this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this, +this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop", +a,this._uiHash());for(e=0;e li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"); +a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); +if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion", +function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a= +this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"); +this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons(); +b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target); +a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+ +c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options; +if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header); +if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(), +e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight|| +e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false", +"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.16", +animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/); +f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide", +paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); +;/* + * jQuery UI Autocomplete 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.position.js + */ +(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.propAttr("readOnly"))){g= +false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!= +a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)}; +this.menu=d("
    ").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&& +a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"); +d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&& +b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source= +this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length").data("item.autocomplete",b).append(d("").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, +"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery); +(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", +-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id"); +this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b, +this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| +this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| +this.first()?":last":":first"))},hasScroll:function(){return this.element.height()").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("");e.secondary&&a.append("");if(!this.options.text){d.push(f?"ui-button-icons-only": +"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")=== +"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"); +b.Widget.prototype.destroy.call(this)}})})(jQuery); +;/* + * jQuery UI Dialog 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.button.js + * jquery.ui.draggable.js + * jquery.ui.mouse.js + * jquery.ui.position.js + * jquery.ui.resizable.js + */ +(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false, +position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||" ",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+ +b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&!i.isDefaultPrevented()&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g), +h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id", +e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"); +a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!== +b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()};c.ui.dialog.maxZ+=1; +d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target=== +f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("
    ").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a, +function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close", +handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition, +originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize", +f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "): +[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f); +if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"): +e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a= +this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height- +b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.16",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "), +create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&& +c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(b.range==="min"||b.range==="max"?" ui-slider-range-"+b.range:""))}for(var j=c.length;j"); +this.handles=c.add(d(e.join("")).appendTo(a.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle", +g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!a.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");i=a._start(g,l);if(i===false)return}break}m=a.options.step;i=a.options.values&&a.options.values.length? +(h=a.values(l)):(h=a.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(i+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(i-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===a._valueMax())return;h=a._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===a._valueMin())return;h=a._trimAlignValue(i- +m);break}a._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(g,k);a._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy(); +return this},_mouseCapture:function(a){var b=this.options,c,f,e,j,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(b.range===true&&this.values(1)===b.min){g+=1;e=d(this.handles[g])}if(this._start(a,g)===false)return false; +this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();b=e.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-e.width()/2,top:a.pageY-b.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b= +this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b= +this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b); +c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var f;if(this.options.values&&this.options.values.length){f=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>f||b===1&&c1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a= +this.options.range,b=this.options,c=this,f=!this._animateOff?b.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({width:e- +g+"%"},{queue:false,duration:b.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:b.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[f?"animate":"css"]({width:e+"%"}, +b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.16"})})(jQuery); +;/* + * jQuery UI Tabs 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&& +e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b= +d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| +(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); +this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= +this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); +if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")); +this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+ +g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal", +function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")}; +this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected= +-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier."; +d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e= +d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b, +e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]); +j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove(); +if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null, +this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this}, +load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c, +"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, +url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.16"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k'))}function N(a){return a.bind("mouseout", +function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); +b.addClass("ui-state-hover");b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.16"}});var B=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv}, +setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g, +"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('
    '))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker", +function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b);b.settings.disabled&&this._disableDatepicker(a)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c== +"focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f==""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker(): +d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a, +b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.settings.disabled&&this._disableDatepicker(a);b.dpDiv.css("display","block")}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+= +1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/ +2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b= +d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e= +a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a, +"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f== +a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input", +a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=d.datepicker._get(b,"beforeShow");c=c?c.apply(a,[a,b]):{};if(c!==false){H(b.settings,c);b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value= +"";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b); +c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing= +true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}); +a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&& +!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(), +h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b= +this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b); +this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")}, +_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"): +0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e["selected"+(c=="M"? +"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a); +this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField"); +if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"? +b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd", +COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames: +null)||this._defaults.monthNames;var i=function(o){(o=k+1 +12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&& +a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay? +new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a)); +n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m, +g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+s+"":f?"":''+s+"";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&& +a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,s)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='
    '+(/all|left/.test(t)&& +x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'
    ';var z=j?'":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="=5?' class="ui-datepicker-week-end"':"")+'>'+q[r]+""}y+=z+"";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, +z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q";var R=!j?"":'";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&ro;R+='";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+""}g++;if(g>11){g=0;m++}y+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(r)+""+(F&&!D?" ":L?''+ +r.getDate()+"":''+r.getDate()+"")+"
    "+(l?""+(i[0]>0&&G==i[1]-1?'
    ':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': +"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
    ',o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&&l)?" ":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b, +e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="
    ";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+ +(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input? +a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c, +e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a, +"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this; +if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a== +"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery); +;/* + * jQuery UI Progressbar 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); +this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* +this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.16"})})(jQuery); +;/* + * jQuery UI Effects 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/ + */ +jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], +16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, +a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= +a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor", +"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0, +0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211, +211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, +d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})}; +f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this, +[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}), +d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement; +if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)}); +return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this, +arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/ +2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b, +d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c, +a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b, +d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ +e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); +;/* + * jQuery UI Effects Fade 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fade + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Fold 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fold + * + * Depends: + * jquery.effects.core.js + */ +(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1], +10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); +;/* + * jQuery UI Effects Highlight 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Highlight + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& +this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Pulsate 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Pulsate + * + * Depends: + * jquery.effects.core.js + */ +(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); +b.dequeue()})})}})(jQuery); +; \ No newline at end of file diff --git a/public/javascripts/jquery.menu_expand.js b/public/javascripts/jquery.menu_expand.js new file mode 100644 index 00000000..b584c85d --- /dev/null +++ b/public/javascripts/jquery.menu_expand.js @@ -0,0 +1,13 @@ +/* + * Expands Redmine's current menu + */ +(function($) { + $.menu_expand = function(options) { + var opts = $.extend({ + menu: '#main-menu', + menuItem: '.selected' + }, options); + + $(opts.menu +' '+ opts.menuItem).toggleClass("open").siblings("ul").show(); + + }})(jQuery); \ No newline at end of file diff --git a/public/javascripts/jquery.min.js b/public/javascripts/jquery.min.js new file mode 100644 index 00000000..3ca5e0f5 --- /dev/null +++ b/public/javascripts/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7 jquery.com | jquery.org/license */ +(function(a,b){function cA(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cx(a){if(!cm[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cn||(cn=c.createElement("iframe"),cn.frameBorder=cn.width=cn.height=0),b.appendChild(cn);if(!co||!cn.createElement)co=(cn.contentWindow||cn.contentDocument).document,co.write((c.compatMode==="CSS1Compat"?"":"")+""),co.close();d=co.createElement(a),co.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cn)}cm[a]=e}return cm[a]}function cw(a,b){var c={};f.each(cs.concat.apply([],cs.slice(0,b)),function(){c[this]=a});return c}function cv(){ct=b}function cu(){setTimeout(cv,0);return ct=f.now()}function cl(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ck(){try{return new a.XMLHttpRequest}catch(b){}}function ce(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bB(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function br(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bi,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bq(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bp(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bp)}function bp(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bo(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bn(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bm(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(){return!0}function M(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.add(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;B.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return a!=null&&m.test(a)&&!isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
    a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,unknownElems:!!a.getElementsByTagName("nav").length,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",enctype:!!c.createElement("form").enctype,submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.lastChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-999px",top:"-999px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;f(function(){var a,b,d,e,g,h,i=1,j="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",l="visibility:hidden;border:0;",n="style='"+j+"border:5px solid #000;padding:0;'",p="
    "+""+"
    ";m=c.getElementsByTagName("body")[0];!m||(a=c.createElement("div"),a.style.cssText=l+"width:0;height:0;position:static;top:0;margin-top:"+i+"px",m.insertBefore(a,m.firstChild),o=c.createElement("div"),o.style.cssText=j+l,o.innerHTML=p,a.appendChild(o),b=o.firstChild,d=b.firstChild,g=b.nextSibling.firstChild.firstChild,h={doesNotAddBorder:d.offsetTop!==5,doesAddBorderForTableAndCells:g.offsetTop===5},d.style.position="fixed",d.style.top="20px",h.fixedPosition=d.offsetTop===20||d.offsetTop===15,d.style.position=d.style.top="",b.style.overflow="hidden",b.style.position="relative",h.subtractsBorderForOverflowNotVisible=d.offsetTop===-5,h.doesNotIncludeMarginInBodyOffset=m.offsetTop!==i,m.removeChild(a),o=a=null,f.extend(k,h))}),o.innerHTML="",n.removeChild(o),o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[f.expando]:a[f.expando]&&f.expando,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[f.expando]=n=++f.uuid:n=f.expando),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[f.expando]:f.expando;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)?b=b:b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" "));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];if(!arguments.length){if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}return b}e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!a||j===3||j===8||j===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g},removeAttr:function(a,b){var c,d,e,g,h=0;if(a.nodeType===1){d=(b||"").split(p),g=d.length;for(;h=0}})});var z=/\.(.*)$/,A=/^(?:textarea|input|select)$/i,B=/\./g,C=/ /g,D=/[^\w\s.|`]/g,E=/^([^\.]*)?(?:\.(.+))?$/,F=/\bhover(\.\S+)?/,G=/^key/,H=/^(?:mouse|contextmenu)|click/,I=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,J=function(a){var b=I.exec(a);b&& +(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},K=function(a,b){return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||a.id===b[2])&&(!b[3]||b[3].test(a.className))},L=function(a){return f.event.special.hover?a:a.replace(F,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=L(c).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"",(g||!e)&&c.preventDefault();if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,n=null;for(m=e.parentNode;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l=0:t===b&&(t=o[s]=r.quick?K(m,r.quick):f(m).is(s)),t&&q.push(r);q.length&&j.push({elem:m,matches:q})}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),G.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),H.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",Z=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,_=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,ba=/<([\w:]+)/,bb=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bk=X(c);bj.optgroup=bj.option,bj.tbody=bj.tfoot=bj.colgroup=bj.caption=bj.thead,bj.th=bj.td,f.support.htmlSerialize||(bj._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after" +,arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Z,""):null;if(typeof a=="string"&&!bd.test(a)&&(f.support.leadingWhitespace||!$.test(a))&&!bj[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(_,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bn(a,d),e=bo(a),g=bo(d);for(h=0;e[h];++h)g[h]&&bn(e[h],g[h])}if(b){bm(a,d);if(c){e=bo(a),g=bo(d);for(h=0;e[h];++h)bm(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bc.test(k))k=b.createTextNode(k);else{k=k.replace(_,"<$1>");var l=(ba.exec(k)||["",""])[1].toLowerCase(),m=bj[l]||bj._default,n=m[0],o=b.createElement("div");b===c?bk.appendChild(o):X(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=bb.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&$.test(k)&&o.insertBefore(b.createTextNode($.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bt.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bs,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bs.test(g)?g.replace(bs,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bB(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bC=function(a,c){var d,e,g;c=c.replace(bu,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bD=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bv.test(f)&&bw.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bB=bC||bD,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bF=/%20/g,bG=/\[\]$/,bH=/\r?\n/g,bI=/#.*$/,bJ=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bK=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bL=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bM=/^(?:GET|HEAD)$/,bN=/^\/\//,bO=/\?/,bP=/)<[^<]*)*<\/script>/gi,bQ=/^(?:select|textarea)/i,bR=/\s+/,bS=/([?&])_=[^&]*/,bT=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bU=f.fn.load,bV={},bW={},bX,bY,bZ=["*/"]+["*"];try{bX=e.href}catch(b$){bX=c.createElement("a"),bX.href="",bX=bX.href}bY=bT.exec(bX.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bU)return bU.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bP,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bQ.test(this.nodeName)||bK.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bH,"\r\n")}}):{name:b.name,value:c.replace(bH,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?cb(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),cb(a,b);return a},ajaxSettings:{url:bX,isLocal:bL.test(bY[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bZ},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:b_(bV),ajaxTransport:b_(bW),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cd(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=ce(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bJ.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bI,"").replace(bN,bY[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bR),d.crossDomain==null&&(r=bT.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bY[1]&&r[2]==bY[2]&&(r[3]||(r[1]==="http:"?80:443))==(bY[3]||(bY[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),ca(bV,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bM.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bO.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bS,"$1_="+x);d.url=y+(y===d.url?(bO.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bZ+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=ca(bW,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)cc(g,a[g],c,e);return d.join("&").replace(bF,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cf=f.now(),cg=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cf++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cg.test(b.url)||e&&cg.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cg,l),b.url===j&&(e&&(k=k.replace(cg,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ch=a.ActiveXObject?function(){for(var a in cj)cj[a](0,1)}:!1,ci=0,cj;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ck()||cl()}:ck,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ch&&delete cj[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++ci,ch&&(cj||(cj={},f(a).unload(ch)),cj[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cm={},cn,co,cp=/^(?:toggle|show|hide)$/,cq=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cr,cs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],ct;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cw("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cz.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cz.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cA(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cA(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 4baed60e..920c951b 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -1,16 +1,17 @@ html {overflow-y:scroll;} -body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; } +body { font-size: 12px; margin: 0; padding: 0; min-width: 900px; } -h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;} +h1, h2, h3, h4 { font-family:"Arial", Arial, sans-serif; } h1 {margin:0; padding:0; font-size: 24px;} -h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;} -h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;} -h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;} +h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; } +h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; } +h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb;} +h5, .wiki h4 {font-size: 11px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb;} /***** Layout *****/ -#wrapper {background: white;} +#wrapper {background: none;} -#top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;} +#top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 0px; } #top-menu ul {margin: 0; padding: 0;} #top-menu li { float:left; @@ -19,85 +20,74 @@ h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; bord padding: 0px 0px 0px 0px; white-space:nowrap; } -#top-menu a {color: #fff; margin-right: 8px; font-weight: bold;} + #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; } -#account {float:right;} -#header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;} -#header a {color:#f8f8f8;} -#header h1 a.ancestor { font-size: 80%; } #quick-search {float:right;} -#main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;} +#side-container {position: absolute; bottom: auto; left:6px; margin-right: -500px;} #main-menu ul {margin: 0; padding: 0;} #main-menu li { - float:left; + float: none; list-style-type:none; - margin: 0px 2px 0px 0px; + margin: 0px; padding: 0px 0px 0px 0px; white-space:nowrap; } #main-menu li a { display: block; - color: #fff; text-decoration: none; - font-weight: bold; + font-weight: normal; + background: none; margin: 0; - padding: 4px 10px 4px 10px; + padding: 0 0 0 24px; } -#main-menu li a:hover {background:#759FCF; color:#fff;} -#main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;} +#main-menu li a:hover {color:#555; text-decoration: none;} -#admin-menu ul {margin: 0; padding: 0;} -#admin-menu li {margin: 0; padding: 0 0 12px 0; list-style-type:none;} - -#admin-menu a { background-position: 0% 40%; background-repeat: no-repeat; padding-left: 20px; padding-top: 2px; padding-bottom: 3px;} -#admin-menu a.projects { background-image: url(../images/projects.png); } -#admin-menu a.users { background-image: url(../images/user.png); } -#admin-menu a.groups { background-image: url(../images/group.png); } -#admin-menu a.roles { background-image: url(../images/database_key.png); } -#admin-menu a.trackers { background-image: url(../images/ticket.png); } -#admin-menu a.issue_statuses { background-image: url(../images/ticket_edit.png); } -#admin-menu a.workflows { background-image: url(../images/ticket_go.png); } -#admin-menu a.custom_fields { background-image: url(../images/textfield.png); } -#admin-menu a.enumerations { background-image: url(../images/text_list_bullets.png); } -#admin-menu a.settings { background-image: url(../images/changeset.png); } -#admin-menu a.plugins { background-image: url(../images/plugin.png); } -#admin-menu a.info { background-image: url(../images/help.png); } -#admin-menu a.server_authentication { background-image: url(../images/server_key.png); } - -#main {background-color:#EEEEEE;} - -#sidebar{ float: right; width: 22%; position: relative; z-index: 9; padding: 0; margin: 0;} -* html #sidebar{ width: 22%; } -#sidebar h3{ font-size: 14px; margin-top:14px; color: #666; } -#sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; } -* html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; } -#sidebar .contextual { margin-right: 1em; } - -#content { width: 75%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; } +#content { + width: 75%; + background-color: #fff; + margin: 0px; + border: 1px #ddd; + border-style: none solid solid solid; + padding: 6px 10px 10px 10px; + z-index: 10; +} * html #content{ width: 75%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;} -html>body #content { min-height: 600px; } +html>body #content { min-height: 600px; } * html body #content { height: 600px; } /* IE */ #main.nosidebar #sidebar{ display: none; } -#main.nosidebar #content{ width: auto; border-right: 0; } +#main.nosidebar #content{ width: auto } -#footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;} +#footer {clear: both; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center;} #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; } #login-form table td {padding: 6px;} #login-form label {font-weight: bold;} #login-form input#username, #login-form input#password { width: 300px; } +#login-form form { + border:1px solid #6DABC2; + -moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px; + display:block; + padding:10px; +} + input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; } .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; } /***** Links *****/ -a, a:link, a:visited{ color: #2A5685; text-decoration: none; } -a:hover, a:active{ color: #c61a1a; text-decoration: underline;} +a, a:link, a:visited { + text-decoration: none; + color: #6a0406; + font-weight:bold; + } +a:hover, a:active{ text-decoration: underline;} a img{ border: 0; } a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; } @@ -203,8 +193,6 @@ table.list tbody tr:hover { background-color:#ffffdd; } table.list tbody tr.group:hover { background-color:inherit; } table td {padding:2px;} table p {margin:0;} -.odd {background-color:#f6f7f8;} -.even {background-color: #fff;} a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; } a.sort.asc { background-image: url(../images/sort_asc.png); } @@ -240,8 +228,6 @@ div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padd .box{ padding:6px; margin-bottom: 10px; -background-color:#f6f6f6; -color:#505050; line-height:1.5em; border: 1px solid #e4e4e4; } @@ -261,17 +247,16 @@ div.square { .splitcontentright{float:right; width:49%;} form {display: inline;} input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;} -fieldset {border: 1px solid #e4e4e4; margin:0;} -legend {color: #484848;} +fieldset {border: 1px solid #e4e4e4; margin:0; padding:4px;} hr { width: 100%; height: 1px; background: #ccc; border: 0;} blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;} blockquote blockquote { margin-left: 0;} acronym { border-bottom: 1px dotted; cursor: help; } textarea.wiki-edit { width: 99%; } li p {margin-top: 0;} -div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;} +div.issue {padding:6px; margin-bottom:6px;} p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;} -p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; } +p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; } p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; } div.issue div.subject div div { padding-left: 16px; } @@ -282,6 +267,71 @@ div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;} #issue_tree table.issues { border: 0; } #issue_tree td.checkbox {display:none;} +#content fieldset#filters { + padding-bottom:10px; + } +fieldset#filters legend { + -moz-border-radius-bottomleft:0px; + -moz-border-radius-bottomright:0px; + -webkit-border-bottom-left-radius:0px; + -webkit-border-bottom-right-radius:0px; + border-bottom-left-radius:0px; + border-bottom-right-radius:0px; + } +fieldset#column_options legend { + -moz-border-radius-topleft:0px; + -moz-border-radius-topright:0px; + -webkit-border-top-left-radius:0px; + -webkit-border-top-right-radius:0px; + border-top-left-radius:0px; + border-top-right-radius:0px; + } +#content fieldset.collapsible.header_collapsible { + padding-top:0px; + padding-bottom:0px; + border:0px; + margin:0px; + } +fieldset.collapsible.header_collapsible > div { + padding-top:5px; + padding-bottom:5px; +} +fieldset.collapsible.header_collapsible > * { + border-left:1px solid #E6E6E6; + border-right:1px solid #E6E6E6; + border-bottom:1px solid #E6E6E6; + width:100%; + } +fieldset.collapsible.header_collapsible legend { + background:#E6E6E6 url(../images/double_arrow_toggle_up.png) no-repeat 99% 50%; cursor:pointer; + padding-left:0px; + width:100%; + height:23px; + line-height:23px; + text-indent:8px; + -moz-border-radius-topleft:5px; + -moz-border-radius-topright:5px; + -webkit-border-top-left-radius:5px; + -webkit-border-top-right-radius:5px; + border-top-left-radius:5px; + border-top-right-radius:5px; + -moz-border-radius-bottomleft:0px; + -moz-border-radius-bottomright:0px; + -webkit-border-bottom-left-radius:0px; + -webkit-border-bottom-right-radius:0px; + border-bottom-left-radius:0px; + border-bottom-right-radius:0px; +} + +fieldset.collapsible.header_collapsible legend:hover { + background-color:#d8d8d8; +} +fieldset.collapsible.collapsed.header_collapsible legend { + background-image:url(../images/double_arrow_toggle_down.png); + -moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px; + } fieldset.collapsible { border-width: 1px 0 0 0; font-size: 0.9em; } fieldset.collapsible.borders { border-width: 1px; } fieldset.collapsible.collapsed.borders { border-width: 1px 0 0 0; } @@ -314,7 +364,7 @@ div#activity dd span.description, #search-results dd span.description { display: div#search-results-counts {float:right;} div#search-results-counts ul { margin-top: 0.5em; } div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; } - + dt.issue { background-image: url(../images/ticket.png); } dt.issue-edit { background-image: url(../images/ticket_edit.png); } dt.issue-closed { background-image: url(../images/ticket_checked.png); } @@ -356,14 +406,15 @@ form .attributes select { min-width: 50%; } ul.projects { margin: 0; padding-left: 1em; } ul.projects.root { margin: 0; padding: 0; } +ul.projects ul {border: none; } ul.projects ul.projects { border-left: 3px solid #e0e0e0; } ul.projects li.root { list-style-type:none; margin-bottom: 1em; } ul.projects li.child { list-style-type:none; margin-top: 1em;} -ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; } +ul.projects div.root a.project { font-family:"Arial", Arial, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; } .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; } #tracker_project_ids ul { margin: 0; padding-left: 1em; } -#tracker_project_ids li { list-style-type:none; } +#tracker_project_ids li { list-style-type:none; } ul.properties {padding:0; font-size: 0.9em; color: #777;} ul.properties li {list-style-type:none;} @@ -377,7 +428,7 @@ ul.properties li span {font-style:italic;} #workflow_copy_form select { width: 200px; } -textarea#custom_field_possible_values {width: 99%} +textarea#custom_field_possible_values {width: 99%} .pagination {font-size: 90%} p.pagination {margin-top:8px;} @@ -387,7 +438,7 @@ p.pagination {margin-top:8px;} margin: 0; padding: 5px 0 8px 0; padding-left: 180px; /*width of left column containing the label elements*/ -height: 1%; +height: auto; clear:left; } @@ -398,7 +449,7 @@ font-weight: bold; float: left; text-align: right; margin-left: -180px; /*width of left column*/ -width: 175px; /*width of labels. Should be smaller than left column to create some right +width: 175px; /*width of labels. Should be smaller than left column to create some right margin*/ } @@ -453,7 +504,7 @@ div.attachments p { margin:4px 0 2px 0; } div.attachments img { vertical-align: middle; } div.attachments span.author { font-size: 0.9em; color: #888; } -p.other-formats { text-align: right; font-size:0.9em; color: #666; } +p.other-formats { font-size:0.9em; color: #666; } .other-formats span + span:before { content: "| "; } a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; } @@ -464,7 +515,7 @@ div#tab-content-members .splitcontentright, div#tab-content-memberships .splitco div#tab-content-members fieldset, div#tab-content-memberships fieldset, div#tab-content-users fieldset { padding:1em; margin-bottom: 1em; } div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend, div#tab-content-users fieldset legend { font-weight: bold; } div#tab-content-members fieldset label, div#tab-content-memberships fieldset label, div#tab-content-users fieldset label { display: block; } -div#tab-content-members fieldset div, div#tab-content-users fieldset div { max-height: 400px; overflow:auto; } +div#tab-content-members fieldset div, div#tab-content-users fieldset div, div#tab-content-memberships fieldset div { max-height: 400px; overflow:auto; } table.members td.group { padding-left: 20px; background: url(../images/group.png) no-repeat 0% 50%; } @@ -719,10 +770,6 @@ div.wiki .external { background-image: url(../images/external.png); } -div.wiki a.new { - color: #b73535; -} - div.wiki pre { margin: 1em 1em 1em 1.6em; padding: 2px 2px 2px 0; @@ -734,36 +781,36 @@ div.wiki pre { } div.wiki ul.toc { - background-color: #ffffdd; - border: 1px solid #e4e4e4; - padding: 4px; - line-height: 1.2em; - margin-bottom: 12px; - margin-right: 12px; - margin-left: 0; + margin: 0; + padding: 0 12px; display: table } + * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */ -div.wiki ul.toc {font-size: 0.8em;} -div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; } -div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; } +div.wiki fieldset {padding: 0} +div.wiki fieldset.collapsible.header_collapsible legend {background-position: 95% 50%} +div.wiki fieldset legend span { padding-right: 20px} +div.wiki fieldset.toc div {background-color: #fff} +div.wiki fieldset.toc.right { float: right; padding-left: 12px; width: auto; position: relative; } +div.wiki fieldset.toc.left { float: left; padding-right: 12px; width: auto; position: relative;} div.wiki ul.toc ul { margin: 0; padding: 0; } div.wiki ul.toc li { list-style-type:none; margin: 0;} div.wiki ul.toc li li { margin-left: 1.5em; } div.wiki ul.toc a { font-weight: normal; - text-decoration: none; - color: #606060; + background-image: url(../images/arrow-down.png); + background-repeat: no-repeat; + background-position: 0% 60%; + padding-left: 16px; } -div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;} a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; } a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; } h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; } -div.wiki img { vertical-align: middle; } +div.wiki img { vertical-align: middle; } /***** My page layout *****/ .block-receiver { @@ -773,9 +820,54 @@ padding: 15px 0 15px 0; } .mypage-box { -margin:0 0 20px 0; -color:#505050; -line-height:1.5em; + margin-top:20px; + padding: 0px 10px 0px 10px; + border: #C4C4C4 solid 1px; + font-size:11px; + background: #FFFFFF url(../images/background_widgets.png) repeat-x 0 0; + + /* Shadow definitions cross browser */ + -moz-box-shadow: 1px 1px 1px #BFBFBF; + -webkit-box-shadow: 1px 1px 1px #BFBFBF; + box-shadow: 1px 1px 1px #BFBFBF; + /* For IE 8 */ + -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=2, Direction=135, Color='#333333')"; + /* For IE 5.5 - 7 */ + filter: progid:DXImageTransform.Microsoft.Shadow(Strength=2, Direction=135, Color='#000000'); + zoom: 1; +} + +#content .mypage-box h3 { + margin: 9px 0px 6px; +} + +#content .mypage-box table { + padding: 10; + margin-top:15px; + margin-bottom:15px; +} + +.mypage-box h3, .mypage-box a { + font-size:11px; + font-weight:bold; +} + +#content .mypage-box table.issues td, #content .mypage-box table th, #content .mypage-box table.list { + border:none; + padding:0; + width:auto; + padding-right:30px; + padding-bottom:5px; +} + +#content .mypage-box table th { + border:none; + background: white; + font-weight: bold; +} + +#content .mypage-box table tr.even { + background-color: white; } .handle { @@ -784,7 +876,7 @@ cursor: move; a.close-icon { display:block; -margin-top:3px; +margin-top:10px; overflow:hidden; width:12px; height:12px; @@ -827,7 +919,7 @@ background-image:url('../images/close_hl.png'); .task.label.project, .task.label.version { font-weight: bold; } .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; } -.task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; } +.task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; } .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; } .task_todo.parent { background: #888; border: 1px solid #888; height: 3px;} @@ -908,19 +1000,14 @@ padding-bottom: 3px; .icon-file.application-zip { background-image: url(../images/files/zip.png); } .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); } -img.gravatar { +img.gravatar { padding: 2px; border: solid 1px #d5d5d5; background: #fff; } -div.issue img.gravatar { - float: right; - margin: 0 0 0 1em; - padding: 5px; -} - -div.issue table img.gravatar { +div.issue table img.gravatar, +p.author img.gravatar { height: 14px; width: 14px; padding: 2px; @@ -969,16 +1056,1505 @@ h2 img { vertical-align:middle; } .hascontextmenu { cursor: context-menu; } -/***** Media print specific styles *****/ -@media print { - #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; } - #main { background: #fff; } - #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;} - #wiki_add_attachment { display:none; } - .hide-when-print { display: none; } - .autoscroll {overflow-x: visible;} - table.list {margin-top:0.5em;} - table.list th, table.list td {border: 1px solid #aaa;} +/* Merged from the new layout #263 */ +/** + * ToC + * 00 - Base Typography + * 01 - Header + * 02 - Main Menu + * 03 - Main Content + * 04 - Issue Tables + * 05 - Single Issue Page + * 06 - Projects Page + * 07 - Behavior Styles + * 08 - Reusable classes + * 09 - New Issue Form + * 10 - Plugins and misc + * 11 - Appended from staging +*/ + +/*------------------------------------------------------------------------------- + * =00 - Base Typography + * + * This section includes the typography base for the body and heading elements. + ------------------------------------------------------------------------------*/ + +body { + font: normal normal normal 12px/1.5 arial,'lucida grandriale','lucida sans unicode',tahom,sans-serif; + background:#f3f3f3; + color:#333; + +} + +h1,h2,h3,h4,h5,h6 { + font-weight:bold; +} +h1 { + font-size:21px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-weight:normal; + margin-bottom:16px; +} +h4 { + font-size:14px; + margin-bottom:16px; +} +h5 { + font-size:12px; + text-transform: uppercase; + margin-bottom:18px; +} +a:hover { + text-decoration:underline; +} + +/*------------------------------------------------------------------------------- + * =01 - Header + * + * This section includes the site logo, main navigation background, and styles + * the header and navigation links. + ------------------------------------------------------------------------------*/ + + +#top-menu { + height: 81px; + position:relative; + z-index:21; +} +#top-menu.open { /*IE 6-7 z-index stacking issue*/ + z-index:5; +} +#account-nav { + display:block; + background:url(../images/background_top_navigation.png) repeat-x left top; +} +#logo { + float:left; + width:200px; + height:43px; + font-size: 3em; + padding-left: 15px; +} +#logo a, #logo a:hover { + color: #FFFFFF; + text-decoration: none; +} +#top-menu-items { + float:right; + padding-right:15px; +} +#header { + background: url(../images/background_header.png) repeat-x 20px; + height:43px; + font: arial,19px,white; +} +#header ul { + line-height:43px; + float:left; + font-size:13px; + font-family:"Arial", Arial, sans-serif; + font-style:normal; + font-weight:normal; + color:#ffffff; + height:43px; + border-top:0; + position:relative; + } + +#header ul#account-info { + margin-right:15px; +} +#header li { + float:left; + border-left:1px solid #450308; + position:relative; + z-index:5; +} +#header li:last-child { + border-right:0px; +} +#header li > a { + text-decoration:none; + color:#FFFFFF; + height:43px; + font-weight:normal; +} +#header li.drop-down select { + width:100%; +} +#header li.drop-down > a { + background:url(../images/arrow-down-white.png) no-repeat right center; + padding-right:35px; +} +#header li > a:hover { + background-color:#700407; +} +#header li.drop-down > a:hover { + background:#700407 url(../images/arrow-down-grey.png) no-repeat right center; + padding-right:35px; +} +#header li li a:hover { + background-color:#FFFFFF; + color:#222222; +} +#header li.drop-down li > a:hover, #main-menu li a.selected, #main-menu li a:hover { + -moz-border-radius-topleft:5px; + -moz-border-radius-topright:5px; + -webkit-border-top-left-radius:5px; + -webkit-border-top-right-radius:5px; + border-top-left-radius:5px; + border-top-right-radius:5px; + -moz-border-radius-bottomleft:5px; + -moz-border-radius-bottomright:5px; + -webkit-border-bottom-left-radius:5px; + -webkit-border-bottom-right-radius:5px; + border-bottom-left-radius:5px; + border-bottom-right-radius:5px; + background-color:#7F7F7F; + color:#FFFFFF; +} + +li a.home { + text-indent:-999em; + width:20px; + height:43px; + background:url(../images/icon_home.png) no-repeat 50% 50%; +} + +li a.help { + text-indent:-999em; + width:20px; + height:43px; + background:url(../images/icon_help.png) no-repeat 50% 50%; +} +li a.help:hover { + background-image:url(../images/icon_help_grey.png); +} +#header li.drop-down.open > a { + color: #6A0406; + background:#FFFFFF url(../images/arrow-down-grey.png) no-repeat right center; + padding-right:35px; +} +#account-nav li{ + padding:0; +} +#account-nav li a { + display:block; + padding:0px 9px 0px; +} +#header .search_field { + border:0px; + color:white; + background:transparent url(../images/background_search.png); + background-repeat:repeat-x; + margin-right:15px; + margin-top:6px; + margin-bottom:4px; + padding-left:10px; + padding-right:27px; + width:125px; + height:30px; +} +input::-webkit-input-placeholder { + color: white; +} +input:-moz-placeholder { + color: white; +} +#search { + float:left; +} +#header li.open > a { + position:relative; + top:0px; + z-index:21; +} +#header li > ul { + display:none; + position:absolute; + height:auto; + min-width:200px; + left:-1px; + z-index:20; + border: 1px solid #b7b7b7; + border-top: 0px; + background-color:#FFFFFF; +} +#header li.last-child > ul { + left:auto; + right:-1px; +} +#header li li { + float:none; + white-space:nowrap; + border-style:solid; + border-width:1px 0; +} +#header li.drop-down li > a { + padding:5px 9px; + background:#FFFFFF; + position:static; + color:#222222; + font-size:13px; + font-family:"Arial", Arial, sans-serif; + font-style:normal; + font-weight:normal; + margin-top:3px; + margin-bottom:3px; + margin-left:6px; + margin-right:6px; + height:15px; + line-height:15px; +} +#header li.drop-down li > a.separator { + border-bottom: 1px solid #555555; +} +#breadcrumb { + height:38px; + line-height:38px; + background:url(../images/background_breadcrumb.png) repeat-x left top; + border-bottom: 1px #d9d9d9; + color:#777777; + padding-left:15px; + font-size:16px; + font-family:"Arial", Arial, sans-serif; + font-style:normal; + font-weight:normal; +} +#breadcrumb a { + text-decoration:underline; + color:#777777; +} +#nav-search { + float: right; +} + +#nav-search input { + margin:5px 5px; + width:94%; +} +#nav-login-content { + position: absolute; + top:44px; + right: 0px; + float: right; + background: white; + border:1px solid #194E60; + border-top:0; + padding-left:5px; + padding-top:5px; + padding-right:10px; + padding-bottom:8px; + color:black; +} +#nav-login input[type=text], #nav-login input[type=password] { + height: 20px; + width: 150px; +} +#nav-login div a { + display: inline; + color:black; + padding:0; +} +div#optional_login_fields { + top:10px; + white-space:nowrap; + margin-left:2px; +} +/*------------------------------------------------------------------------------- + * =02 - Side container + * + * This section includes the layout and styles for the left navigation column and menu. + ------------------------------------------------------------------------------*/ + +#side-container { + width:185px; + height:100%; + position:absolute; + margin:10px 0 0; + left:0; +} +#main-menu ul { + border-top:1px solid #ddd; + border-bottom:1px solid #fff; +} +#main-menu ul ul { + display:none; + background-color:white; + padding-top:1px; + padding-bottom:1px; +} +#main-menu ul ul.menu-children.unattached { + border-top:1px solid #ddd; +} +#main-menu ul ul ul { + position:absolute; + left:185px; + z-index:20; + width:100%; + top:0; + border:1px solid #AFAFAF; + background:#f3f3f3; +} +#main-menu li { + border-top:1px solid #e2e2e2; + border-bottom:1px solid #f5f5f5; + position:relative; + min-height:23px; + background-color:#e9e9e9; +} +#main-menu li li { + border:0; + background-color:white; +} +#main-menu li li li { + padding:0; + width:100%; + border-bottom:1px solid #DDDDDD; + border-top:1px solid #FFFFFF; +} +#main-menu li li li:first-child { + border-top:0; +} +#main-menu a { + text-decoration:none; + line-height:23px; + display:block; + position:relative; + height:23px; + color:#333333; + font-weight:normal; + font-size:13px; + font-family:"Arial", Arial, sans-serif; + font-style:normal; +} +#main-menu li a { + margin:6px; + font-weight:bold; + padding: 0 0 0 20px; +} +#main-menu a.selected, #main-menu a:hover { +} +#main-menu li li a { + font-weight:normal; +} +#main-menu li li.current a { + font-weight:bold; +} +#main-menu li li a span { + font-weight:normal; + color:#999; + float:right; + padding-right:9px; +} +#main-menu li li li a span { + padding:0; +} +#main-menu .toggler { + position:absolute; + right:0px; + top:0px; + background:url(../images/double_arrow_toggle_down.png) no-repeat 32px 7px; + width:50px; + height:25px; +} +#main-menu li .open .toggler { + background-image:url(../images/double_arrow_toggle_up.png); +} +#main-menu li .open:hover .toggler { + background-image:url(../images/double_arrow_toggle_up_white.png); +} +#main-menu li .selected .toggler { + background-image:url(../images/double_arrow_toggle_down_white.png); +} +#main-menu li .selected.open .toggler { + background-image:url(../images/double_arrow_toggle_up_white.png); +} +#main-menu li a:hover .toggler { + background-image:url(../images/double_arrow_toggle_down_white.png); +} + +#main-menu li li .toggler { + display:none; +} +#main-menu li li li a { + padding:0 12px; +} +#main-menu li li ul.profile-box li { + padding:6px 12px; +} +#main-menu li li ul.profile-box li a { + display:inline; + padding:0; + color:#226D81; + line-height:1.5; +} +#main-menu li li ul.profile-box li a:hover { + text-decoration:underline; +} + +/* Mimic ".icon .icon-time" */ +#main-menu li a.time-details, #main-menu li a.billable-time-details, #main-menu li a.overhead-time-details { background-image:url(../../../images/time.png); background-position:30px 40%; background-repeat:no-repeat; padding-left:50px; } + +#main-menu p.password { font-weight: bold; margin: 25px 0; } + + + +/*------------------------------------------------------------------------------- + * =03 - Main Content + * + * This section includes the layout and styles for the main content area. + ------------------------------------------------------------------------------*/ +#main { + background:#f3f3f3; + position:relative; + z-index:20; + } +#footer { color: #aaa; padding-left: 186px } + +h1.title { + margin:12px 24px 9px; +} +#content { + margin:0 15px 10px 185px; + padding:10px; + font-size:11px; + width: auto; +} +#content .title-bar { + position:relative; + margin-bottom:10px; + top: -10px; + +} +.title-bar h2 { + padding:9px 100px 9px 12px; + color:#000; + font-weight:normal; + font-weight:bold; +} +.title-bar h2 span { + font-weight:bold; +} + +.button-large { +} + +/* .button-large was too generic and targeted other pages with it's positioning */ +.title-bar .button-large { + float: left; + padding: 2px 0 3px; +} + +.button-large a { + line-height: 3.5; + display:block; + color:#6a0406; +} +.title-bar .add-filter { + right:155px; +} + +fieldset#filters div.add-filter { + text-align: right; +} + +.title-bar .new-issue { + right:10px; +} +.title-bar .new-issue a { + background:url(../images/add.png) no-repeat 6px center; + padding-left:26px; +} +.title-bar-extras { + color:#333333; +} +.title-bar-extras ul { + padding:10px; + overflow:auto; +} +.title-bar-extras li { + padding-bottom:5px; +} +.title-bar-extras select { + margin-right:10px; +} +.extras-actions { + padding:5px 10px; + position:relative; +} +.extras-actions a { + border:0; + color:#000; + padding-left:18px; + margin-right:10px; + cursor:pointer; + font-family:inherit; + font-size:11px; +} +input.apply { + background-image:url(../images/check.png); +} +input.clear { + background-image:url(../images/refresh.png); +} +input.save { + background-image:url(../images/disk.png); +} +a#extras-close { + position:absolute; + color:#fff; + background:url(../images/arrow-up-white.png) no-repeat right center; + right:10px; + padding-right:15px; + display:none; /* Remove to show the Hide Filters thing */ +} + +/*------------------------------------------------------------------------------- + * =04 - Issue Table + * + * This section includes the layout and styles for the main issues table. + ------------------------------------------------------------------------------*/ + +form#issue-list { + position:relative; +} +#content table.issues { + width:100%; +} +#content table th { + font-weight:normal; + background:#f3f3f3 url(../images/gradient-down.png) repeat-x; +} +#content table.issues td, #content table th { + border:1px solid #e6e6e6; + padding:6px; + text-align:left; + position:relative; + vertical-align:top; +} +#content table th a { + color:#111; + text-decoration:none; +} +#content table th.current-sort { + background:#fff url(../images/gradient-up.png) repeat-x; +} +#content table th.current-sort a { + background:url(../images/arrow-down-3.png) no-repeat right center; + padding-right:16px; + display:block; +} +#content table tr.even { + background-color:#f9f9f9; +} +#content table td.priority { + text-align:center; +} +#content table td.issue { + background:url(../images/arrow-bottom-right.png) no-repeat right bottom; + width:42px; + cursor:context-menu; +} +#content table td.updated { + width:80px; +} +#content table a.toggle-select { + background:url(../images/check.png) no-repeat center center; + display:block; + text-indent:-9999em; +} +#multiple-action-buttons { + float:left; + margin:10px 0; +} +#multiple-action-buttons li { + float:left; + margin-right:10px; +} +.pagination, .other-formats { + margin:10px 0; +} +.pagination a { + padding:1px 2px; +} + +/*------------------------------------------------------------------------------- + * =05 - Single Issue Page + * + * This section includes the layout and styles for the single issues page. + ------------------------------------------------------------------------------*/ + +.title-bar-actions { + position:absolute; + right:0px; + top:0; + padding:0 100px 0 10px; + height:100%; +} +.title-bar-actions .contextual a { +/* color:#fff;*/ +} +.title-bar .title-bar-actions .contextual a.icon { + color:#000000; + margin-right: 0px; +} +.title-bar .update { + right:0; +} +.title-bar .update a { + padding-left:26px; + background:url(../images/edit.png) no-repeat 5px center; + font-weight:bold; +} +div.issue { + padding:10px; +} +div.issue hr { + margin:10px -10px; + clear:both; +} +div.issue h3 { + font-size:14px; + border:0px; +} +#content .meta table { + border:0 none; +} +#content .meta table tr:hover { + background:none; +} +#content .meta table td, #content .meta table th { + background: none; + border:0 none; + padding:0 3px; +} +#content .meta table th { + font-weight:bold; +} +#content .meta table td a:hover, #content .meta table th a:hover { + text-decoration:underline; +} +#content .meta table td.priority { + text-align:left; /* Is set to center above */ +} +.gravatar { + border:1px solid #aaa; +} +.issue p { + margin-bottom:5px; +} +.attachments h4 { + margin-bottom:6px; + background:url(../images/files-showhide.png) no-repeat right bottom; + cursor:pointer; +} +.attachments h4.closed { + background-position:right 5px; +} + +table.files { + display: table; +} +#content table.files td, #content table.files th, #content table.files { + border:0; + background:none; +} +#content table.files th { + font-weight:bold; + padding:1px; +} +#content table.files td { + color:#555; + padding:1px; +} +#content table.files .opt-desc { + width:60%; +} +#content table.files td a { + position:relative; +} +#history { + margin:20px 0; +} +#history h3 { + font-size:14px; + border-bottom:1px solid #ddd; + padding-left:10px; + margin-bottom:20px; +} +#history .journal { + margin:0 0 45px; +} +.journal.has-avatar .journal-details { + padding-left: 50px; +} +.journal .profile-wrap { + float: left; + margin: 5px 0 0; +} + +.journal h4 { + font-size:12px; + font-weight:normal; + margin-bottom:10px; + padding-right: 0; + z-index:5; +} +.journal h4 .history-id { + float:right; + color:#999; +} +.journal .wiki { + overflow:auto; +} +.journal .contextual { + float:right; +} +.journal .contextual a { + float:left; + display:block; + margin:0 0 0 5px; + height:16px; + width:16px; + background-repeat:no-repeat; + background-position:center center; +} +.contextual .edit { + background-image:url(../images/edit.png); +} +.contextual .comment { + background-image:url(../images/comment.png); +} +.question-line { + display:block; +} +#content blockquote, .wiki ol, .wiki ul { + padding-left:40px; +} +.wiki p { + margin-bottom:5px; +} +blockquote { + font-style:italic; + background:url(../images/blockquote-bg.png) no-repeat 25px 3px; +} +.wiki ul li { + list-style: disc outside none; +} +.file-thumbs { + margin:20px 0 0; + overflow:hidden; + float: left; +} +.file-thumbs a { + display:block; + float:left; + margin-right:10px; + text-align:center; +} +.file-thumbs a img { + display:block; + margin:0 auto 5px; + border:1px solid #226D81; +} +.file-thumbs a img.pdf { + border:0; +} + +.journal-attributes {color: #999999;} + +/*------------------------------------------------------------------------------- + * =06 - Projects Page + * + * This section defines the styles for the projects "home" page. + ------------------------------------------------------------------------------*/ + +#content.nosidebar { + margin-left:20px; + padding:15px 60px 15px 25px; + font-size:12px; + +} +#project-links { + position:absolute; + right:30px; + color:#ccc; + font-weight:bold; +} +.nosidebar blockquote { + margin:1em 0; +} +.nosidebar p { + margin-bottom:1em; +} +li.root { + font-size:18px; + margin-bottom:24px; +} +li.child { + font-size:14px; +} +.nosidebar ul.projects { + margin:24px 0 0; +} +.nosidebar ul.projects ul { + margin:0; +} +ul.projects .description { + font-size:12px; +} +.nosidebar ul.projects li { + list-style:none outside none; + background: none; +} +.nosidebar ul.projects li .my-project { + padding:0 0 0 24px; + background:url(../images/star.png) no-repeat left top; +} +ul.projects a { + font-weight:bold; +} +ul.projects li div.root { + margin-bottom:12px; +} +.nosidebar ol li { + list-style: decimal outside none; + margin-left:24px; +} +.nosidebar ul li { + background:url(../images/dot-blue.png) no-repeat left top; +} +.nosidebar ol, .nosidebar ul { + margin:0 0 12px 18px; +} + + +/*------------------------------------------------------------------------------- + * =07 - Behavior Styles + * + * This section defines the styles for handling behaviors - popups, flyouts, etc. + ------------------------------------------------------------------------------*/ + +.profile-box { + position:absolute; + right:0; + top:45px; + width:205px; + display:none; + z-index:10; +} +.issue .profile-box ul, .journal .profile-box ul { + background:url(../images/profile-arrow-up.png) no-repeat 175px top; + position:relative; + z-index:11; + top:-8px; + padding-top:8px; + margin-bottom:-8px; +} +.journal .profile-box { + right:auto; + left:0; +} +.journal .profile-box ul { + background-position: 13px top; +} +.profile-box ul li { + border-top:1px solid #fff; + border-bottom:1px solid #ddd; + padding:5px 10px; +} +.profile-box ul li:first-child { + border-top:0; +} +.profile-box ul li:last-child { + border-bottom:0; +} +.profile-box .gravatar { + border:0; + float:left; + margin-right:6px; +} +.profile-box .vcard { + padding-left:20px !important; + background:url(../images/vcard.png) no-repeat left center; + display:block; +} + +/* file table hovers */ +a.has-thumb img { + position:absolute; + display:none; + border:1px solid #a6c6cf; + padding:4px; + background:#fff; + -moz-border-radius:3px; + -webkit-border-radius:3px; + border-radius:3px; + +} +a.has-thumb.active { + left:-10px; + background:url(../images/thumb-arrow-right.png) no-repeat left center; + padding-left:10px; + margin-right:-10px; +} + +td.issue div.issue-wrap-outer { + position:relative; +} + +/* table tooltips */ +.js-tooltip { + position:absolute; + left:-30px; + z-index:20; +} +.js-tooltip-inner { + position:absolute; + bottom:5px; + padding:10px; + width:500px; + font-size:11px; + max-height:200px; + overflow:hidden; + z-index:15; +} +.js-tooltip .arrow { + width:16px; + height:12px; + position:absolute; + bottom:-6px; + left:76px; + z-index:16; + background:url(../images/tooltip-arrow.png) no-repeat left top; +} +.js-tooltip .meta { + margin-top:20px; + overflow:hidden; +} +.js-tooltip .meta li { + float:left; + margin-right:30px; +} +.button-large ul { + position:absolute; + right:-1px; + top:20px; + z-index:5; + display:none; +} +.button-large ul li { + padding:0; + white-space:nowrap; +} +.title-bar .button-large ul li a { + background-image:none; + padding:6px 12px; +} +.title-bar .button-large ul li a:hover { + background-color:#fff; +} + +/*------------------------------------------------------------------------------- + * =08 - Reusable Classes + * + * This section defines reusable classes for menus, etc. + ------------------------------------------------------------------------------*/ + +.menu li { + position:relative; + padding:6px; +} +.menu li:first-child { + border-top:0; +} +.menu li:last-child { + border-bottom:0; +} +.inline { + display: inline; +} + +/*------------------------------------------------------------------------------- + * =11 - Appended from staging + ------------------------------------------------------------------------------*/ +/* tooltip fix */ +form#issue-list { + display:block; +} +.js-tooltip, .js-tooltip-inner {width:100%;} +.js-tooltip-inner { + max-height:none; +} +.js-tooltip .issue-tooltip-description { + max-height:200px; + overflow:hidden; +} + +/* roadmap breathing */ +#roadmap h3 { + margin:21px 0 12px; +} +div#roadmap fieldset.related-issues { + margin: 12px 0; + padding:6px 12px; + -moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px; +} +#roadmap fieldset legend { + font-style: italic; +} + +tr.context-menu-selection td.priority { + background:none !important; +} + +/* Blue dots killed */ +.nosidebar ul li { + background:none; + list-style: disc outside none; +} +.nosidebar ul { + margin:12px 0 12px 18px; +} + +ul.projects div.root a.project { + font-family:inherit; +} +#content #login-form table { + border:0 none; + background:none; + margin:0; +} +#content #login-form table tr:hover { + background:none; +} +#login-form table td, #login-form table th { + border:0 none; +} + +/* tables don't all need border you know */ +#relations table td, #relations table th { + border:0 none; +} + +/* sidebar cleanup */ +#sidebar { + padding:10px 2px 2px 20px; + width:auto; + color:#888888; + font-weight:normal; + font-size:11px; + font-family:"Arial", Arial, sans-serif; + font-style:normal; +} +#sidebar h3 { + color:#777777; + font-weight:bold; + font-size:12px; + font-family:"Arial", Arial, sans-serif; + font-style:normal; + margin:0px; + padding:0px; + margin-bottom:8px; +} +#sidebar a, #sidebar a:link, #sidebar a:visited { + color: #6a0406; + font-weight:bold; + height:auto; + display:inline; + position:static; + font-size:11px; + font-style:normal; + line-height:1.5; +} +#sidebar a:hover { + text-decoration:underline; +} +#sidebar input.button-small { + margin-top:6px; +} +#sidebar ul {border: none; } +#sidebar li {border: none; } +#sidebar li a {padding: 0px; } + +#main-menu li li a { + padding-left:30px; + padding-right:3px; + text-indent:-6px; + letter-spacing:-.01em; +} +#main-menu li a.time-details, #main-menu li a.overhead-time-details, #main-menu li a.billable-time-details { + padding-left:40px; + background-position:12px 45%; +} + +/* custom query page */ +#content .box fieldset { + border:1px solid #ddd; + margin:18px 10px 6px; + padding:10px; +} +#content .box fieldset legend { + font-weight:bold; +} +.box fieldset li.filter { + padding-top:6px; + overflow:hidden; +} +.box fieldset select { + margin-right:6px; +} +.box fieldset #add_filter_select { + margin-bottom:6px; +} +.box li.filter label { + clear:left; + float:left; + width:170px; +} +fieldset#columns table { + width:auto; +} +fieldset#columns td { + border:0; + vertical-align:middle; +} + +/* Flash notices */ +div.flash { + margin:0 0 10px; + border:1px solid; + -moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px; +} + +/* all kinds of wonderful tweaks */ + +#account li li:last-child a:hover { + -moz-border-radius-bottomleft:5px; + -moz-border-radius-bottomright:5px; + -webkit-border-bottom-left-radius:5px; + -webkit-border-bottom-right-radius:5px; + border-bottom-left-radius:5px; + border-bottom-right-radius:5px; +} +.question pre { + color:#111; +} +.box p { + padding-top:5px; + padding-bottom:8px; +} +#content .box h3 { + margin-top:3px; +} +div.issue hr { + width:auto; +} +.question .wiki { + margin:0; +} +.wiki { + font-size:12px; +} +.wiki ol, .wiki ul { + margin-bottom:6px; +} +#content h3 { + margin:12px 0 6px; +} +#content h2 + h3 { + margin-top:12px; +} +p.author { + margin-bottom:3px; + font-style:italic; +} + +/* add filter select box on non-issue pages */ +fieldset#filters div.add-filter { + text-align:left; + margin:0 0 6px 0; +} +.nosidebar #add_filter_select { + margin-bottom:6px; +} +.nosidebar .box fieldset { + line-height:1.5; + margin:0 0 12px 180px; +} +.nosidebar .box fieldset legend { + margin-bottom:6px; +} +.nosidebar fieldset ul li { + background:none; +} +.title-bar .add-filter.button-large { + background:none; + border:none; +} +.title-bar .contextual { + padding:0 12px 0 12px; + position:absolute; + right:0px; + top:0; + margin:0; +} +.title-bar .contextual a.icon { + float: left; + line-height:3.5; + margin-right:16px; +} +.title-bar .grouping { + padding:0 10px 10px; +} +.title-bar-extras ul { + border-bottom:none; +} +.extras-actions { + border-top:none; +} +#content .meta table.progress { + border:1px solid #bbb; + border-collapse:separate; + -moz-border-radius:3px; + -webkit-border-radius:3px; + border-radius:3px; +} +.nosidebar fieldset ul { + margin-left:0; +} +.nosidebar ol.ui-sortable li { + list-style: none outside none; +} +tr.time-entry { + white-space:normal; +} +.meta td.priority { + background:none !important; +} + +/*===== Replacement Images =====*/ + +.icon-edit, .title-bar .update a { + background-image:url(../images/pencil.png); +} +.icon-del { + background-image:url(../images/delete.png); +} +.journal .contextual a[title=Edit] img { + display:none; +} +.journal .contextual a[title=Edit] { + background:url(../images/pencil.png) no-repeat; +} + + +/* -- New #main-menu toggle CSS */ +#main-menu .toggle-follow { + padding:5px 5px 5px 0; +} + +/* Weird Safari cascade bug. More specificity */ +div.issue p, div.issue div, #content td { + font-size:11px; +} + +/* comments */ +.wiki ol li { + list-style: decimal outside; +} + +/* scm */ +#content table .changeset td.id a:hover { + text-decoration:underline; +} +#history .journal { + clear:left; + min-height: 50px; + margin-bottom:45px; +} + +/* issue updates */ +#update form#issue-form .attributes p { + padding-bottom:5px; +} +#update fieldset .box { + padding:0; + border:0 none; +} +#update .tabular label { + width:140px; + margin-left:-147px; +} +#update .tabular p { + padding-left:140px; +} + +/* Delete icon */ +table.files a.icon-delete { + float:left; + padding:0; + display:block; + text-indent:-9999em; + width:16px; + height:16px; + background:url(../images/delete.png) no-repeat 0 0; + margin-right:6px; +} + + +/* clearfix */ +html > body #content:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + +} +#content table.files .opt-desc { + width:45%; +} + +/* member settings [pc] */ + +select#member_role_id { + width:75px; +} + +/* fix for thumbnail jankiness */ +a.has-thumb.active { + left:auto; + margin-left:-10px; + margin-right:0; + *left:-10px; /* IE6 & 7 hacks */ + *margin-left:0; +} +a.has-thumb img { + z-index:1001; +} + +/* max height on menus */ +#context-menu li.assigned > ul { + max-height:250px; + overflow-x:hidden; + overflow-y:auto; +} + +/* Make icons non repeating */ + +#more-menu.drop-down ul li a.projects { border-top:1px solid #555; } + +/* Adds a simple rounded corner background */ +.rounded-background { background: #E6E5E5; -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; } + +/* Used on the wiki to show a page hierarchy/tree */ +.pages-hierarchy { padding-left: 10px; } + +/************************************************************************* +Additional wiki styles +*************************************************************************/ + +.button { + padding-left: .25em; + padding-right: .25em; + background:#507aaa; + color: white; + font-weight: bold; +} + +.wiki p.see-also, .wiki p.caution, .wiki p.important, .wiki p.info, .wiki p.tip, .wiki p.note, +.wiki span.see-also, .wiki span.caution, .wiki span.important, .wiki span.info, .wiki span.tip, .wiki span.note { + display: block; + margin-top: .5em; + margin-bottom: .5em; + + padding: 4px 4px 4px 48px; + min-height: 33px; +} + +/* Reset .wiki p.important for syntax highlighting rules */ +.syntaxhl .CodeRay .important { + background: none; + border: none; + display: inline; + margin: 0; + padding: 0; + min-height: 0; +} + +.wiki p.smallsee-also, .wiki p.smallcaution, .wiki p.smallimportant, .wiki p.smallinfo, .wiki p.smalltip, .wiki p.smallnote, +.wiki span.smallsee-also, .wiki span.smallcaution, .wiki span.smallimportant, .wiki span.smallinfo, .wiki span.smalltip, .wiki span.smallnote { + display: block; + margin-top: .5em; + margin-bottom: .5em; + + padding: 4px 4px 4px 34px; + min-height: 24px; +} + +.wiki p.see-also, .wiki span.see-also { + background: url(../images/wiki_styles/see-also.png) 4px 4px no-repeat #f5fffa; + border: 1px solid #AAB1AD; +} +.wiki p.smallsee-also, .wiki span.smallsee-also { + background: url(../images/wiki_styles/see-also_small.png) 4px 4px no-repeat #f5fffa; + border: 1px solid #AAB1AD; +} + +.wiki p.caution, .wiki span.caution { + background: url(../images/wiki_styles/caution.png) 4px 6px no-repeat #f5fffa; + border: 1px solid #AAB1AD; +} +.wiki p.smallcaution, .wiki span.smallcaution { + background: url(../images/wiki_styles/caution_small.png) 4px 4px no-repeat #f5fffa; + border: 1px solid #AAB1AD; +} + +.wiki p.important, .wiki span.important { + background: url(../images/wiki_styles/important.png) 4px 7px no-repeat #F0F8FF; + border: 1px solid #C1C8CF; +} +.wiki p.smallimportant, .wiki span.smallimportant { + background: url(../images/wiki_styles/important_small.png) 4px 6px no-repeat #F0F8FF; + border: 1px solid #C1C8CF; +} + +.wiki p.info, .wiki span.info { + background: url(../images/wiki_styles/info.png) 4px 4px no-repeat #FFFFE0; + border: 1px solid #FFFF00; +} +.wiki p.smallinfo, .wiki span.smallinfo { + background: url(../images/wiki_styles/info_small.png) 4px 4px no-repeat #FFFFE0; + border: 1px solid #FFFF00; +} + +.wiki p.tip, .wiki span.tip { + background: url(../images/wiki_styles/tip.png) 4px 4px no-repeat #F5FFFA; + border: 1px solid #C7CFCA; +} +.wiki p.smalltip, .wiki span.smalltip { + background: url(../images/wiki_styles/tip_small.png) 4px 5px no-repeat #F5FFFA; + border: 1px solid #C7CFCA; +} + +.wiki p.note, .wiki span.note { + background: url(../images/wiki_styles/note.png) 6px 4px no-repeat #F5FFFA; + border: 1px solid #C7CFCA; +} +.wiki p.smallnote, .wiki span.smallnote { + background: url(../images/wiki_styles/note_small.png) 5px 4px no-repeat #F5FFFA; + border: 1px solid #C7CFCA; } diff --git a/public/stylesheets/context_menu.css b/public/stylesheets/context_menu.css index f86e12e0..4580042a 100644 --- a/public/stylesheets/context_menu.css +++ b/public/stylesheets/context_menu.css @@ -1,4 +1,4 @@ -#context-menu { position: absolute; z-index: 40; font-size: 0.9em;} +#context-menu { position: absolute; z-index: 40; } #context-menu ul, #context-menu li, #context-menu a { display:block; @@ -47,6 +47,5 @@ #context-menu li:hover ul, #context-menu li:hover li:hover ul { display:block; } /* selected element */ -.context-menu-selection { background-color:#507AAA !important; color:#f8f8f8 !important; } -.context-menu-selection a, .context-menu-selection a:hover { color:#f8f8f8 !important; } -.context-menu-selection:hover { background-color:#507AAA !important; color:#f8f8f8 !important; } +.context-menu-selection { background-color:#507AAA !important; color:#000 !important; } +.context-menu-selection:hover { background-color:#507AAA !important; color:#000 !important; } diff --git a/public/stylesheets/ie6.css b/public/stylesheets/ie6.css new file mode 100644 index 00000000..cafd0d8f --- /dev/null +++ b/public/stylesheets/ie6.css @@ -0,0 +1,46 @@ +/* IE6 how i love to hate thee */ + +#account-nav li a { + width:45px; +} +#account-nav li li a { + width:150px; +} +.title-bar { + zoom:1; +} +.title-bar-extras label { + float:none; + display:inline; + padding-right:10px; +} +.issue-dropdown li.hover { + background-color:#fff; +} +.issue-dropdown li.hover ul { + display:block; + left:112px; +} +body .file-thumbs a { + width:150px; +} +#history .journal { + zoom:1; +} +body #history .wiki { + overflow:hidden; + zoom:1; +} +#main-menu li li { + height:30px; +} +#main-menu li li li { + height:auto; +} +a.has-thumb.active { + background:none; +} +.title-bar-extras ul { + background-image:none; + border-top:1px solid #154E5D; +} \ No newline at end of file diff --git a/public/stylesheets/ie7.css b/public/stylesheets/ie7.css new file mode 100644 index 00000000..6925d908 --- /dev/null +++ b/public/stylesheets/ie7.css @@ -0,0 +1,48 @@ +/* These will be included for IE6 & IE7 */ + +.title-bar h2 { + height:21px; +} +td.dropdown { + z-index:50; + position:relative; +} + +body .title-bar-extras { + overflow:hidden; +} + +#main-menu, .title-bar { + z-index:4; +} +.title-bar .button-large ul { + z-index:15; +} +form.tooltip-active { + z-index:14; +} +#main-menu li li a span { + position:absolute; + right:0; + top:0; + display:block; +} +#main-menu li li li a span { + right:10px; +} + +body .file-thumbs a { + max-width:150px; +} + +#watchers { + position:relative; + z-index:5; +} +div.attachments { + position:relative; + z-index:4; +} +fieldset.collapsible.header_collapsible legend { + margin-left:-15px; +} diff --git a/public/stylesheets/print.css b/public/stylesheets/print.css new file mode 100644 index 00000000..d589de30 --- /dev/null +++ b/public/stylesheets/print.css @@ -0,0 +1,11 @@ +/***** Media print specific styles *****/ +@media print { + #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; } + #main { background: #fff; } + #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;} + #wiki_add_attachment { display:none; } + .hide-when-print { display: none; } + .autoscroll {overflow-x: visible;} + table.list {margin-top:0.5em;} + table.list th, table.list td {border: 1px solid #aaa;} +} diff --git a/public/stylesheets/redmine-reset.css b/public/stylesheets/redmine-reset.css new file mode 100644 index 00000000..e69de29b diff --git a/public/stylesheets/reset.css b/public/stylesheets/reset.css new file mode 100644 index 00000000..6d771b36 --- /dev/null +++ b/public/stylesheets/reset.css @@ -0,0 +1,38 @@ +/* +* CSS Reset +* Based on http://meyerweb.com/eric/tools/css/reset/ +*/ + +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} + +ins { + text-decoration: underline; +} +del { + text-decoration: line-through; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} + +/* Helper classes */ +.clearfix { + clear:both; +} \ No newline at end of file diff --git a/public/stylesheets/scm.css b/public/stylesheets/scm.css index b974e2b3..2b1fed44 100644 --- a/public/stylesheets/scm.css +++ b/public/stylesheets/scm.css @@ -91,105 +91,91 @@ div.action_A { background: #bfb } /************* CodeRay styles *************/ .syntaxhl div {display: inline;} -.syntaxhl .no { padding: 2px 4px 2px 4px; background-color: #eee; margin:0 } +.syntaxhl .line-numbers { padding: 2px 4px 2px 4px; background-color: #eee; margin: 0 7px 0 0; } .syntaxhl .code pre { overflow: auto } .syntaxhl .debug { color:white ! important; background:blue ! important; } -.syntaxhl .af { color:#00C } -.syntaxhl .an { color:#007 } -.syntaxhl .at { color:#f08 } -.syntaxhl .av { color:#700 } -.syntaxhl .aw { color:#C00 } -.syntaxhl .bi { color:#509; font-weight:bold } -.syntaxhl .c { color:#888; } +.syntaxhl .annotation { color:#007 } +.syntaxhl .attribute-name { color:#b48 } +.syntaxhl .attribute-value { color:#700 } +.syntaxhl .binary { color:#509 } +.syntaxhl .char .content { color:#D20 } +.syntaxhl .char .delimiter { color:#710 } +.syntaxhl .char { color:#D20 } +.syntaxhl .class { color:#B06; font-weight:bold } +.syntaxhl .class-variable { color:#369 } +.syntaxhl .color { color:#0A0 } +.syntaxhl .comment { color:#777 } +.syntaxhl .comment .char { color:#444 } +.syntaxhl .comment .delimiter { color:#444 } +.syntaxhl .complex { color:#A08 } +.syntaxhl .constant { color:#036; font-weight:bold } +.syntaxhl .decorator { color:#B0B } +.syntaxhl .definition { color:#099; font-weight:bold } +.syntaxhl .delimiter { color:black } +.syntaxhl .directive { color:#088; font-weight:bold } +.syntaxhl .doc { color:#970 } +.syntaxhl .doc-string { color:#D42; font-weight:bold } +.syntaxhl .doctype { color:#34b } +.syntaxhl .entity { color:#800; font-weight:bold } +.syntaxhl .error { color:#F00; background-color:#FAA } +.syntaxhl .escape { color:#666 } +.syntaxhl .exception { color:#C00; font-weight:bold } +.syntaxhl .float { color:#60E } +.syntaxhl .function { color:#06B; font-weight:bold } +.syntaxhl .global-variable { color:#d70 } +.syntaxhl .hex { color:#02b } +.syntaxhl .imaginary { color:#f00 } +.syntaxhl .include { color:#B44; font-weight:bold } +.syntaxhl .inline { background-color: hsla(0,0%,0%,0.07); color: black } +.syntaxhl .inline-delimiter { font-weight: bold; color: #666 } +.syntaxhl .instance-variable { color:#33B } +.syntaxhl .integer { color:#00D } +.syntaxhl .key .char { color: #60f } +.syntaxhl .key .delimiter { color: #404 } +.syntaxhl .key { color: #606 } +.syntaxhl .keyword { color:#080; font-weight:bold } +.syntaxhl .label { color:#970; font-weight:bold } +.syntaxhl .local-variable { color:#963 } +.syntaxhl .namespace { color:#707; font-weight:bold } +.syntaxhl .octal { color:#40E } +.syntaxhl .operator { } +.syntaxhl .predefined { color:#369; font-weight:bold } +.syntaxhl .predefined-constant { color:#069 } +.syntaxhl .predefined-type { color:#0a5; font-weight:bold } +.syntaxhl .preprocessor { color:#579 } +.syntaxhl .pseudo-class { color:#00C; font-weight:bold } +.syntaxhl .regexp .content { color:#808 } +.syntaxhl .regexp .delimiter { color:#404 } +.syntaxhl .regexp .modifier { color:#C2C } +.syntaxhl .regexp { background-color:hsla(300,100%,50%,0.06); } +.syntaxhl .reserved { color:#080; font-weight:bold } +.syntaxhl .shell .content { color:#2B2 } +.syntaxhl .shell .delimiter { color:#161 } +.syntaxhl .shell { background-color:hsla(120,100%,50%,0.06); } +.syntaxhl .string .char { color: #b0b } +.syntaxhl .string .content { color: #D20 } +.syntaxhl .string .delimiter { color: #710 } +.syntaxhl .string .modifier { color: #E40 } +.syntaxhl .string { background-color:hsla(0,100%,50%,0.05); } +.syntaxhl .symbol .content { color:#A60 } +.syntaxhl .symbol .delimiter { color:#630 } +.syntaxhl .symbol { color:#A60 } +.syntaxhl .tag { color:#070 } +.syntaxhl .type { color:#339; font-weight:bold } +.syntaxhl .value { color: #088; } +.syntaxhl .variable { color:#037 } -.syntaxhl .ch { color:#04D } -.syntaxhl .ch .k { color:#04D } -.syntaxhl .ch .dl { color:#039 } - -.syntaxhl .cl { color:#B06; font-weight:bold } -.syntaxhl .cm { color:#A08; font-weight:bold } -.syntaxhl .co { color:#036; font-weight:bold } -.syntaxhl .cr { color:#0A0 } -.syntaxhl .cv { color:#369 } -.syntaxhl .de { color:#B0B; } -.syntaxhl .df { color:#099; font-weight:bold } -.syntaxhl .di { color:#088; font-weight:bold } -.syntaxhl .dl { color:black } -.syntaxhl .do { color:#970 } -.syntaxhl .dt { color:#34b } -.syntaxhl .ds { color:#D42; font-weight:bold } -.syntaxhl .e { color:#666; font-weight:bold } -.syntaxhl .en { color:#800; font-weight:bold } -.syntaxhl .er { color:#F00; background-color:#FAA } -.syntaxhl .ex { color:#C00; font-weight:bold } -.syntaxhl .fl { color:#60E; font-weight:bold } -.syntaxhl .fu { color:#06B; font-weight:bold } -.syntaxhl .gv { color:#d70; font-weight:bold } -.syntaxhl .hx { color:#058; font-weight:bold } -.syntaxhl .i { color:#00D; font-weight:bold } -.syntaxhl .ic { color:#B44; font-weight:bold } - -.syntaxhl .il { background: #ddd; color: black } -.syntaxhl .il .il { background: #ccc } -.syntaxhl .il .il .il { background: #bbb } -.syntaxhl .il .idl { background: #ddd; font-weight: bold; color: #666 } -.syntaxhl .idl { background-color: #bbb; font-weight: bold; color: #666; } - -.syntaxhl .im { color:#f00; } -.syntaxhl .in { color:#B2B; font-weight:bold } -.syntaxhl .iv { color:#33B } -.syntaxhl .la { color:#970; font-weight:bold } -.syntaxhl .lv { color:#963 } -.syntaxhl .oc { color:#40E; font-weight:bold } -.syntaxhl .of { color:#000; font-weight:bold } -.syntaxhl .op { } -.syntaxhl .pc { color:#038; font-weight:bold } -.syntaxhl .pd { color:#369; font-weight:bold } -.syntaxhl .pp { color:#579; } -.syntaxhl .ps { color:#00C; font-weight:bold } -.syntaxhl .pt { color:#074; font-weight:bold } -.syntaxhl .r, .kw { color:#080; font-weight:bold } - -.syntaxhl .ke { color: #808; } -.syntaxhl .ke .dl { color: #606; } -.syntaxhl .ke .ch { color: #80f; } -.syntaxhl .vl { color: #088; } - -.syntaxhl .rx { background-color:#fff0ff } -.syntaxhl .rx .k { color:#808 } -.syntaxhl .rx .dl { color:#404 } -.syntaxhl .rx .mod { color:#C2C } -.syntaxhl .rx .fu { color:#404; font-weight: bold } - -.syntaxhl .s { background-color:#fff0f0; color: #D20; } -.syntaxhl .s .s { background-color:#ffe0e0 } -.syntaxhl .s .s .s { background-color:#ffd0d0 } -.syntaxhl .s .k { } -.syntaxhl .s .ch { color: #b0b; } -.syntaxhl .s .dl { color: #710; } - -.syntaxhl .sh { background-color:#f0fff0; color:#2B2 } -.syntaxhl .sh .k { } -.syntaxhl .sh .dl { color:#161 } - -.syntaxhl .sy { color:#A60 } -.syntaxhl .sy .k { color:#A60 } -.syntaxhl .sy .dl { color:#630 } - -.syntaxhl .ta { color:#070 } -.syntaxhl .tf { color:#070; font-weight:bold } -.syntaxhl .ts { color:#D70; font-weight:bold } -.syntaxhl .ty { color:#339; font-weight:bold } -.syntaxhl .v { color:#036 } -.syntaxhl .xt { color:#444 } - -.syntaxhl .ins { background: #cfc; } -.syntaxhl .del { background: #fcc; } -.syntaxhl .chg { color: #aaf; background: #007; } +.syntaxhl .insert { background: hsla(120,100%,50%,0.12) } +.syntaxhl .delete { background: hsla(0,100%,50%,0.12) } +.syntaxhl .change { color: #bbf; background: #007; } .syntaxhl .head { color: #f8f; background: #505 } +.syntaxhl .head .filename { color: white; } -.syntaxhl .ins .ins { color: #080; font-weight:bold } -.syntaxhl .del .del { color: #800; font-weight:bold } -.syntaxhl .chg .chg { color: #66f; } -.syntaxhl .head .head { color: #f4f; } +.syntaxhl .delete .eyecatcher { background-color: hsla(0,100%,50%,0.2); border: 1px solid hsla(0,100%,45%,0.5); margin: -1px; border-bottom: none; border-top-left-radius: 5px; border-top-right-radius: 5px; } +.syntaxhl .insert .eyecatcher { background-color: hsla(120,100%,50%,0.2); border: 1px solid hsla(120,100%,25%,0.5); margin: -1px; border-top: none; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } + +.syntaxhl .insert .insert { color: #0c0; background:transparent; font-weight:bold } +.syntaxhl .delete .delete { color: #c00; background:transparent; font-weight:bold } +.syntaxhl .change .change { color: #88f } +.syntaxhl .head .head { color: #f4f } diff --git a/public/stylesheets/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png b/public/stylesheets/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 00000000..5b5dab2a Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/public/stylesheets/smoothness/images/ui-bg_flat_75_ffffff_40x100.png b/public/stylesheets/smoothness/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 00000000..ac8b229a Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_flat_75_ffffff_40x100.png differ diff --git a/public/stylesheets/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png b/public/stylesheets/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 00000000..ad3d6346 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png differ diff --git a/public/stylesheets/smoothness/images/ui-bg_glass_65_ffffff_1x400.png b/public/stylesheets/smoothness/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 00000000..42ccba26 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/public/stylesheets/smoothness/images/ui-bg_glass_75_dadada_1x400.png b/public/stylesheets/smoothness/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 00000000..5a46b47c Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_glass_75_dadada_1x400.png differ diff --git a/public/stylesheets/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png b/public/stylesheets/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 00000000..86c2baa6 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png differ diff --git a/public/stylesheets/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png b/public/stylesheets/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 00000000..4443fdc1 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png differ diff --git a/public/stylesheets/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/public/stylesheets/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 00000000..7c9fa6c6 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png differ diff --git a/public/stylesheets/smoothness/images/ui-icons_222222_256x240.png b/public/stylesheets/smoothness/images/ui-icons_222222_256x240.png new file mode 100644 index 00000000..b273ff11 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-icons_222222_256x240.png differ diff --git a/public/stylesheets/smoothness/images/ui-icons_2e83ff_256x240.png b/public/stylesheets/smoothness/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 00000000..09d1cdc8 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-icons_2e83ff_256x240.png differ diff --git a/public/stylesheets/smoothness/images/ui-icons_454545_256x240.png b/public/stylesheets/smoothness/images/ui-icons_454545_256x240.png new file mode 100644 index 00000000..59bd45b9 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-icons_454545_256x240.png differ diff --git a/public/stylesheets/smoothness/images/ui-icons_888888_256x240.png b/public/stylesheets/smoothness/images/ui-icons_888888_256x240.png new file mode 100644 index 00000000..6d02426c Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-icons_888888_256x240.png differ diff --git a/public/stylesheets/smoothness/images/ui-icons_cd0a0a_256x240.png b/public/stylesheets/smoothness/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 00000000..2ab019b7 Binary files /dev/null and b/public/stylesheets/smoothness/images/ui-icons_cd0a0a_256x240.png differ diff --git a/public/stylesheets/smoothness/jquery-ui.css b/public/stylesheets/smoothness/jquery-ui.css new file mode 100644 index 00000000..0f1a7e77 --- /dev/null +++ b/public/stylesheets/smoothness/jquery-ui.css @@ -0,0 +1,568 @@ +/* + * jQuery UI CSS Framework 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } +.ui-widget-content a { color: #222222; } +.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; } +.ui-widget-header a { color: #222222; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* + * jQuery UI Resizable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; } +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* + * jQuery UI Selectable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectable#theming + */ +.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } +/* + * jQuery UI Accordion 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion#theming + */ +/* IE/Win - Fix animation bug - #4615 */ +.ui-accordion { width: 100%; } +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; } +/* + * jQuery UI Autocomplete 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { position: absolute; cursor: default; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu 1.8.16 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} +/* + * jQuery UI Button 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button#theming + */ +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button { padding: .4em 1em; } + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ +/* + * jQuery UI Dialog 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } +.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } +.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* + * jQuery UI Slider 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* + * jQuery UI Tabs 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } +/* + * jQuery UI Datepicker 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* + * jQuery UI Progressbar 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file diff --git a/public/themes/alternate/stylesheets/application.css b/public/themes/alternate/stylesheets/application.css deleted file mode 100644 index ef2c8cf9..00000000 --- a/public/themes/alternate/stylesheets/application.css +++ /dev/null @@ -1,70 +0,0 @@ -@import url(../../../stylesheets/application.css); - -body, #wrapper { background-color:#EEEEEE; } -#header, #top-menu { margin: 0px 10px 0px 11px; } -#main { background: #EEEEEE; margin: 8px 10px 0px 10px; } -#content, #main.nosidebar #content { background: #fff; border-right: 1px solid #bbb; border-bottom: 1px solid #bbb; border-left: 1px solid #d7d7d7; border-top: 1px solid #d7d7d7; } -#footer { background-color:#EEEEEE; border: 0px; } - -/* Headers */ -h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 {border-bottom: 0px;} - -/* Menu */ -#main-menu li a { background-color: #507AAA; font-weight: bold;} -#main-menu li a:hover { background: #507AAA; text-decoration: underline; } -#main-menu li a.selected, #main-menu li a.selected:hover { background-color:#EEEEEE; } - -/* Tables */ -table.list tbody td, table.list tbody tr:hover td { border: solid 1px #d7d7d7; } -table.list thead th { - border-width: 1px; - border-style: solid; - border-top-color: #d7d7d7; - border-right-color: #d7d7d7; - border-left-color: #d7d7d7; - border-bottom-color: #999999; -} - -/* Issues grid styles by priorities (provided by Wynn Netherland) */ -table.list tr.issue a { color: #666; } - -tr.odd.priority-5, table.list tbody tr.odd.priority-5:hover { color: #900; font-weight: bold; } -tr.odd.priority-5 { background: #ffc4c4; } -tr.even.priority-5, table.list tbody tr.even.priority-5:hover { color: #900; font-weight: bold; } -tr.even.priority-5 { background: #ffd4d4; } -tr.priority-5 a, tr.priority-5:hover a { color: #900; } -tr.odd.priority-5 td, tr.even.priority-5 td { border-color: #ffb4b4; } - -tr.odd.priority-4, table.list tbody tr.odd.priority-4:hover { color: #900; } -tr.odd.priority-4 { background: #ffc4c4; } -tr.even.priority-4, table.list tbody tr.even.priority-4:hover { color: #900; } -tr.even.priority-4 { background: #ffd4d4; } -tr.priority-4 a { color: #900; } -tr.odd.priority-4 td, tr.even.priority-4 td { border-color: #ffb4b4; } - -tr.odd.priority-3, table.list tbody tr.odd.priority-3:hover { color: #900; } -tr.odd.priority-3 { background: #fee; } -tr.even.priority-3, table.list tbody tr.even.priority-3:hover { color: #900; } -tr.even.priority-3 { background: #fff2f2; } -tr.priority-3 a { color: #900; } -tr.odd.priority-3 td, tr.even.priority-3 td { border-color: #fcc; } - -tr.odd.priority-1, table.list tbody tr.odd.priority-1:hover { color: #559; } -tr.odd.priority-1 { background: #eaf7ff; } -tr.even.priority-1, table.list tbody tr.even.priority-1:hover { color: #559; } -tr.even.priority-1 { background: #f2faff; } -tr.priority-1 a { color: #559; } -tr.odd.priority-1 td, tr.even.priority-1 td { border-color: #add7f3; } - -/* Buttons */ -input[type="button"], input[type="submit"], input[type="reset"] { background-color: #f2f2f2; color: #222222; border: 1px outset #cccccc; } -input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover { background-color: #ccccbb; } - -/* Fields */ -input[type="text"], input[type="password"], textarea, select { padding: 2px; border: 1px solid #d7d7d7; } -input[type="text"], input[type="password"] { padding: 3px; } -input[type="text"]:focus, input[type="password"]:focus, textarea:focus, select:focus { border: 1px solid #888866; } -option { border-bottom: 1px dotted #d7d7d7; } - -/* Misc */ -.box { background-color: #fcfcfc; } diff --git a/public/themes/chiliproject/CHANGELOG b/public/themes/chiliproject/CHANGELOG deleted file mode 100644 index 8ae9a965..00000000 --- a/public/themes/chiliproject/CHANGELOG +++ /dev/null @@ -1,52 +0,0 @@ -0.2.0 (22-05-2009) - * Defect #236: Icons (ticket__*.png) aren't referenced correctly - * Feature #243: Add support for Redmine Graphs plugin - * Feature #261: Add support for Redmine Bugcloud plugin - * Feature #262: Add support for Redmine Scrumdashboard plugin - * Feature #263: Add support for Redmine Code Review plugin - * Enhancement #230: Other font-family for header - * Enhancement #233: Change used Fugue-icon for Roadmap project-menu item - * Enhancement #255: Make image-references relative for sub-URI compatibility - * Enhancement #256: Fix upcoming change in supported Budget-plugin - * Enhancement #258: Fix upcoming change in supported Invoices-plugin - * Enhancement #259: Fix upcoming change in supported Scores-plugin - * Enhancement #260: Fix upcoming change in supported Simple-CI plugin - * Enhancement #267: Fix upcoming change in supported Todos plugin - * Task-HITDM #235: Update vendor source of the fugue icon-set to current release - * Task-HITDM #257: Clean-up and Fix project-menu declarations of supported plugins - * Task-HITDM #266: Remove unused images while creating the 0.2-release branch - -0.1.0 (21-03-2009) - * Defect #186: Project-menu icons aren't rendered after Redmine core rev2022 - * Defect #187: Right-aligned columns on version-page have a white background instead of transparent - * Defect #193: There exists some cross-browser compatibility-errors on Presto-engine - * Defect #195: Some faulty references to non-existing images exists due to typos - * Defect #196: Fix overflow of the content - * Defect #199: Issue-journals should be displayed *behind* the related-revisions block, not below it - * Defect #207: Several PNG-images are rendered with a white background, disrupting the nice gray-gradient - * Defect #208: Fix the rendering of the note-icon when inline-editing an issue-journal - * Defect #211: After r40 unordered lists inside the issue-journal comment are smaller-sized too - * Defect #212: Issue-journal comment note-icons rendered for all paragraphs instead of the first only - * Defect #213: After r37 the context-menu item-links are rendered as bold; should be normal - * Defect #228: After r6 selected project-menu tabs aren't highlighted anymore - * Feature #210: Add specific declarations for third-party plugins - * Feature #229: Add KHTML-support for rounded-borders on the first & last project-menu items - * Enhancement #190: Improve link-visibility globally - * Enhancement #191: Refactor the styling of the issue-journals - * Enhancement #192: Modify (overall) font-sizes to make the UI more consistent - * Enhancement #194: Sidebar should have a bit more width - * Enhancement #198: Overrule bordered-table styles imported from the Alternate-theme - * Enhancement #214: Project-menu overflows the right border when lots of modules are enabled on a project - * Enhancement #223: Improve icon-declarations - * Task-HITDM #188: Remove project-body headers introduced by Squeejee based on a core-hack - * Task-HITDM #189: Undue the theme from dependency on (import of) the Alternate-theme - * Task-HITDM #200: Remove unused images from release - * Task-HITDM #201: Clean CSS-styles coding - * Task-HITDM #205: 0.1.0 Release QA - * Task-HITDM #209: CSS-Code Cleanup (round 2) - * Task-HITDM #215: Update vendor source of the fugue icon-set to 1.4.6 - * Task-HITDM #224: Update vendor source of the fugue icon-set to 1.4.7 - * Task-HITDM #226: Fix issues found in 0.1.0 Release QA-Sprint/2 (#205) - -0.0.0 - Initial source by Wynn Netherland as of 20-11-2008 diff --git a/public/themes/chiliproject/GPL.txt b/public/themes/chiliproject/GPL.txt deleted file mode 100644 index 82fa1daa..00000000 --- a/public/themes/chiliproject/GPL.txt +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/public/themes/chiliproject/README.rdoc b/public/themes/chiliproject/README.rdoc deleted file mode 100644 index bfb8a710..00000000 --- a/public/themes/chiliproject/README.rdoc +++ /dev/null @@ -1,8 +0,0 @@ -= ChiliProject.org theme - -This theme is based on the Redmine Squeejee theme. It is available bundled with ChiliProject as well as on Github. - -* ChiliProject - https://www.chiliproject.org -* Github - https://github.com/edavis10/squeejee_theme/tree/chiliproject.org - -See README.redmine for the official Squeejee theme readme. diff --git a/public/themes/chiliproject/README.redmine b/public/themes/chiliproject/README.redmine deleted file mode 100644 index 6b2dbb4c..00000000 --- a/public/themes/chiliproject/README.redmine +++ /dev/null @@ -1,100 +0,0 @@ -h1. Squeejee theme - -A theme for Redmine which is based on a dark but "shiny" color-scheme and which includes a subtle (re)styled project-menu. - -h2. Packager, contributor and maintainer - -* Mischa The Evil - -h2. Initial author - -* "Wynn Netherland":http://www.squeejee.com/team.html#wynn_netherland - -h2. History - -For the complete history of the theme's creation you could see "this forum-thread":http://www.redmine.org/boards/1/topics/2736. - -h2. Features - -The Squeejee© theme is an updated, packaged release of Wynn Netherland's work for "Squeejee's":http://www.squeejee.com internal Redmine instance. It initially was a heavily modified derivation of the [[ThemeAlternate|alternate theme]] which required Redmine core hacks but is made stand-alone and working without core hacks (by using the core's support for project-menu item styling (r2059)) for this release. - -It's looks can be best described as a dark theme with gradient backgrounds, dark-gray and bold links, but all with a "shiny" and "friendly" touch. It reintroduces the project-menu item-icons with the extension that styling of third-party plugins (including selected icons) is supported too. -Also the overflow to a second line of the project-menu, when using a lot of plugins with menu-items, is handled without disturbing the surrounding layout and elements. - -The colour-scheme basically consists of three basic colours: black, grey and white. - -It includes: -* issue-colouring in the issuelist, based on default priority-enumerations for issues -* more sophistically styled "tabs", "tables", etc. -* (project-menu item styling) support for (lots of) third-party plugins - -h2. Screenshots - -[ ... see online version of this page ... ] - -h2. Compatibility - -h3. Redmine compatibility - -The theme is compatible with the Redmine 0.8-stable branch (thus including releases: 0.8.0, 0.8.1, 0.8.2 and 0.8.3) and the trunk. - -h3. Browser compatibility - -* This theme is fully compatible with the current, big-four of browser-engines: - * Gecko (Mozilla) - * Trident (Internet Explorer 8) - * Presto (Opera) - * WebKit (Safari/Chrome) - -* This theme is for about 95% compatible with the following browser-engine: - * Trident (Internet Explorer 7) - -Though, tiny differences can occur across different browsers. - -h2. Obtaining the theme - -The theme can be downloaded as a packaged release from: -* this page; the archive is attached -* this MediaFire-mirror: http://www.mediafire.com/evildev -* the upcoming website http://www.evil-dev.net - -h2. Installation - -Follow the Redmine theme installation steps at: http://www.redmine.org/wiki/redmine/Themes#Installing-a-theme. - -h2. Upgrade - -1. Download the latest archive file from the available sources (see "Obtaining the theme") -2. Backup the currently deployed squeejee theme (in _"../public/themes"_: @mv squeejee squeejee-backup@) -3. Unzip the downloaded file to your Redmine into the theme-directory _"../public/themes"_ -4. Restart your Redmine - -h2. Uninstall - -1. Remove the directory "squeejee" from the theme-directory _"../public/themes"_ -2. Restart Redmine - -h2. Changelog - -For the complete changelog see the @CHANGELOG@-file in the Squeejee theme directory. - -h2. Credits - -Thanks goes out to the following people: - -* Wynn Netherland, Squeejee (http://www.squeejee.com) -** Initial author (designer and coder) of the draft of this theme which can be found here: http://github.com/squeejee/redmine/tree/master/public/themes/squeejee -* Jean-Philippe Lang, Project-leader of Redmine (http://www.redmine.org) -** For creating and maintaining the Redmine system... - -h2. Licensing - -This theme is open-source and licensed under the "GNU General Public License v2":http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (GPL). Certain icons are part of the Fugue icon-set (http://www.pinvoke.com) which is released under the "Creative Commons Attribution 3.0 Unported":http://creativecommons.org/licenses/by/3.0 license. - -* (C)2009, Mischa The Evil (http://www.evil-dev.net) -* (C)2008, Wynn Netherland (http://www.squeejee.com) - -h2. Support - -If you need help, would like to report a bug or request a new feature you can contact the -maintainer via mail (mischa_the_evil [AT] hotmail [DOT] com) or at his (upcoming) website: http://www.evil-dev.net. diff --git a/public/themes/chiliproject/VERSION b/public/themes/chiliproject/VERSION deleted file mode 100644 index f8677859..00000000 --- a/public/themes/chiliproject/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.2.0 ~ 22-05-2009 diff --git a/public/themes/chiliproject/images/bottom_shine.png b/public/themes/chiliproject/images/bottom_shine.png deleted file mode 100644 index f6cf4ac0..00000000 Binary files a/public/themes/chiliproject/images/bottom_shine.png and /dev/null differ diff --git a/public/themes/chiliproject/images/map.png b/public/themes/chiliproject/images/map.png deleted file mode 100644 index df0f725f..00000000 Binary files a/public/themes/chiliproject/images/map.png and /dev/null differ diff --git a/public/themes/chiliproject/images/middle_shine.png b/public/themes/chiliproject/images/middle_shine.png deleted file mode 100644 index f9096314..00000000 Binary files a/public/themes/chiliproject/images/middle_shine.png and /dev/null differ diff --git a/public/themes/chiliproject/images/shadow_top.png b/public/themes/chiliproject/images/shadow_top.png deleted file mode 100644 index 53fc5f5b..00000000 Binary files a/public/themes/chiliproject/images/shadow_top.png and /dev/null differ diff --git a/public/themes/chiliproject/stylesheets/application.css b/public/themes/chiliproject/stylesheets/application.css deleted file mode 100644 index 6651b1c0..00000000 --- a/public/themes/chiliproject/stylesheets/application.css +++ /dev/null @@ -1,959 +0,0 @@ -/* Load the default Redmine stylesheet */ -@import url(../../../stylesheets/application.css); - -body { - font-size: 81%; - font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; - background: #333 url(../images/bottom_shine.png) bottom left repeat-x; -} - -/* -* Links -*/ -a, a:link, a:visited { - color: #8c8c8c; - text-decoration: none; - font-weight: bold; -} - -a:hover { - color: #b3b3b3; - text-decoration: underline; -} - -/* -* Layout -*/ -#wrapper, -#main, -#footer { - background: none; -} - -#main { - margin: 0; -} - -#content { - margin: 1em 1.4em 1em 1em; - width: 77%; - overflow: auto; - background: #fff url(../images/shadow_top.png) top left repeat-x; - border-top: solid 1px #fff; - border-right: solid 1px #fff; - -webkit-box-shadow: 5px 5px 10px rgba(0,0,0,.8); -} - -/* -* Top-menu -*/ -#top-menu { - background: #000 url(../images/bottom_shine.png) bottom left repeat-x; -} - -#top-menu { - padding: 1em 1em 0em 1em; - color: #999; - font-family: Verdana, sans-serif; -} - -#top-menu #loggedas { - color: #ccc; -} - -#top-menu a { - font-weight: bold; - color: #999; -} - -#top-menu a:hover { - text-decoration: none; - color: #fff; -} - -/* -* Header -*/ -#header { - background: #000 url(../images/bottom_shine.png) bottom left repeat-x; - padding: 0.5em 1em 2.3em 1em; -} - -#header > h1 { - font-family: Verdana, sans-serif; -} - -/* -* Project-menu -*/ -/* Prevent the project-menu to overflow into right, instead continue on a new line */ -#main-menu { - right: 6px; - margin-right: 0; -} - -/* Project-menu link containers (ul > li == tab) */ -#main-menu li { - background: #111 url(../images/bottom_shine.png) 0px -40px repeat-x; - border: solid 1px #333; - border-width: 1px 1px 0px 0px; - font-size: 1.00em; - font-family: "Myriad Pro", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; - margin: 0; -} - -/* Project-menu links (ul > li > a == textlink) */ -#main-menu li a, -#main-menu li a.selected, -#main-menu li a:hover { - text-decoration: none; - line-height: 20px; - text-shadow: 1px 1px 1px rgba(0,0,0,.8); -} - - -#main-menu li a, -#main-menu li a:hover { - background: none; - color: #aaa; - padding: 5px 10px 5px 10px; - font-weight: normal; -} - -#main-menu li a.selected, -#main-menu li a.selected:hover { - background: #222 url(../images/bottom_shine.png) 0px -36px repeat-x; - border-color: #444; - color: #d5d5d5; - font-weight: bold; -} - -#main-menu li:hover { - background: #333 url(../images/bottom_shine.png) 0px -40px repeat-x; - border-color: #555; -} - -#main-menu li:hover a { - color: #fff; -} - -/* Project-menu first/last tab roundings (for KHTML [Konqueror], Gecko [Mozilla] and WebKit [Safari/Chrome]) */ -#main-menu li:first-child { - -khtml-border-radius-topleft: 5px; - -moz-border-radius-topleft: 5px; - -webkit-border-top-left-radius: 5px; -} - -#main-menu li:last-child { - -khtml-border-radius-topright: 5px; - -moz-border-radius-topright: 5px; - -webkit-border-top-right-radius: 5px; -} - -/* Redmine core project-menu links */ -#main-menu li a.overview, -#main-menu li a.overview:hover { - background: url(../images/fugue/document-text-image.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.activity, -#main-menu li a.activity:hover { - background: url(../images/fugue/lightning.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.roadmap, -#main-menu li a.roadmap:hover { - background: url(../images/fugue/map-pin.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.issues, -#main-menu li a.issues:hover { - background: url(../images/fugue/ticket.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.new-issue, -#main-menu li a.new-issue:hover { - background: url(../images/fugue/ticket--plus.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.news, -#main-menu li a.news:hover { - background: url(../images/fugue/newspaper.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.documents, -#main-menu li a.documents:hover { - background: url(../images/fugue/documents-text.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.wiki, -#main-menu li a.wiki:hover { - background: url(../images/fugue/document-horizontal-text.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.boards, -#main-menu li a.boards:hover { - background: url(../images/fugue/balloons.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.files, -#main-menu li a.files:hover { - background: url(../images/fugue/document-zipper.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.repository, -#main-menu li a.repository:hover { - background: url(../images/fugue/safe.png) 6px center no-repeat; - padding-left: 26px; -} - -#main-menu li a.settings, -#main-menu li a.settings:hover { - background: url(../images/fugue/equalizer.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Third-party project-menu links */ - -/************************************************************************* -THIRD-PARTY DECLARATIONS FOR PROJECT-MENU LINKS - START -*************************************************************************/ -/* Budget plugin */ - /* > 0.2.0 */ -#main-menu li a.deliverables, -#main-menu li a.deliverables:hover, - /* <= 0.2.0 */ -#main-menu li a.budget, -#main-menu li a.budget:hover { - background: url(../images/fugue/money--pencil.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Bugcloud plugin */ -#main-menu li a.bugcloud, -#main-menu li a.bugcloud:hover { - background: url(../images/fugue/tags-label.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Burndowns plugin */ -#main-menu li a.burndown, -#main-menu li a.burndown:hover { - background: url(../images/fugue/burn.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Charts plugin */ -#main-menu li a.charts, -#main-menu li a.charts:hover { - background: url(../images/fugue/monitor.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Code Review plugin */ -#main-menu li a.code-review, -#main-menu li a.code-review:hover { - background: url(../images/fugue/ruler--pencil.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Customer plugin */ -#main-menu li a.customers, -#main-menu li a.customers:hover { - background: url(../images/fugue/user-business.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Embedded plugin */ -#main-menu li a.embedded, -#main-menu li a.embedded:hover { - background: url(../images/fugue/layout-select-content.png) 6px center no-repeat; - padding-left: 26px; -} - -/* EzFAQ plugin */ -#main-menu li a.ezfaq, -#main-menu li a.ezfaq:hover { - background: url(../images/fugue/question-balloon.png) 6px center no-repeat; - padding-left: 26px; -} - -/* EzLibrarian plugin */ - /* > 0.0.2 (AKA EzLibrarian) */ -#main-menu li a.treasures, -#main-menu li a.treasures:hover { - background: url(../images/fugue/trophy.png) 6px center no-repeat; - padding-left: 26px; -} - /* <= 0.0.2 (AKA EzBookshelf) */ -#main-menu li a.books, -#main-menu li a.books:hover { - background: url(../images/fugue/books-stack.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Google Calendar plugin */ -#main-menu li a.google-calendar, -#main-menu li a.google-calendar:hover { - background: url(../images/fugue/calendar-month.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Invoice plugin */ - /* > 0.1.0 */ -#main-menu li a.invoice, -#main-menu li a.invoice:hover, - /* <= 0.1.0 */ -#main-menu li a.Invoices, -#main-menu li a.Invoices:hover { - background: url(../images/fugue/notebooks--pencil.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Schedules plugin */ -#main-menu li a.schedules, -#main-menu li a.schedules:hover { - background: url(../images/fugue/report--exclamation.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Scores plugin */ - /* > 0.0.1 */ -#main-menu li a.scores, -#main-menu li a.scores:hover, - /* <= 0.0.1 */ -#main-menu li a.Scores, -#main-menu li a.Scores:hover { - background: url(../images/fugue/ui-progress-bar.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Scrum plugin */ -#main-menu li a.scrum, -#main-menu li a.scrum:hover { - background: url(../images/fugue/projection-screen--pencil.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Scrumdashboard plugin */ -#main-menu li a.dashboard, -#main-menu li a.dashboard:hover { - background: url(../images/fugue/dashboard--pencil.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Simple CI plugin */ - /* > 1.0 */ -#main-menu li a.simple-ci, -#main-menu li a.simple-ci:hover, - /* <= 1.0 */ -#main-menu li a.Integration, -#main-menu li a.Integration:hover { - background: url(../images/fugue/pill--exclamation.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Status Updates plugin */ - /* Initial release by Brian Terlson and fork by Eric Davis*/ -#main-menu li a.Status.Updates, -#main-menu li a.Status.Updates:hover, - /* Fork by Joe Naha */ -#main-menu li a.statuses, -#main-menu li a.statuses:hover { - background: url(../images/fugue/tick-shield.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Tab plugin */ -#main-menu li a.tab, -#main-menu li a.tab:hover { - background: url(../images/fugue/layout-2.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Taskboard plugin */ -#main-menu li a.task-board, -#main-menu li a.task-board:hover { - background: url(../images/fugue/dashboard--pencil.png) 6px center no-repeat; - padding-left: 26px; -} - -/* Todo-lists plugin */ - /* > 0.0.3.4 */ -#main-menu li a.todos, -#main-menu li a.todos:hover, - /* <= 0.0.3.4 */ -#main-menu li a.todo-lists, -#main-menu li a.todo-lists:hover { - background: url(../images/fugue/hammer--arrow.png) 6px center no-repeat; - padding-left: 26px; -} -/************************************************************************* -THIRD-PARTY DECLARATIONS FOR PROJECT-MENU LINKS - END -*************************************************************************/ - -/* Project-menu tab highlight when link == the selected page (has the class .selected) */ -#main-menu li a.selected { - background-color: #333; -} - -/* -* Headings -*/ -h1, h2, h3, h4, h5 { - font-family: "Myriad Pro", "Lucida Grande", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; -} - -h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { - border-bottom: none; -} - -/* -* Footer -*/ -#footer { - border: none; -} - -#footer a { - color: #ccc; - font-weight: normal; -} - -#footer a:hover { - color: #fff; - text-decoration: none; -} - -/* -* Sidebar -*/ -#sidebar h1, #sidebar h2, #sidebar h3, #sidebar h4, #sidebar h5 { - color: #efefef; - text-shadow: 1px 1px 1px rgba(0,0,0,.8); -} - -#sidebar { - color: #ccc; - width: 19%; - text-shadow: 1px 1px 1px rgba(0,0,0,.6); -} - -#sidebar a { - color: #aaa; - font-weight: normal; -} - -/* -* Generic Tables -*/ -/* Table headers */ -table.list thead th { - background: #ccc url(../images/shadow_top.png) left center repeat-x; - font-size: 0.94em; - border-color: #999; - border-color: #d5d5d5 #d5d5d5 #aaa; - border-style: solid; - border-width: 1px; -} - -/* Table header links */ -table.list thead th a, -table.list thead th a:hover { - color: #444; - text-shadow: 1px 1px 1px rgba(0,0,0,.3); - border: none; -} - -table.list thead th a:hover { - color: #ddd; -} - -/* Table data (cells) */ -table.list td { - background-image: url(../images/shadow_top.png); - background-repeat: repeat-x; - background-position: 0px -65px; - vertical-align: middle; - font-size: 0.94em; -} - -/* -* Issue-List Tables -*/ -/* Table-row links */ -table.list tr.issue a { - color: #666; -} - -table.list tr.issue a:hover { - color: #999; - border-bottom: dotted 1px #999; - text-decoration: none; -} - -/* Table-row colouring by priority (styles provided by Wynn Netherland) */ -tr.odd.priority-5, table.list tbody tr.odd.priority-5:hover { color: #900; font-weight: bold; } -tr.odd.priority-5 { background: #ffc4c4; } -tr.even.priority-5, table.list tbody tr.even.priority-5:hover { color: #900; font-weight: bold; } -tr.even.priority-5 { background: #ffd4d4; } -tr.priority-5 a, tr.priority-5:hover a { color: #900; } -tr.odd.priority-5 td, tr.even.priority-5 td { border-color: #ffb4b4; } - -tr.odd.priority-4, table.list tbody tr.odd.priority-4:hover { color: #900; } -tr.odd.priority-4 { background: #ffc4c4; } -tr.even.priority-4, table.list tbody tr.even.priority-4:hover { color: #900; } -tr.even.priority-4 { background: #ffd4d4; } -tr.priority-4 a { color: #900; } -tr.odd.priority-4 td, tr.even.priority-4 td { border-color: #ffb4b4; } - -tr.odd.priority-3, table.list tbody tr.odd.priority-3:hover { color: #900; } -tr.odd.priority-3 { background: #fee; } -tr.even.priority-3, table.list tbody tr.even.priority-3:hover { color: #900; } -tr.even.priority-3 { background: #fff2f2; } -tr.priority-3 a { color: #900; } -tr.odd.priority-3 td, tr.even.priority-3 td { border-color: #fcc; } - -tr.odd.priority-1, table.list tbody tr.odd.priority-1:hover { color: #559; } -tr.odd.priority-1 { background: #eaf7ff; } -tr.even.priority-1, table.list tbody tr.even.priority-1:hover { color: #559; } -tr.even.priority-1 { background: #f2faff; } -tr.priority-1 a { color: #559; } -tr.odd.priority-1 td, tr.even.priority-1 td { border-color: #add7f3; } - -/* Table-row link-colouring by priority (respecting the table-row colouring styles above) */ -table.list tr.priority-5 a, -table.list tr.priority-4 a, -table.list tr.priority-3 a { color: #900; } - -table.list tr.priority-1 a { color: #559; } - -/* -* Progressbars -*/ -/* Generic progressbars */ -table.progress td.todo, -table.progress td.done, -table.progress td.closed { - background: #666 url(../images/middle_shine.png) left center repeat-x; - height: 25px; - border: none; -} - -table.progress td.todo { - background: #fff; -} - -table.progress td.done { - background-color: #37b3ff; -} - -table.progress td.closed { - background-color: #2187C6; -} - -/* Issuelist progressbars */ -table.list table.progress td { - height: 15px; -} - -p.pourcent { - font-size: 1.25em; -} - -/* -* Icons -*/ -.icon { - background-position: left top; - background-repeat: no-repeat; - padding-left: 20px; - padding-top: 2px; - padding-bottom: 3px; - line-height: 16px; - margin-left: 5px; -} - -.icon22 { - background-position: 0% 40%; - background-repeat: no-repeat; - padding-left: 26px; - line-height: 22px; - vertical-align: middle; -} - -/* Icons replaced by Fugue icons */ -.icon-add { background-image: url(../images/fugue/plus-small.png); } -.icon-edit { background-image: url(../images/fugue/pencil-small.png); } -.icon-copy { background-image: url(../images/fugue/documents.png); } -.icon-del { background-image: url(../images/fugue/ticket--minus.png); } -.icon-move { background-image: url(../images/fugue/ticket--arrow.png); } -.icon-save { background-image: url(../images/fugue/disk-black.png); } -.icon-cancel { background-image: url(../images/fugue/arrow.png); } - -.icon-time { background-image: url(../images/fugue/clock.png); } -.icon-time-add { background-image: url(../images/fugue/clock--plus.png); } - -.icon-fav { background-image: url(../images/fugue/star.png); } -.icon-fav-off { background-image: url(../images/fugue/star-empty.png); } - -/* Icons not replaced by Fugue icons -.icon-file { background-image: url(/images/file.png); } -.icon-folder { background-image: url(/images/folder.png); } -.open .icon-folder { background-image: url(/images/folder_open.png); } -.icon-package { background-image: url(/images/package.png); } -.icon-home { background-image: url(/images/home.png); } -.icon-user { background-image: url(/images/user.png); } -.icon-mypage { background-image: url(/images/user_page.png); } -.icon-admin { background-image: url(/images/admin.png); } -.icon-projects { background-image: url(/images/projects.png); } -.icon-help { background-image: url(/images/help.png); } -.icon-attachment { background-image: url(/images/attachment.png); } -.icon-index { background-image: url(/images/index.png); } -.icon-history { background-image: url(/images/history.png); } -.icon-stats { background-image: url(/images/stats.png); } -.icon-warning { background-image: url(/images/warning.png); } -.icon-reload { background-image: url(/images/reload.png); } -.icon-lock { background-image: url(/images/locked.png); } -.icon-unlock { background-image: url(/images/unlock.png); } -.icon-checked { background-image: url(/images/true.png); } -.icon-details { background-image: url(/images/zoom_in.png); } -.icon-report { background-image: url(/images/report.png); } -.icon-comment { background-image: url(/images/comment.png); } - -.icon22-projects { background-image: url(/images/22x22/projects.png); } -.icon22-users { background-image: url(/images/22x22/users.png); } -.icon22-tracker { background-image: url(/images/22x22/tracker.png); } -.icon22-role { background-image: url(/images/22x22/role.png); } -.icon22-workflow { background-image: url(/images/22x22/workflow.png); } -.icon22-options { background-image: url(/images/22x22/options.png); } -.icon22-notifications { background-image: url(/images/22x22/notifications.png); } -.icon22-authent { background-image: url(/images/22x22/authent.png); } -.icon22-info { background-image: url(/images/22x22/info.png); } -.icon22-comment { background-image: url(/images/22x22/comment.png); } -.icon22-package { background-image: url(/images/22x22/package.png); } -.icon22-settings { background-image: url(/images/22x22/settings.png); } -.icon22-plugin { background-image: url(/images/22x22/plugin.png); } -*/ - -/* -* Buttons -*/ -input[type="button"], input[type="submit"], input[type="reset"] { - background-color: #f2f2f2; - color: #222222; - border: 1px outset #cccccc; -} -input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover { - background-color: #ccccbb; -} - -/* -* Fields -*/ -input[type="text"], textarea, select { - padding: 2px; - border: 1px solid #d7d7d7; -} -input[type="text"] { - padding: 3px; -} -input[type="text"]:focus, textarea:focus, select:focus { - border: 1px solid #888866; -} - -/* -* Boxes -*/ -div.box { - border: none; - background: none; -} - -/* -* Quicksearch -*/ -div#quick-search a { - padding-left: 20px; - background: url(../images/fugue/magnifier-left.png) left center no-repeat; - font-weight: normal; -} - -/* -* Issue Journals -*/ -/* Issue-history (journal) UL for issue-field changes*/ -#history ul { - list-style: disc; - margin-top: 8px; - margin-left: 14px; - padding-left: 20px; - font-size: 94%; -} - -/* Issue-history comment UL font-size reset*/ -#history .wiki > ul { - font-size: inherit; /* back to 81% to workaround the 94% of #history ul */ -} - -/* Generic Issue-journals */ -#history .journal { - border-bottom: solid 1px #d5d5d5; - padding-bottom: 14px; -} - -/* Issue-journal paragraphs */ -#history .journal p { - margin-bottom: 8px; -} - -/* Issue-journals, first paragraph after .contextual, note-image insertion */ -#history .journal .contextual + p:before { /* Not on <= IE7 */ - vertical-align: -12%; - padding-right: 5px; - content: url(../images/fugue/sticky-note.png); -} - -/* Fixes for issue-journals when editing them inline */ -#history .journal > form > p { - padding-left: 0px; - background: none; -} - -/* -* Roadmap versions -*/ -div#version-summary { - background: transparent; -} - -/* -* Issue-report zoom-images -*/ -div.nosidebar#main > div#content > div.splitcontentleft > h3, -div.nosidebar#main > div#content > div.splitcontentright > h3 { - margin-top: 15px; -} - -/* -* Repository-browser -*/ -table#browser tr td.filename a { - color: #666; - font-weight: normal; -} - -table#browser tr td.filename a:hover { - color: #b3b3b3; -} - -/* -* Repository-statistics images -*/ -div.nosidebar#main > div#content embed { - display: block; - margin-top: 28px; - margin-left: auto; - margin-right: auto; -} - -/* -* Context-menu -*/ -/* Context-menu link font-weight ("a:hover {color:xxx;}" isn't settable...) */ -#context-menu a { - font-weight: normal; -} - -/************************************************************************* -THIRD-PARTY DECLARATIONS FOR NON PROJECT-MENU ITEMS - START -*************************************************************************/ -/* Burndowns plugin */ -div#main > div#content > p > img { - display: block; - margin-top: 28px; - margin-left: auto; - margin-right: auto; -} - -/* Charts plugin */ -div#main > div#content > div.splitcontentright > object { - margin-top: 28px; -} - -/* Google-Calendar & Tab plugin */ -div#main > div#content > iframe { - display: block; - margin-top: 28px; - margin-left: auto; - margin-right: auto; -} - -/* Graphs plugin */ -div#main > div#content > embed { - margin-top: 28px; - margin-bottom: 20px; -} - -div.nosidebar#main > div#content > fieldset#target_version_graph > embed { - margin-top: 0px; -} - -/* Taskboard plugin */ -#task_board tr > td { - height: 0.75em; -} -/************************************************************************* -THIRD-PARTY DECLARATIONS FOR NON PROJECT-MENU ITEMS - END -*************************************************************************/ - - -/************************************************************************* -Custom tweaks based on other themes -*************************************************************************/ - -body {background: white; } -#top-menu {background-color: white; color: black; } -#top-menu a {color: black; } -#top-menu a:hover {color: black; text-decoration: underline; text-shadow: 1px 1px 1px rgba(0,0,0,.5); } -#header {background: none; color: black; } -#header a {color: black; } -#top-menu #loggedas { color: black; } - -#content { background-image: none; border: 1px solid #000; -webkit-box-shadow: 2px 2px 4px rgba(0,0,0,.8); } - -#content > h2 {font-size: 3em; text-shadow: 1px 1px 1px rgba(0,0,0,.5); background-color: #EDF3FE; margin: -6px -10px 10px -15px; padding: 10px 20px; } /* Title of the content section */ -#main { background: white; } -#sidebar { padding-top: 20px; } /* lower the text below any content headers */ -#sidebar, #sidebar h1, #sidebar h2, #sidebar h3, #sidebar h4, #sidebar h5 { color: #666; text-shadow: none; } -#sidebar a { color: #000000; } - -#footer, #footer a { color: #AAAAAA;} -#footer a { color: #888888; } -#footer a:hover { color: #888888; text-decoration: underline; } - - - -/**** Project menus ****/ -/* Link color */ -#main-menu li a {color: #222222;} -#main-menu li a.selected {color: #111111;} -#main-menu li a:hover, #main-menu li a.selected:hover {color: #000000;} - -/* Background */ -#main-menu li {background-color: #FFFFFF; border-color: #DDDDDD; border-width: 1px 1px 1px 1px; } -#main-menu li:hover {background-color: #DDDDDD;} -#main-menu li a, #main-menu li a:hover {} -#main-menu li a.selected, #main-menu li a.selected:hover {background-color: #EEEEEE;} - -#main-menu li a, #main-menu li a.selected, #main-menu li a:hover {text-shadow: none;} /* Remove drop shadow */ - -#top-menu { border-bottom: 2px solid #FF4719; } - -/* Project-menu first/last tab bottom roundings (for KHTML [Konqueror], Gecko [Mozilla] and WebKit [Safari/Chrome]) */ -#main-menu li:first-child { - -khtml-border-radius-bottomleft: 5px; - -moz-border-radius-bottomleft: 5px; - -webkit-border-bottom-left-radius: 5px; -} - -#main-menu li:last-child { - -khtml-border-radius-bottomright: 5px; - -moz-border-radius-bottomright: 5px; - -webkit-border-bottom-right-radius: 5px; -} - -/************************************************************************* -Additional wiki styles -*************************************************************************/ - -.button { - padding-left: .25em; - padding-right: .25em; - background:#507aaa; - color: white; - font-weight: bold; -} - -.wiki p.see-also, .wiki p.caution, .wiki p.important, .wiki p.info, .wiki p.tip, .wiki p.note, -.wiki span.see-also, .wiki span.caution, .wiki span.important, .wiki span.info, .wiki span.tip, .wiki span.note { - display: block; - margin-top: .5em; - margin-bottom: .5em; - - padding: 4px 4px 4px 48px; - min-height: 33px; -} -.wiki p.smallsee-also, .wiki p.smallcaution, .wiki p.smallimportant, .wiki p.smallinfo, .wiki p.smalltip, .wiki p.smallnote, -.wiki span.smallsee-also, .wiki span.smallcaution, .wiki span.smallimportant, .wiki span.smallinfo, .wiki span.smalltip, .wiki span.smallnote { - display: block; - margin-top: .5em; - margin-bottom: .5em; - - padding: 4px 4px 4px 34px; - min-height: 24px; -} - -.wiki p.see-also, .wiki span.see-also { - background: url(../images/wiki_styles/see-also.png) 4px 4px no-repeat #f5fffa; - border: 1px solid #AAB1AD; -} -.wiki p.smallsee-also, .wiki span.smallsee-also { - background: url(../images/wiki_styles/see-also_small.png) 4px 4px no-repeat #f5fffa; - border: 1px solid #AAB1AD; -} - -.wiki p.caution, .wiki span.caution { - background: url(../images/wiki_styles/caution.png) 4px 6px no-repeat #f5fffa; - border: 1px solid #AAB1AD; -} -.wiki p.smallcaution, .wiki span.smallcaution { - background: url(../images/wiki_styles/caution_small.png) 4px 4px no-repeat #f5fffa; - border: 1px solid #AAB1AD; -} - -.wiki p.important, .wiki span.important { - background: url(../images/wiki_styles/important.png) 4px 7px no-repeat #F0F8FF; - border: 1px solid #C1C8CF; -} -.wiki p.smallimportant, .wiki span.smallimportant { - background: url(../images/wiki_styles/important_small.png) 4px 6px no-repeat #F0F8FF; - border: 1px solid #C1C8CF; -} - -.wiki p.info, .wiki span.info { - background: url(../images/wiki_styles/info.png) 4px 4px no-repeat #FFFFE0; - border: 1px solid #FFFF00; -} -.wiki p.smallinfo, .wiki span.smallinfo { - background: url(../images/wiki_styles/info_small.png) 4px 4px no-repeat #FFFFE0; - border: 1px solid #FFFF00; -} - -.wiki p.tip, .wiki span.tip { - background: url(../images/wiki_styles/tip.png) 4px 4px no-repeat #F5FFFA; - border: 1px solid #C7CFCA; -} -.wiki p.smalltip, .wiki span.smalltip { - background: url(../images/wiki_styles/tip_small.png) 4px 5px no-repeat #F5FFFA; - border: 1px solid #C7CFCA; -} - -.wiki p.note, .wiki span.note { - background: url(../images/wiki_styles/note.png) 6px 4px no-repeat #F5FFFA; - border: 1px solid #C7CFCA; -} -.wiki p.smallnote, .wiki span.smallnote { - background: url(../images/wiki_styles/note_small.png) 5px 4px no-repeat #F5FFFA; - border: 1px solid #C7CFCA; -} \ No newline at end of file diff --git a/public/themes/classic/images/home.png b/public/themes/classic/images/home.png deleted file mode 100644 index fed62219..00000000 Binary files a/public/themes/classic/images/home.png and /dev/null differ diff --git a/public/themes/classic/images/wrench.png b/public/themes/classic/images/wrench.png deleted file mode 100644 index 5c8213fe..00000000 Binary files a/public/themes/classic/images/wrench.png and /dev/null differ diff --git a/public/themes/classic/stylesheets/application.css b/public/themes/classic/stylesheets/application.css deleted file mode 100644 index 3f855fe9..00000000 --- a/public/themes/classic/stylesheets/application.css +++ /dev/null @@ -1,41 +0,0 @@ -@import url(../../../stylesheets/application.css); - -body{ color:#303030; background:#e8eaec; } - -#top-menu { font-size: 80%; height: 2em; padding-top: 0.5em; background-color: #578bb8; } -#top-menu a { font-weight: bold; } -#header { background: #467aa7; height:5.8em; padding: 10px 0 0 0; } -#header h1 { margin-left: 6px; } -#quick-search { margin-right: 6px; } -#main-menu { background-color: #578bb8; left: 0; border-top: 1px solid #fff; width: 100%; } -#main-menu li { margin: 0; padding: 0; } -#main-menu li a { background-color: #578bb8; border-right: 1px solid #fff; font-size: 90%; padding: 4px 8px 4px 8px; font-weight: bold; } -#main-menu li a:hover { background-color: #80b0da; color: #ffffff; } -#main-menu li a.selected, #main-menu li a.selected:hover { background-color: #80b0da; color: #ffffff; } - -#footer { background-color: #578bb8; border: 0; color: #fff;} -#footer a { color: #fff; font-weight: bold; } - -#main { font:90% Verdana,Tahoma,Arial,sans-serif; background: #e8eaec; } -#main a { font-weight: bold; color: #467aa7;} -#main a:hover { color: #2a5a8a; text-decoration: underline; } -#content { background: #fff; } -#content .tabs ul { bottom:-1px; } - -h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { border-bottom: 0px; color:#606060; font-family: Trebuchet MS,Georgia,"Times New Roman",serif; } -h2, .wiki h1 { letter-spacing:-1px; } -h4 { border-bottom: dotted 1px #c0c0c0; } - -#top-menu a.home, #top-menu a.my-page, #top-menu a.projects, #top-menu a.administration, #top-menu a.help { - background-position: 0% 40%; - background-repeat: no-repeat; - padding-left: 20px; - padding-top: 2px; - padding-bottom: 3px; -} - -#top-menu a.home { background-image: url(../images/home.png); } -#top-menu a.my-page { background-image: url(../../../images/user.png); } -#top-menu a.projects { background-image: url(../../../images/projects.png); } -#top-menu a.administration { background-image: url(../images/wrench.png); } -#top-menu a.help { background-image: url(../../../images/help.png); } diff --git a/public/themes/example/stylesheets/application.css b/public/themes/example/stylesheets/application.css new file mode 100644 index 00000000..4bee14bc --- /dev/null +++ b/public/themes/example/stylesheets/application.css @@ -0,0 +1,7 @@ +/* This file is used to define colors for ChiliProject. They are kept */ +/* separate in order to make it easy for someone to create a new theme. */ +/* */ +/* Some default colors are defined in application.css, mostly grays and */ +/* logical ones (e.g. errors are red). */ + +@import url(../../../stylesheets/application.css); diff --git a/test/fixtures/diffs/subversion.diff b/test/fixtures/diffs/subversion.diff index 9b6c9d08..67def6bf 100644 --- a/test/fixtures/diffs/subversion.diff +++ b/test/fixtures/diffs/subversion.diff @@ -22,7 +22,7 @@ Index: app/views/common/_diff.rhtml +<% diff.each do |table_file| -%>
    <% if diff_type == 'sbs' -%> -
    +
    @@ -62,3 +63,5 @@ diff --git a/test/fixtures/mail_handler/ticket_on_project_with_missing_information.eml b/test/fixtures/mail_handler/ticket_on_project_with_missing_information.eml new file mode 100644 index 00000000..51e00752 --- /dev/null +++ b/test/fixtures/mail_handler/ticket_on_project_with_missing_information.eml @@ -0,0 +1,23 @@ +Return-Path: +Received: from osiris ([127.0.0.1]) + by OSIRIS + with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200 +Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris> +From: "John Smith" +To: +Subject: New ticket on a given project +Date: Sun, 22 Jun 2008 12:28:07 +0200 +MIME-Version: 1.0 +Content-Type: text/plain; + format=flowed; + charset="iso-8859-1"; + reply-type=original +Content-Transfer-Encoding: 7bit +X-Priority: 3 +X-MSMail-Priority: Normal +X-Mailer: Microsoft Outlook Express 6.00.2900.2869 +X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869 + +Test with missing information + +Project: onlinestore diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml index cd2538cc..5fc52b67 100644 --- a/test/fixtures/roles.yml +++ b/test/fixtures/roles.yml @@ -34,6 +34,9 @@ roles_001: - :comment_news - :view_documents - :manage_documents + - :view_document_watchers + - :add_document_watchers + - :delete_document_watchers - :view_wiki_pages - :export_wiki_pages - :view_wiki_edits @@ -42,6 +45,9 @@ roles_001: - :protect_wiki_pages - :delete_wiki_pages - :rename_wiki_pages + - :view_wiki_page_watchers + - :add_wiki_page_watchers + - :delete_wiki_page_watchers - :add_messages - :edit_messages - :delete_messages diff --git a/test/fixtures/wiki_contents.yml b/test/fixtures/wiki_contents.yml index bf2797b8..060ec167 100644 --- a/test/fixtures/wiki_contents.yml +++ b/test/fixtures/wiki_contents.yml @@ -3,7 +3,7 @@ wiki_contents_001: text: |- h1. CookBook documentation - {{child_pages}} + {% child_pages %} Some updated [[documentation]] here with gzipped history updated_on: 2007-03-07 00:10:51 +01:00 @@ -17,7 +17,7 @@ wiki_contents_002: This is a link to a ticket: #2 And this is an included page: - {{include(Page with an inline image)}} + {% include 'Page with an inline image' %} updated_on: 2007-03-08 00:18:07 +01:00 page_id: 2 id: 2 diff --git a/test/functional/account_controller_test.rb b/test/functional/account_controller_test.rb index 4c87e1eb..7a797dcc 100644 --- a/test/functional/account_controller_test.rb +++ b/test/functional/account_controller_test.rb @@ -21,6 +21,7 @@ class AccountControllerTest < ActionController::TestCase fixtures :users, :roles def setup + super @controller = AccountController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new diff --git a/test/functional/admin_controller_test.rb b/test/functional/admin_controller_test.rb index 800d12f2..ba6de52d 100644 --- a/test/functional/admin_controller_test.rb +++ b/test/functional/admin_controller_test.rb @@ -74,7 +74,7 @@ class AdminControllerTest < ActionController::TestCase mail = ActionMailer::Base.deliveries.last assert_kind_of TMail::Mail, mail user = User.find(1) - assert_equal [user.mail], mail.bcc + assert_equal [user.mail], mail.to end def test_no_plugins diff --git a/test/functional/auth_sources_controller_test.rb b/test/functional/auth_sources_controller_test.rb index b1e39107..18af1011 100644 --- a/test/functional/auth_sources_controller_test.rb +++ b/test/functional/auth_sources_controller_test.rb @@ -14,6 +14,12 @@ require File.expand_path('../../test_helper', __FILE__) +# Remove to_s on the TreeNode. This would cause an error on Ruby 1.9 as the +# method has a bug preventing it to return strings. It is implicitly called by +# shoulda during an inspect on Ruby 1.9 only. The bug is reported at +# http://rubyforge.org/tracker/index.php?func=detail&aid=29435&group_id=1215&atid=4793 +Tree::TreeNode.class_eval {remove_method :to_s} + class AuthSourcesControllerTest < ActionController::TestCase fixtures :all diff --git a/test/functional/auto_completes_controller_test.rb b/test/functional/auto_completes_controller_test.rb index 84e62cf4..50e0ba81 100644 --- a/test/functional/auto_completes_controller_test.rb +++ b/test/functional/auto_completes_controller_test.rb @@ -63,4 +63,138 @@ class AutoCompletesControllerTest < ActionController::TestCase assert_response :success assert_equal [], assigns(:issues) end + + context "GET :users" do + setup do + @login = User.generate!(:login => 'Acomplete') + @firstname = User.generate!(:firstname => 'Complete') + @lastname = User.generate!(:lastname => 'Complete') + @none = User.generate!(:login => 'hello', :firstname => 'ABC', :lastname => 'DEF') + @inactive = User.generate!(:firstname => 'Complete', :status => User::STATUS_LOCKED) + end + + context "with no restrictions" do + setup do + get :users, :q => 'complete' + end + + should_respond_with :success + + should "render a list of matching users in checkboxes" do + assert_select "input[type=checkbox][value=?]", @login.id + assert_select "input[type=checkbox][value=?]", @firstname.id + assert_select "input[type=checkbox][value=?]", @lastname.id + assert_select "input[type=checkbox][value=?]", @none.id, :count => 0 + end + + should "only show active users" do + assert_select "input[type=checkbox][value=?]", @inactive.id, :count => 0 + end + end + + context "including groups" do + setup do + @group = Group.generate(:lastname => 'Complete Group').reload + get :users, :q => 'complete', :include_groups => true + end + + should_respond_with :success + + should "include matching groups" do + assert_select "input[type=checkbox][value=?]", @group.id + end + + end + + context "restrict by removing group members" do + setup do + @group = Group.first + @group.users << @login + @group.users << @firstname + get :users, :q => 'complete', :remove_group_members => @group.id + end + + should_respond_with :success + + should "not include existing members of the Group" do + assert_select "input[type=checkbox][value=?]", @lastname.id + + assert_select "input[type=checkbox][value=?]", @login.id, :count => 0 + assert_select "input[type=checkbox][value=?]", @firstname.id, :count => 0 + end + end + + context "restrict by removing issue watchers" do + setup do + @issue = Issue.find(2) + @issue.add_watcher(@login) + @issue.add_watcher(@firstname) + get :users, :q => 'complete', :remove_watchers => @issue.id, :klass => 'Issue' + end + + should_respond_with :success + + should "not include existing watchers" do + assert_select "input[type=checkbox][value=?]", @lastname.id + + assert_select "input[type=checkbox][value=?]", @login.id, :count => 0 + assert_select "input[type=checkbox][value=?]", @firstname.id, :count => 0 + end + end + end + + context "POST to #projects" do + setup do + # Clear out some fixtures + Project.delete_all + ProjectCustomField.delete_all + end + + should 'require admin' do + @request.session[:user_id] = 2 + post :projects, {} + + assert_response 403 + end + + context 'with a valid search' do + setup do + @user = User.generate_with_protected! + @projects = [ + Project.generate!(:name => "Test"), + Project.generate!(:name => "This is a Test") + ] + Project.generate!(:name => "No match") + + @request.session[:user_id] = 1 + post :projects, { + :id => @user.id, + :q => 'TeST' + } + + end + + should_assign_to(:principal) { @user } + should_assign_to(:projects) { @projects } + should_render_template :projects + end + + context 'with an invalid search' do + setup do + @user = User.generate_with_protected! + Project.generate!(:name => "Test") + + @request.session[:user_id] = 1 + post :projects, { + :id => @user.id, + :q => 'nothing' + } + + end + should_assign_to(:principal) { @user } + should_assign_to(:projects) { [] } + should_render_template :projects + + end + end end diff --git a/test/functional/context_menus_controller_test.rb b/test/functional/context_menus_controller_test.rb index 00048973..3b6736fb 100644 --- a/test/functional/context_menus_controller_test.rb +++ b/test/functional/context_menus_controller_test.rb @@ -46,23 +46,18 @@ class ContextMenusControllerTest < ActionController::TestCase :attributes => { :href => '/projects/ecookbook/issues/1/copy', :class => 'icon-duplicate' } assert_tag :tag => 'a', :content => 'Copy', - :attributes => { :href => '/issues/move/new?copy_options%5Bcopy%5D=t&ids%5B%5D=1', - :class => 'icon-copy' } + :attributes => { :href => '/issues/move/new?copy_options%5Bcopy%5D=t&ids%5B%5D=1' } assert_tag :tag => 'a', :content => 'Move', - :attributes => { :href => '/issues/move/new?ids%5B%5D=1', - :class => 'icon-move' } + :attributes => { :href => '/issues/move/new?ids%5B%5D=1'} assert_tag :tag => 'a', :content => 'Delete', - :attributes => { :href => '/issues/destroy?ids%5B%5D=1', - :class => 'icon-del' } + :attributes => { :href => '/issues/destroy?ids%5B%5D=1' } end def test_context_menu_one_issue_by_anonymous get :issues, :ids => [1] assert_response :success assert_template 'context_menu' - assert_tag :tag => 'a', :content => 'Delete', - :attributes => { :href => '#', - :class => 'icon-del disabled' } + assert_select "a.disabled", :text => /Delete/ end def test_context_menu_multiple_issues_of_same_project @@ -87,14 +82,11 @@ class ContextMenusControllerTest < ActionController::TestCase :attributes => { :href => "/issues/bulk_edit?#{ids}&issue%5Bassigned_to_id%5D=3", :class => '' } assert_tag :tag => 'a', :content => 'Copy', - :attributes => { :href => "/issues/move/new?copy_options%5Bcopy%5D=t&#{ids}", - :class => 'icon-copy' } + :attributes => { :href => "/issues/move/new?copy_options%5Bcopy%5D=t&#{ids}"} assert_tag :tag => 'a', :content => 'Move', - :attributes => { :href => "/issues/move/new?#{ids}", - :class => 'icon-move' } + :attributes => { :href => "/issues/move/new?#{ids}"} assert_tag :tag => 'a', :content => 'Delete', - :attributes => { :href => "/issues/destroy?#{ids}", - :class => 'icon-del' } + :attributes => { :href => "/issues/destroy?#{ids}"} end def test_context_menu_multiple_issues_of_different_projects @@ -119,8 +111,7 @@ class ContextMenusControllerTest < ActionController::TestCase :attributes => { :href => "/issues/bulk_edit?#{ids}&issue%5Bassigned_to_id%5D=2", :class => '' } assert_tag :tag => 'a', :content => 'Delete', - :attributes => { :href => "/issues/destroy?#{ids}", - :class => 'icon-del' } + :attributes => { :href => "/issues/destroy?#{ids}"} end def test_context_menu_issue_visibility diff --git a/test/functional/documents_controller_test.rb b/test/functional/documents_controller_test.rb index 2375594a..441f5ab7 100644 --- a/test/functional/documents_controller_test.rb +++ b/test/functional/documents_controller_test.rb @@ -80,7 +80,71 @@ LOREM assert_equal Enumeration.find(2), document.category assert_equal 1, document.attachments.size assert_equal 'testfile.txt', document.attachments.first.filename - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size + end + + context "#new" do + should "allow adding watchers" do + @request.session[:user_id] = 2 + set_tmp_attachments_directory + + post(:new, + :project_id => 'ecookbook', + :document => { + :title => 'DocumentsControllerTest#test_post_new', + :description => 'This is a new document', + :category_id => 2, + :watcher_user_ids => ['2','3'] + }, + :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}) + + assert_redirected_to '/projects/ecookbook/documents' + + document = Document.find_by_title('DocumentsControllerTest#test_post_new') + assert_not_nil document + assert document.watched_by?(User.find(2)) + assert document.watched_by?(User.find(3)) + end + end + + context "POST #edit" do + setup do + @request.session[:user_id] = 2 + set_tmp_attachments_directory + + @document = Document.generate!(:project => Project.find('ecookbook'), + :title => 'Test') + end + + should "update the document" do + post(:edit, + :id => @document.id, + :document => { + :title => 'Change' + }) + + assert_response :redirect + + @document.reload + assert_not_nil @document + assert_equal 'Change', @document.title + end + + should "allow adding watchers" do + post(:edit, + :id => @document.id, + :document => { + :title => 'Change', + :watcher_user_ids => ['2','3'] + }) + + assert_response :redirect + + @document.reload + assert_not_nil @document + assert @document.watched_by?(User.find(2)) + assert @document.watched_by?(User.find(3)) + end end def test_destroy diff --git a/test/functional/groups_controller_test.rb b/test/functional/groups_controller_test.rb index 7568d29f..06695d73 100644 --- a/test/functional/groups_controller_test.rb +++ b/test/functional/groups_controller_test.rb @@ -18,7 +18,7 @@ require 'groups_controller' class GroupsController; def rescue_action(e) raise e end; end class GroupsControllerTest < ActionController::TestCase - fixtures :projects, :users, :members, :member_roles, :groups_users + fixtures :all def setup @controller = GroupsController.new @@ -95,18 +95,16 @@ class GroupsControllerTest < ActionController::TestCase end end + def test_new_membership_with_multiple_projects + assert_difference 'Group.find(10).members.count', 3 do + post :edit_membership, :id => 10, :project_ids => [1,2,3], :membership => { :role_ids => ['1', '2']} + end + end + def test_destroy_membership assert_difference 'Group.find(10).members.count', -1 do post :destroy_membership, :id => 10, :membership_id => 6 end end - def test_autocomplete_for_user - get :autocomplete_for_user, :id => 10, :q => 'mis' - assert_response :success - users = assigns(:users) - assert_not_nil users - assert users.any? - assert !users.include?(Group.find(10).users.first) - end end diff --git a/test/functional/help_controller_test.rb b/test/functional/help_controller_test.rb index e41fbae1..3a316b26 100644 --- a/test/functional/help_controller_test.rb +++ b/test/functional/help_controller_test.rb @@ -12,7 +12,7 @@ # See doc/COPYRIGHT.rdoc for more details. #++ -require 'test_helper' +require File.expand_path('../../test_helper', __FILE__) class HelpControllerTest < ActionController::TestCase test "renders wiki_syntax properly" do diff --git a/test/functional/issue_statuses_controller_test.rb b/test/functional/issue_statuses_controller_test.rb index cb630394..129ac91b 100644 --- a/test/functional/issue_statuses_controller_test.rb +++ b/test/functional/issue_statuses_controller_test.rb @@ -23,6 +23,7 @@ class IssueStatusesControllerTest < ActionController::TestCase fixtures :issue_statuses, :issues def setup + super @controller = IssueStatusesController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 9f186ce4..2d417da4 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -521,9 +521,8 @@ class IssuesControllerTest < ActionController::TestCase assert_equal [2, 3], issue.watcher_user_ids.sort assert issue.watched_by?(User.find(3)) # Watchers notified - mail = ActionMailer::Base.deliveries.last - assert_kind_of TMail::Mail, mail - assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail) + recipients = ActionMailer::Base.deliveries.collect(&:to) + assert recipients.flatten.include?(User.find(3).mail) end def test_post_create_subissue @@ -568,7 +567,7 @@ class IssuesControllerTest < ActionController::TestCase end assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_post_create_should_preserve_fields_values_on_validation_failure @@ -1017,7 +1016,7 @@ class IssuesControllerTest < ActionController::TestCase :priority_id => '6', :category_id => '1' # no change } - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_put_update_should_not_send_a_notification_if_send_notification_is_off @@ -1240,7 +1239,7 @@ class IssuesControllerTest < ActionController::TestCase }) assert_response 302 - assert_equal 2, ActionMailer::Base.deliveries.size + assert_equal 5, ActionMailer::Base.deliveries.size end def test_bulk_update_status @@ -1390,9 +1389,9 @@ class IssuesControllerTest < ActionController::TestCase def test_default_search_scope get :index - assert_tag :div, :attributes => {:id => 'quick-search'}, - :child => {:tag => 'form', - :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}} + assert_select "#search form" do + assert_select "input[type=hidden][name=issues][value=1]" + end end def test_reply_to_note diff --git a/test/functional/journals_controller_test.rb b/test/functional/journals_controller_test.rb index 7654682a..58e78feb 100644 --- a/test/functional/journals_controller_test.rb +++ b/test/functional/journals_controller_test.rb @@ -81,4 +81,68 @@ class JournalsControllerTest < ActionController::TestCase assert_select_rjs :show, "update" end + context "#diff" do + setup do + @request.session[:user_id] = 1 + @issue = Issue.find(6) + @previous_description = @issue.description + @new_description = "New description" + + assert_difference("Journal.count") do + @issue.description = @new_description + assert @issue.save + end + @last_journal = @issue.last_journal + end + + context "without a valid journal" do + should "return a 404" do + get :diff, :id => '0' + assert_response :not_found + end + end + + + context "with no field parameter" do + should "return a 404" do + get :diff, :id => @last_journal.id + assert_response :not_found + end + end + + context "for an invalid field" do + should "return a 404" do + get :diff, :id => @last_journal.id, :field => 'id' + assert_response :not_found + end + end + + context "without permission to view_issues" do + should "return a 403" do + @request.session[:user_id] = 7 + get :diff, :id => @last_journal.id, :field => 'description' + + assert_response :forbidden + end + + end + + context "with permission to view_issues" do + setup do + get :diff, :id => @last_journal.id, :field => 'description' + end + + should "create a diff" do + assert_not_nil assigns(:diff) + assert assigns(:diff).is_a?(Redmine::Helpers::Diff) + end + + should "render an inline diff" do + assert_select "#content .text-diff" + end + + end + + end + end diff --git a/test/functional/ldap_auth_sources_controller.rb b/test/functional/ldap_auth_sources_controller.rb index ed7af8a3..d39a1590 100644 --- a/test/functional/ldap_auth_sources_controller.rb +++ b/test/functional/ldap_auth_sources_controller.rb @@ -12,7 +12,7 @@ # See doc/COPYRIGHT.rdoc for more details. #++ -require 'test_helper' +require File.expand_path('../../test_helper', __FILE__) class LdapAuthSourcesControllerTest < ActionController::TestCase fixtures :all diff --git a/test/functional/messages_controller_test.rb b/test/functional/messages_controller_test.rb index d9800e56..fecc5e26 100644 --- a/test/functional/messages_controller_test.rb +++ b/test/functional/messages_controller_test.rb @@ -88,14 +88,19 @@ class MessagesControllerTest < ActionController::TestCase assert_equal 2, message.author_id assert_equal 1, message.board_id - mail = ActionMailer::Base.deliveries.last + # author + mails_to_author = ActionMailer::Base.deliveries.select {|m| m.to.include?('jsmith@somenet.foo') } + assert_equal 1, mails_to_author.length + mail = mails_to_author.first + assert mail.to.include?('jsmith@somenet.foo') assert_kind_of TMail::Mail, mail assert_equal "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] Test created message", mail.subject assert mail.body.include?('Message body') - # author - assert mail.bcc.include?('jsmith@somenet.foo') + # project member - assert mail.bcc.include?('dlopper@somenet.foo') + mails_to_member = ActionMailer::Base.deliveries.select {|m| m.to.include?('dlopper@somenet.foo') } + assert_equal 1, mails_to_member.length + assert mails_to_member.first.to.include?('dlopper@somenet.foo') end def test_get_edit diff --git a/test/functional/news_controller_test.rb b/test/functional/news_controller_test.rb index b2ae7ab4..138f56e8 100644 --- a/test/functional/news_controller_test.rb +++ b/test/functional/news_controller_test.rb @@ -76,7 +76,7 @@ class NewsControllerTest < ActionController::TestCase assert_equal 'This is the description', news.description assert_equal User.find(2), news.author assert_equal Project.find(1), news.project - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_get_edit diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index bc62708f..d650e6fd 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -429,21 +429,6 @@ class ProjectsControllerTest < ActionController::TestCase assert Project.find(1).active? end - def test_project_breadcrumbs_should_be_limited_to_3_ancestors - CustomField.delete_all - parent = nil - 6.times do |i| - p = Project.create!(:name => "Breadcrumbs #{i}", :identifier => "breadcrumbs-#{i}") - p.set_parent!(parent) - get :show, :id => p - assert_tag :h1, :parent => { :attributes => {:id => 'header'}}, - :children => { :count => [i, 3].min, - :only => { :tag => 'a' } } - - parent = p - end - end - def test_copy_with_project @request.session[:user_id] = 1 # admin get :copy, :id => 1 diff --git a/test/functional/repositories_bazaar_controller_test.rb b/test/functional/repositories_bazaar_controller_test.rb index b493c45c..6e643d57 100644 --- a/test/functional/repositories_bazaar_controller_test.rb +++ b/test/functional/repositories_bazaar_controller_test.rb @@ -131,7 +131,6 @@ class RepositoriesBazaarControllerTest < ActionController::TestCase :sibling => { :tag => 'td', :content => /Main purpose/ } end else - puts "Bazaar test repository NOT FOUND. Skipping functional tests !!!" - def test_fake; assert true end + should "Bazaar test repository not found." end end diff --git a/test/functional/repositories_cvs_controller_test.rb b/test/functional/repositories_cvs_controller_test.rb index ef22f344..d94db60a 100644 --- a/test/functional/repositories_cvs_controller_test.rb +++ b/test/functional/repositories_cvs_controller_test.rb @@ -189,7 +189,6 @@ class RepositoriesCvsControllerTest < ActionController::TestCase } end else - puts "CVS test repository NOT FOUND. Skipping functional tests !!!" - def test_fake; assert true end + should "CVS test repository not found." end end diff --git a/test/functional/repositories_darcs_controller_test.rb b/test/functional/repositories_darcs_controller_test.rb index 0a3944e9..700ea7ef 100644 --- a/test/functional/repositories_darcs_controller_test.rb +++ b/test/functional/repositories_darcs_controller_test.rb @@ -98,7 +98,6 @@ class RepositoriesDarcsControllerTest < ActionController::TestCase :content => /def remove/ } end else - puts "Darcs test repository NOT FOUND. Skipping functional tests !!!" - def test_fake; assert true end + should "Darcs test repository not found." end end diff --git a/test/functional/repositories_filesystem_controller_test.rb b/test/functional/repositories_filesystem_controller_test.rb index 8ea577f6..3a310d11 100644 --- a/test/functional/repositories_filesystem_controller_test.rb +++ b/test/functional/repositories_filesystem_controller_test.rb @@ -102,7 +102,6 @@ class RepositoriesFilesystemControllerTest < ActionController::TestCase end end else - puts "Filesystem test repository NOT FOUND. Skipping functional tests !!!" - def test_fake; assert true end + should "Filesystem test repository not found." end end diff --git a/test/functional/repositories_git_controller_test.rb b/test/functional/repositories_git_controller_test.rb index 02d014d3..d1097afe 100644 --- a/test/functional/repositories_git_controller_test.rb +++ b/test/functional/repositories_git_controller_test.rb @@ -231,7 +231,6 @@ class RepositoriesGitControllerTest < ActionController::TestCase end end else - puts "Git test repository NOT FOUND. Skipping functional tests !!!" - def test_fake; assert true end + should "Git test repository not found." end end diff --git a/test/functional/repositories_mercurial_controller_test.rb b/test/functional/repositories_mercurial_controller_test.rb index aa53ca85..d37729fa 100644 --- a/test/functional/repositories_mercurial_controller_test.rb +++ b/test/functional/repositories_mercurial_controller_test.rb @@ -362,7 +362,6 @@ class RepositoriesMercurialControllerTest < ActionController::TestCase end end else - puts "Mercurial test repository NOT FOUND. Skipping functional tests !!!" - def test_fake; assert true end + should "Mercurial test repository not found." end end diff --git a/test/functional/repositories_subversion_controller_test.rb b/test/functional/repositories_subversion_controller_test.rb index 90b49224..bfd5f996 100644 --- a/test/functional/repositories_subversion_controller_test.rb +++ b/test/functional/repositories_subversion_controller_test.rb @@ -285,7 +285,7 @@ class RepositoriesSubversionControllerTest < ActionController::TestCase assert_tag :tag => 'h2', :content => /@ 8/ end else - puts "Subversion test repository NOT FOUND. Skipping functional tests !!!" - def test_fake; assert true end + should "Subversion test repository not found." + end end diff --git a/test/functional/time_entry_reports_controller_test.rb b/test/functional/time_entry_reports_controller_test.rb index 5ad5c971..e96d640e 100644 --- a/test/functional/time_entry_reports_controller_test.rb +++ b/test/functional/time_entry_reports_controller_test.rb @@ -128,6 +128,14 @@ class TimeEntryReportsControllerTest < ActionController::TestCase assert_equal "0.00", "%.2f" % assigns(:total_hours) end + def test_report_status_criterion + get :report, :project_id => 1, :criterias => ['status'] + assert_response :success + assert_template 'report' + assert_tag :tag => 'th', :content => 'Status' + assert_tag :tag => 'td', :content => 'New' + end + def test_report_all_projects_csv_export get :report, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv" assert_response :success diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index c09b2a4f..c33c9fa4 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -179,7 +179,7 @@ class UsersControllerTest < ActionController::TestCase mail = ActionMailer::Base.deliveries.last assert_not_nil mail - assert_equal [user.mail], mail.bcc + assert_equal [user.mail], mail.to assert mail.body.include?('secret') end @@ -240,7 +240,7 @@ class UsersControllerTest < ActionController::TestCase assert u.reload.active? mail = ActionMailer::Base.deliveries.last assert_not_nil mail - assert_equal ['foo.bar@somenet.foo'], mail.bcc + assert_equal ['foo.bar@somenet.foo'], mail.to assert mail.body.include?(ll('fr', :notice_account_activated)) end @@ -254,7 +254,7 @@ class UsersControllerTest < ActionController::TestCase mail = ActionMailer::Base.deliveries.last assert_not_nil mail - assert_equal [u.mail], mail.bcc + assert_equal [u.mail], mail.to assert mail.body.include?('newpass') end @@ -302,6 +302,13 @@ class UsersControllerTest < ActionController::TestCase assert_equal [2], Member.find(1).role_ids end + + def test_new_membership_with_multiple_projects + assert_difference 'User.find(2).members.count', 2 do + post :edit_membership, :id => 2, :project_ids => [3,6], :membership => { :role_ids => ['1', '2']} + end + end + def test_destroy_membership post :destroy_membership, :id => 2, :membership_id => 1 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships' diff --git a/test/functional/watchers_controller_test.rb b/test/functional/watchers_controller_test.rb index 17bc7397..77900331 100644 --- a/test/functional/watchers_controller_test.rb +++ b/test/functional/watchers_controller_test.rb @@ -19,7 +19,8 @@ class WatchersController; def rescue_action(e) raise e end; end class WatchersControllerTest < ActionController::TestCase fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, - :issues, :trackers, :projects_trackers, :issue_statuses, :enumerations, :watchers + :issues, :trackers, :projects_trackers, :issue_statuses, :enumerations, :watchers, + :wikis, :wiki_pages def setup @controller = WatchersController.new @@ -114,14 +115,159 @@ class WatchersControllerTest < ActionController::TestCase def test_new_watcher @request.session[:user_id] = 2 assert_difference('Watcher.count') do - xhr :post, :new, :object_type => 'issue', :object_id => '2', :watcher => {:user_id => '4'} + xhr :post, :new, :object_type => 'issue', :object_id => '2', :user_ids => ['4'] assert_response :success assert_select_rjs :replace_html, 'watchers' end assert Issue.find(2).watched_by?(User.find(4)) end + def test_new_multiple_users + @request.session[:user_id] = 2 + assert_difference('Watcher.count', 2) do + xhr :post, :new, :object_type => 'issue', :object_id => '2', :user_ids => ['4','7'] + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + assert Issue.find(2).watched_by?(User.find(4)) + assert Issue.find(2).watched_by?(User.find(7)) + end + + context "POST :new" do + should "add groups" do + @group = Group.generate!.reload + Member.generate!(:project => Project.find(1), :roles => [Role.find(1)], :principal => @group) + + @request.session[:user_id] = 2 + assert_difference('Watcher.count') do + xhr :post, :new, :object_type => 'issue', :object_id => '2', :user_ids => [@group.id.to_s] + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + assert Issue.find(2).watched_by?(@group) + end + + end + + def test_new_multiple_users_watching_wiki_page + Role.find(1).add_permission! :add_wiki_page_watchers + + @request.session[:user_id] = 2 + @page = WikiPage.find(1) + assert !@page.watched_by?(User.find(2)) + assert !@page.watched_by?(User.find(4)) + assert !@page.watched_by?(User.find(7)) + + assert_difference('Watcher.count', 3) do + xhr :post, :new, :object_type => 'wiki_page', :object_id => '1', :user_ids => ['2','4','7'] + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + @page.reload + assert @page.watched_by?(User.find(2)) + assert @page.watched_by?(User.find(4)) + assert @page.watched_by?(User.find(7)) + end + + def test_new_multiple_users_watching_board + Role.find(1).add_permission! :add_board_watchers + + @request.session[:user_id] = 2 + @project = Project.find(1) + @board = Board.generate!(:project => @project) + assert !@board.watched_by?(User.find(2)) + assert !@board.watched_by?(User.find(4)) + + assert_difference('Watcher.count', 2) do + xhr :post, :new, :object_type => 'board', :object_id => @board.id, :user_ids => ['2','4'] + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + @board.reload + assert @board.watched_by?(User.find(2)) + assert @board.watched_by?(User.find(4)) + end + + def test_new_multiple_users_watching_message + Role.find(1).add_permission! :add_message_watchers + + @request.session[:user_id] = 2 + @project = Project.find(1) + @board = Board.generate!(:project => @project) + @message = Message.generate!(:board => @board) + assert !@message.watched_by?(User.find(2)) + assert !@message.watched_by?(User.find(4)) + + assert_difference('Watcher.count', 2) do + xhr :post, :new, :object_type => 'message', :object_id => @message.id, :user_ids => ['2','4'] + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + @message.reload + assert @message.watched_by?(User.find(2)) + assert @message.watched_by?(User.find(4)) + end + + def test_new_issue_watcher_without_permission + Role.find(1).remove_permission! :add_issue_watchers + + @request.session[:user_id] = 2 + assert_difference('Watcher.count',0) do + xhr :post, :new, :object_type => 'issue', :object_id => '2', :user_ids => ['4'] + assert_response :forbidden + end + assert !Issue.find(2).watched_by?(User.find(4)) + + end + + def test_new_wiki_page_watcher_without_permission + Role.find(1).remove_permission! :add_wiki_page_watchers + + @request.session[:user_id] = 2 + @page = WikiPage.find(1) + + assert_difference('Watcher.count',0) do + xhr :post, :new, :object_type => 'wiki_page', :object_id => '1', :user_ids => ['2'] + assert_response :forbidden + end + assert !WikiPage.find(1).watched_by?(User.find(2)) + + end + + def test_new_board_watcher_without_permission + Role.find(1).remove_permission! :add_board_watchers + + @request.session[:user_id] = 2 + @project = Project.find(1) + @board = Board.generate!(:project => @project) + + assert_difference('Watcher.count',0) do + xhr :post, :new, :object_type => 'board', :object_id => @board.id, :user_ids => ['2'] + assert_response :forbidden + end + assert !Board.find(@board.id).watched_by?(User.find(2)) + + end + + def test_new_message_watcher_without_permission + Role.find(1).remove_permission! :add_message_watchers + + @request.session[:user_id] = 2 + @project = Project.find(1) + @board = Board.generate!(:project => @project) + @message = Message.generate!(:board => @board) + + assert_difference('Watcher.count',0) do + xhr :post, :new, :object_type => 'message', :object_id => @message.id, :user_ids => ['2'] + assert_response :forbidden + end + assert !Message.find(@message.id).watched_by?(User.find(2)) + + end + def test_remove_watcher + Role.find(1).add_permission! :delete_issue_watchers + @request.session[:user_id] = 2 assert_difference('Watcher.count', -1) do xhr :post, :destroy, :object_type => 'issue', :object_id => '2', :user_id => '3' @@ -130,4 +276,130 @@ class WatchersControllerTest < ActionController::TestCase end assert !Issue.find(2).watched_by?(User.find(3)) end + + context "POST :destroy" do + should "remove a group" do + @group = Group.generate!.reload + Member.generate!(:project => Project.find(1), :roles => [Role.find(1)], :principal => @group) + assert Issue.find(2).add_watcher(@group) + assert Issue.find(2).watched_by?(@group) + + @request.session[:user_id] = 2 + assert_difference('Watcher.count', -1) do + xhr :post, :destroy, :object_type => 'issue', :object_id => '2', :user_id => @group.id.to_s + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + assert !Issue.find(2).watched_by?(@group) + end + + end + + def test_remove_wiki_page_watcher + Role.find(1).add_permission! :delete_wiki_page_watchers + + @request.session[:user_id] = 2 + @page = WikiPage.find(1) + Watcher.create!(:user_id => 2, :watchable => @page) + assert @page.watched_by?(User.find(2)) + + assert_difference('Watcher.count', -1) do + xhr :post, :destroy, :object_type => 'wiki_page', :object_id => '1', :user_id => '2' + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + assert !WikiPage.find(1).watched_by?(User.find(2)) + end + + def test_remove_board_watcher + Role.find(1).add_permission! :delete_board_watchers + @project = Project.find(1) + @board = Board.generate!(:project => @project) + Watcher.create!(:user_id => 2, :watchable => @board) + assert @board.watched_by?(User.find(2)) + + @request.session[:user_id] = 2 + assert_difference('Watcher.count', -1) do + xhr :post, :destroy, :object_type => 'board', :object_id => @board.id, :user_id => '2' + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + assert !Board.find(@board.id).watched_by?(User.find(2)) + end + + def test_remove_message_watcher + Role.find(1).add_permission! :delete_message_watchers + @project = Project.find(1) + @board = Board.generate!(:project => @project) + @message = Message.generate!(:board => @board) + Watcher.create!(:user_id => 2, :watchable => @message) + assert @message.watched_by?(User.find(2)) + + @request.session[:user_id] = 2 + assert_difference('Watcher.count', -1) do + xhr :post, :destroy, :object_type => 'message', :object_id => @message.id, :user_id => '2' + assert_response :success + assert_select_rjs :replace_html, 'watchers' + end + assert !Message.find(@message.id).watched_by?(User.find(2)) + end + + def test_remove_issue_watcher_without_permission + Role.find(1).remove_permission! :delete_issue_watchers + + @request.session[:user_id] = 2 + assert_difference('Watcher.count',0) do + xhr :post, :destroy, :object_type => 'issue', :object_id => '2', :user_id => '3' + assert_response :forbidden + end + assert Issue.find(2).watched_by?(User.find(3)) + + end + + def test_remove_wiki_page_watcher_without_permission + Role.find(1).remove_permission! :delete_wiki_page_watchers + + @request.session[:user_id] = 2 + @page = WikiPage.find(1) + Watcher.create!(:user_id => 2, :watchable => @page) + assert @page.watched_by?(User.find(2)) + + assert_difference('Watcher.count',0) do + xhr :post, :destroy, :object_type => 'wiki_page', :object_id => '1', :user_id => '2' + assert_response :forbidden + end + assert WikiPage.find(1).watched_by?(User.find(2)) + + end + + def test_remove_board_watcher_without_permission + Role.find(1).remove_permission! :delete_board_watchers + @project = Project.find(1) + @board = Board.generate!(:project => @project) + Watcher.create!(:user_id => 2, :watchable => @board) + assert @board.watched_by?(User.find(2)) + + @request.session[:user_id] = 2 + assert_difference('Watcher.count', 0) do + xhr :post, :destroy, :object_type => 'board', :object_id => @board.id, :user_id => '2' + assert_response :forbidden + end + assert Board.find(@board.id).watched_by?(User.find(2)) + end + + def test_remove_message_watcher_without_permission + Role.find(1).remove_permission! :delete_message_watchers + @project = Project.find(1) + @board = Board.generate!(:project => @project) + @message = Message.generate!(:board => @board) + Watcher.create!(:user_id => 2, :watchable => @message) + assert @message.watched_by?(User.find(2)) + + @request.session[:user_id] = 2 + assert_difference('Watcher.count', 0) do + xhr :post, :destroy, :object_type => 'message', :object_id => @message.id, :user_id => '2' + assert_response :forbidden + end + assert Message.find(@message.id).watched_by?(User.find(2)) + end end diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb index 355ddc50..b59e9154 100644 --- a/test/functional/wiki_controller_test.rb +++ b/test/functional/wiki_controller_test.rb @@ -18,7 +18,7 @@ require 'wiki_controller' class WikiController; def rescue_action(e) raise e end; end class WikiControllerTest < ActionController::TestCase - fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :journals, :attachments + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :journals, :attachments, :enumerations def setup @controller = WikiController.new diff --git a/test/integration/application_test.rb b/test/integration/application_test.rb index d99bf190..e4366af5 100644 --- a/test/integration/application_test.rb +++ b/test/integration/application_test.rb @@ -49,4 +49,13 @@ class ApplicationTest < ActionController::IntegrationTest assert_response 200 assert_nil session[:user_id] end + + def test_always_use_custom_404 + get 'something_not_existing' + assert_response :not_found + + assert_tag :tag => 'p', + :attributes => {:id => 'errorExplanation'}, + :content => "The page you were trying to access doesn't exist or has been removed." + end end diff --git a/test/integration/journals_test.rb b/test/integration/journals_test.rb new file mode 100644 index 00000000..46f6d9f9 --- /dev/null +++ b/test/integration/journals_test.rb @@ -0,0 +1,47 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ +require File.expand_path('../../test_helper', __FILE__) +require 'capybara/rails' + +class JournalsTest < ActionController::IntegrationTest + fixtures :all + + include IntegrationTestHelpers::CapybaraHelpers + include Capybara::DSL + + test "showing issue description changes as a diff" do + # Description change + @issue = Issue.find(1) + @issue.recreate_initial_journal! + @issue.reload + assert_difference("Journal.count") do + @issue.journal_user = User.find_by_login('jsmith') + @issue.description = "A new description" + assert @issue.save + end + + log_user('jsmith', 'jsmith') + + # Issue page + visit_issue_page(@issue) + assert has_selector?("#history .journal-attributes li i", :text => 'A new description') + within("#history .journal-attributes li") do + find_link("More").click + end + + # Diff page + assert_response :success + assert has_selector?("#content .text-diff", :text => /A new description/) + end +end diff --git a/test/integration/layout_test.rb b/test/integration/layout_test.rb index ab2ea581..c59ae48b 100644 --- a/test/integration/layout_test.rb +++ b/test/integration/layout_test.rb @@ -23,7 +23,7 @@ class LayoutTest < ActionController::IntegrationTest assert_response :not_found # UsersController uses the admin layout by default - assert_select "#admin-menu", :count => 0 + assert_select "#main-menu", :count => 0 end test "browsing to an unauthorized page should render the base layout" do @@ -33,22 +33,20 @@ class LayoutTest < ActionController::IntegrationTest get "/admin" assert_response :forbidden - assert_select "#admin-menu", :count => 0 + assert_select "#main-menu", :count => 0 end - def test_top_menu_and_search_not_visible_when_login_required + def test_top_menu_navigation_not_visible_when_login_required with_settings :login_required => '1' do get '/' - assert_select "#top-menu > ul", 0 - assert_select "#quick-search", 0 + assert_select "#account-nav", 0 end end - def test_top_menu_and_search_visible_when_login_not_required + def test_top_menu_navigation_visible_when_login_not_required with_settings :login_required => '0' do get '/' - assert_select "#top-menu > ul" - assert_select "#quick-search" + assert_select "#account-nav" end end diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 1822ad20..f3a1730e 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -109,6 +109,10 @@ class RoutingTest < ActionController::IntegrationTest should_route :post, "/issues/bulk_edit", :controller => 'issues', :action => 'bulk_update' end + context "journals" do + should_route :get, "/journals/100/diff/description", :controller => 'journals', :action => 'diff', :id => '100', :field => 'description' + end + context "issue categories" do should_route :get, "/projects/test/issue_categories/new", :controller => 'issue_categories', :action => 'new', :project_id => 'test' @@ -346,4 +350,9 @@ class RoutingTest < ActionController::IntegrationTest context "administration panel" do should_route :get, "/admin/projects", :controller => 'admin', :action => 'projects' end + + context "auto_completes" do + should_route :get, "/users/auto_complete", :controller => 'auto_completes', :action => 'users' + end + end diff --git a/test/integration_test_helpers.rb b/test/integration_test_helpers.rb new file mode 100644 index 00000000..c0478b49 --- /dev/null +++ b/test/integration_test_helpers.rb @@ -0,0 +1,84 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +module IntegrationTestHelpers + + # Helpers for Capybara tests only + # + # Warning: might have API incompatibilities with Rails default + # integration test methods. Only include where needed. + module CapybaraHelpers + # Capybara doesn't set the response object so we need to glue this to + # it's own object but without @response + def assert_response(code) + # Rewrite human status codes to numeric + converted_code = case code + when :success + 200 + when :missing + 404 + when :redirect + 302 + when :error + 500 + when code.is_a?(Symbol) + ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[code] + else + code + end + + assert_equal converted_code, page.status_code + end + + # Override the existing log_user method so it correctly sets + # the capybara page elements (sessions, etc) + # + # Actually drives the login form + def log_user(user="existing", password="existing") + visit "/logout" # Make sure the session is cleared + + visit "/login" + fill_in 'Login', :with => user + fill_in 'Password', :with => password + click_button 'Login' + assert_response :success + assert User.current.logged? + end + + def visit_home + visit '/' + assert_response :success + end + + def visit_project(project) + visit_home + assert_response :success + + click_link 'Projects' + assert_response :success + + click_link project.name + assert_response :success + end + + def visit_issue_page(issue) + visit '/issues/' + issue.id.to_s + end + + def visit_issue_bulk_edit_page(issues) + visit url_for(:controller => 'issues', :action => 'bulk_edit', :ids => issues.collect(&:id)) + end + + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 49debf3b..435cc5ab 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -20,6 +20,7 @@ require File.join(RAILS_ROOT,'test', 'mocks', 'open_id_authentication_mock.rb') require File.expand_path(File.dirname(__FILE__) + '/object_daddy_helpers') include ObjectDaddyHelpers +require File.expand_path(File.dirname(__FILE__) + '/integration_test_helpers') class ActiveSupport::TestCase # Transactional fixtures accelerate your tests by wrapping each test method @@ -46,7 +47,8 @@ class ActiveSupport::TestCase # Add more helper methods to be used by all tests here... def setup super - Setting.clear_cache + Setting.use_caching = false + Rails.cache.clear end def log_user(login, password) @@ -434,6 +436,10 @@ class ActiveSupport::TestCase end +class ActionController::IntegrationTest + include IntegrationTestHelpers +end + # Simple module to "namespace" all of the API tests module ApiTest end diff --git a/test/unit/changeset_test.rb b/test/unit/changeset_test.rb index 1a5d7330..592d568f 100644 --- a/test/unit/changeset_test.rb +++ b/test/unit/changeset_test.rb @@ -19,6 +19,7 @@ class ChangesetTest < ActiveSupport::TestCase :custom_fields, :custom_values, :users, :members, :member_roles, :trackers def setup + super end def test_ref_keywords_any @@ -37,7 +38,7 @@ class ChangesetTest < ActiveSupport::TestCase fixed = Issue.find(1) assert fixed.closed? assert_equal 90, fixed.done_ratio - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_ref_keywords diff --git a/test/unit/document_test.rb b/test/unit/document_test.rb index 888b45a6..6aafec4a 100644 --- a/test/unit/document_test.rb +++ b/test/unit/document_test.rb @@ -14,7 +14,7 @@ require File.expand_path('../../test_helper', __FILE__) class DocumentTest < ActiveSupport::TestCase - fixtures :projects, :enumerations, :documents, :attachments + fixtures :all def test_create doc = Document.new(:project => Project.find(1), :title => 'New document', :category => Enumeration.find_by_name('User documentation')) @@ -27,7 +27,7 @@ class DocumentTest < ActiveSupport::TestCase doc = Document.new(:project => Project.find(1), :title => 'New document', :category => Enumeration.find_by_name('User documentation')) assert doc.save - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_create_with_default_category @@ -51,4 +51,22 @@ class DocumentTest < ActiveSupport::TestCase assert d.attachments.empty? assert_equal d.created_on, d.updated_on end + + should "allow watchers" do + assert Document.included_modules.include?(Redmine::Acts::Watchable::InstanceMethods) + assert Document.new.respond_to?(:add_watcher) + end + + context "#recipients" do + should "include watchers" do + document = Document.generate!(:project => Project.find(1)) + user = User.find(1) + assert document.add_watcher(user) + + assert document.save + + assert document.recipients.include?(user.mail), "Watcher not included in recipients" + end + end + end diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb index 7a2b03c0..48c99c05 100644 --- a/test/unit/helpers/application_helper_test.rb +++ b/test/unit/helpers/application_helper_test.rb @@ -475,7 +475,7 @@ EXPECTED RAW expected = <<-EXPECTED -
    1 # Some ruby code here
    +
    1# Some ruby code here
     
    EXPECTED @@ -593,7 +593,7 @@ RAW h1. Included -{{include(Child_1)}} +{% include 'Child_1' %} RAW expected = '
      ' + diff --git a/test/unit/helpers/issue_moves_helper_test.rb b/test/unit/helpers/issue_moves_helper_test.rb index d4fc239b..34c2e9fa 100644 --- a/test/unit/helpers/issue_moves_helper_test.rb +++ b/test/unit/helpers/issue_moves_helper_test.rb @@ -12,7 +12,7 @@ # See doc/COPYRIGHT.rdoc for more details. #++ -require 'test_helper' +require File.expand_path('../../../test_helper', __FILE__) class IssueMovesHelperTest < ActionView::TestCase end diff --git a/test/unit/issue_drop_test.rb b/test/unit/issue_drop_test.rb new file mode 100644 index 00000000..56a038c4 --- /dev/null +++ b/test/unit/issue_drop_test.rb @@ -0,0 +1,103 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require File.expand_path('../../test_helper', __FILE__) + +class IssueDropTest < ActiveSupport::TestCase + include ApplicationHelper + + def setup + @project = Project.generate! + @issue = Issue.generate_for_project!(@project) + User.current = @user = User.generate! + @role = Role.generate!(:permissions => [:view_issues]) + Member.generate!(:principal => @user, :project => @project, :roles => [@role]) + @drop = @issue.to_liquid + end + + context "drop" do + should "be a IssueDrop" do + assert @drop.is_a?(IssueDrop), "drop is not a IssueDrop" + end + end + + + [ + :tracker, + :project, + :subject, + :description, + :due_date, + :category, + :status, + :assigned_to, + :priority, + :fixed_version, + :author, + :created_on, + :updated_on, + :start_date, + :done_ratio, + :estimated_hours, + :parent + ].each do |attribute| + + should "IssueDrop##{attribute} should return the actual #{attribute} attribute" do + assert @issue.respond_to?(attribute), "Issue does not have an #{attribute} method" + assert @drop.respond_to?(attribute), "IssueDrop does not have an #{attribute} method" + + assert_equal @issue.send(attribute), @drop.send(attribute) + end + end + + context "custom fields" do + setup do + @field = IssueCustomField.generate!(:name => 'The Name', :field_format => 'string', :is_for_all => true, :trackers => @project.trackers) + @field_name_conflict = IssueCustomField.generate!(:name => 'Subject', :field_format => 'string', :is_for_all => true, :trackers => @project.trackers) + @issue.custom_fields = [{'id' => @field.id, 'value' => 'Custom field value'}, + {'id' => @field_name_conflict.id, 'value' => 'Second subject'}] + assert @issue.save + assert_equal "Custom field value", @issue.reload.custom_value_for(@field).value + assert_equal "Second subject", @issue.reload.custom_value_for(@field_name_conflict).value + @drop = @issue.to_liquid + end + + should "be accessible under #custom_field(name)" do + assert_equal @issue.reload.custom_value_for(@field).value, @drop.custom_field('The Name') + end + + should "be accessible under the custom field name (lowercase, underscored)" do + assert_equal @issue.reload.custom_value_for(@field).value, @drop.the_name + + assert textilizable("{{issue.the_name}}").include?("Custom field value") + end + + should "not be accessible under the custom field name if it conflict with an existing drop method" do + assert_equal @issue.subject, @drop.subject # no confict + end + end + + should "only load an object if it's visible to the current user" do + assert User.current.logged? + assert @issue.visible? + + @private_project = Project.generate!(:is_public => false) + @private_issue = Issue.generate_for_project!(@private_project) + + assert !@private_issue.visible?, "Issue is visible" + @private_drop = IssueDrop.new(@private_issue) + assert_equal nil, @private_drop.instance_variable_get("@object") + assert_equal nil, @private_drop.subject + end +end \ No newline at end of file diff --git a/test/unit/issue_status_drop_test.rb b/test/unit/issue_status_drop_test.rb new file mode 100644 index 00000000..2dea38af --- /dev/null +++ b/test/unit/issue_status_drop_test.rb @@ -0,0 +1,35 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require File.expand_path('../../test_helper', __FILE__) + +class IssueStatusDropTest < ActiveSupport::TestCase + def setup + @issue_status = IssueStatus.generate! + @drop = @issue_status.to_liquid + end + + context "drop" do + should "be a IssueStatusDrop" do + assert @drop.is_a?(IssueStatusDrop), "drop is not a IssueStatusDrop" + end + end + + + context "#name" do + should "return the name" do + assert_equal @issue_status.name, @drop.name + end + end +end diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb index b41591df..6890d591 100644 --- a/test/unit/issue_test.rb +++ b/test/unit/issue_test.rb @@ -599,7 +599,7 @@ class IssueTest < ActiveSupport::TestCase issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => IssuePriority.all.first, :subject => 'test_create', :estimated_hours => '1:30') assert issue.save - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_stale_issue_should_not_send_email_notification @@ -610,7 +610,7 @@ class IssueTest < ActiveSupport::TestCase issue.init_journal(User.find(1)) issue.subject = 'Subjet update' assert issue.save - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size ActionMailer::Base.deliveries.clear stale.init_journal(User.find(1)) diff --git a/test/unit/journal_observer_test.rb b/test/unit/journal_observer_test.rb index eae8fc6b..6e11c95f 100644 --- a/test/unit/journal_observer_test.rb +++ b/test/unit/journal_observer_test.rb @@ -15,6 +15,7 @@ require File.expand_path('../../test_helper', __FILE__) class JournalObserverTest < ActiveSupport::TestCase def setup + super @user = User.generate!(:mail_notification => 'all') @project = Project.generate! User.add_to_project(@user, @project, Role.generate!(:permissions => [:view_issues, :edit_issues])) @@ -25,7 +26,7 @@ class JournalObserverTest < ActiveSupport::TestCase context "#after_create for 'issue_updated'" do should "should send a notification when configured as a notification" do Setting.notified_events = ['issue_updated'] - assert_difference('ActionMailer::Base.deliveries.size') do + assert_difference('ActionMailer::Base.deliveries.size', 2) do @issue.init_journal(@user) @issue.subject = "A change to the issue" assert @issue.save @@ -46,7 +47,7 @@ class JournalObserverTest < ActiveSupport::TestCase context "#after_create for 'issue_note_added'" do should "should send a notification when configured as a notification" do Setting.notified_events = ['issue_note_added'] - assert_difference('ActionMailer::Base.deliveries.size') do + assert_difference('ActionMailer::Base.deliveries.size', 2) do @issue.init_journal(@user, 'This update has a note') assert @issue.save end @@ -66,7 +67,7 @@ class JournalObserverTest < ActiveSupport::TestCase context "#after_create for 'issue_status_updated'" do should "should send a notification when configured as a notification" do Setting.notified_events = ['issue_status_updated'] - assert_difference('ActionMailer::Base.deliveries.size') do + assert_difference('ActionMailer::Base.deliveries.size', 2) do @issue.init_journal(@user) @issue.status = IssueStatus.generate! assert @issue.save @@ -89,7 +90,7 @@ class JournalObserverTest < ActiveSupport::TestCase context "#after_create for 'issue_priority_updated'" do should "should send a notification when configured as a notification" do Setting.notified_events = ['issue_priority_updated'] - assert_difference('ActionMailer::Base.deliveries.size') do + assert_difference('ActionMailer::Base.deliveries.size', 2) do @issue.init_journal(@user) @issue.priority = IssuePriority.generate! assert @issue.save diff --git a/test/unit/journal_test.rb b/test/unit/journal_test.rb index 4e79f4d6..b36bfbf1 100644 --- a/test/unit/journal_test.rb +++ b/test/unit/journal_test.rb @@ -45,7 +45,7 @@ class JournalTest < ActiveSupport::TestCase assert_equal 0, ActionMailer::Base.deliveries.size issue.reload issue.update_attribute(:subject, "New subject to trigger automatic journal entry") - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_create_should_not_send_email_notification_if_told_not_to diff --git a/test/unit/lib/chili_project/liquid_test.rb b/test/unit/lib/chili_project/liquid_test.rb new file mode 100644 index 00000000..fae3f396 --- /dev/null +++ b/test/unit/lib/chili_project/liquid_test.rb @@ -0,0 +1,241 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ +require File.expand_path('../../../../test_helper', __FILE__) + +class ChiliProject::LiquidTest < ActionView::TestCase + include ApplicationHelper + + context "hello_world tag" do + should "render 'Hello world!'" do + text = "{% hello_world %}" + assert_match /Hello world!/, textilizable(text) + end + end + + context "variables" do + should "render a list of the current variables" do + text = "{{ variables | to_list }}" + formatted = textilizable(text) + + assert formatted.include?('
        '), "Not in a list format" + assert formatted.include?('current_user') + end + + should "be lazily evaluated" do + text = ["{{ variables | to_list }}"] + text << '{% assign foo = \"bar\" %}' + text << "{{ variables | to_list }}" + formatted = textilizable(text.join("\n")) + + assert (formatted.scan('
          ').size == 2), "Not in a list format" + assert (formatted.scan('current_user').size == 2) + + assert (formatted.scan('foo').size == 1), "Not updated on added variable" + end + end + + context "child_pages tag" do + context "with no arg" do + context "and @project set" do + should "should list all wiki pages for the current project" do + @project = Project.generate!.reload + wiki = @project.wiki + top = WikiPage.generate!(:wiki => wiki, :title => 'Top', :content => WikiContent.new(:text => 'top page')) + child1 = WikiPage.generate!(:wiki => wiki, :title => 'Child1', :content => WikiContent.new(:text => 'child'), :parent => top) + + text = "{% child_pages %}" + formatted = textilizable(text) + + assert formatted.include?('pages-hierarchy') + assert formatted.include?('Child1') + assert formatted.include?('Top') + end + end + + context "and no @project set" do + should "render a warning" do + text = "{% child_pages %}" + formatted = textilizable(text) + + assert_match /flash error/, formatted + assert formatted.include?('With no argument, this tag can be called from projects only') + end + end + + end + + context "with a valid WikiPage arg" do + should "list all child pages for the wiki page" do + @project = Project.generate!.reload + wiki = @project.wiki + top = WikiPage.generate!(:wiki => wiki, :title => 'Top', :content => WikiContent.new(:text => 'top page')) + child1 = WikiPage.generate!(:wiki => wiki, :title => 'Child1', :content => WikiContent.new(:text => 'child'), :parent => top) + + text = "{% child_pages 'Top' %}" + formatted = textilizable(text) + + assert formatted.include?('pages-hierarchy') + assert formatted.include?('Child1') + assert !formatted.include?('Top') + end + + should "allow cross project listings even when outside of a project" do + project = Project.generate!.reload # project not an ivar + wiki = project.wiki + top = WikiPage.generate!(:wiki => wiki, :title => 'Top', :content => WikiContent.new(:text => 'top page')) + child1 = WikiPage.generate!(:wiki => wiki, :title => 'Child1', :content => WikiContent.new(:text => 'child'), :parent => top) + + text = "{% child_pages #{project.identifier}:'Top' %}" + formatted = textilizable(text) + + assert formatted.include?('pages-hierarchy') + assert formatted.include?('Child1') + assert !formatted.include?('Top') + end + + should "show the WikiPage when parent=1 is set" do + @project = Project.generate!.reload + wiki = @project.wiki + top = WikiPage.generate!(:wiki => wiki, :title => 'Top', :content => WikiContent.new(:text => 'top page')) + child1 = WikiPage.generate!(:wiki => wiki, :title => 'Child1', :content => WikiContent.new(:text => 'child'), :parent => top) + + text = "{% child_pages 'Top', 'parent=1' %}" + formatted = textilizable(text) + + assert formatted.include?('pages-hierarchy') + assert formatted.include?('Child1') + assert formatted.include?('Top') + + end + end + + context "with an invalid arg" do + should "render a warning" do + @project = Project.generate!.reload + wiki = @project.wiki + top = WikiPage.generate!(:wiki => wiki, :title => 'Top', :content => WikiContent.new(:text => 'top page')) + child1 = WikiPage.generate!(:wiki => wiki, :title => 'Child1', :content => WikiContent.new(:text => 'child'), :parent => top) + + text = "{% child_pages 1 %}" + formatted = textilizable(text) + + assert_match /flash error/, formatted + assert formatted.include?('No such page') + + end + end + end + + context "include tag" do + setup do + @project = Project.generate!.reload + @wiki = @project.wiki + @included_page = WikiPage.generate!(:wiki => @wiki, :title => 'Included_Page', :content => WikiContent.new(:text => 'included page [[Second_Page]]')) + + @project2 = Project.generate!.reload + @cross_project_page = WikiPage.generate!(:wiki => @project2.wiki, :title => 'Second_Page', :content => WikiContent.new(:text => 'second page')) + + end + + context "with a direct page" do + should "show the included page's content" do + text = "{% include 'Included Page' %}" + formatted = textilizable(text) + + assert formatted.include?('included page') + end + end + + context "with a cross-project page" do + should "show the included page's content" do + text = "{% include '#{@project2.identifier}:Second Page' %}" + formatted = textilizable(text) + + assert formatted.include?('second page') + end + end + + context "with recursive includes" do + should "render all child pages" do + parent = WikiPage.generate!(:wiki => @wiki, :title => 'Recursive_Parent', :content => WikiContent.new(:text => "h1. Parent\r\n{% include 'Recursive_Child1' %}")) + child1 = WikiPage.generate!(:wiki => @wiki, :title => 'Recursive_Child1', :content => WikiContent.new(:text => "h1. Child1\r\n{% include 'Recursive_Child2' %}")) + child2 = WikiPage.generate!(:wiki => @wiki, :title => 'Recursive_Child2', :content => WikiContent.new(:text => 'h1. Child2')) + + formatted = textilizable(parent.reload.text) + + assert_match /\s*Parent.*?<\/h1>/, formatted + assert_match /\s*Child1.*?<\/h1>/, formatted + assert_match /\s*Child2.*?<\/h1>/, formatted + + # make sure there are no dangling html result variables + assert_no_match /!!html_results.*?!!/, formatted + end + end + + context "with a circular inclusion" do + should "render a warning" do + circle_page = WikiPage.generate!(:wiki => @wiki, :title => 'Circle', :content => WikiContent.new(:text => '{% include "Circle2" %}')) + circle_page2 = WikiPage.generate!(:wiki => @wiki, :title => 'Circle2', :content => WikiContent.new(:text => '{% include "Circle" %}')) + formatted = textilizable(circle_page.reload.text) + + assert_match /flash error/, formatted + assert_match 'Circular inclusion detected', formatted + end + end + + context "with an invalid arg" do + should "render a warning" do + text = "{% include '404' %}" + formatted = textilizable(text) + + assert_match /flash error/, formatted + assert formatted.include?('No such page') + end + + should "HTML escape the error" do + text = "{% include '' %}" + formatted = textilizable(text) + + assert formatted.include?("No such page '<script>alert("foo"):</script>'") + end + end + + context "legacy" do + should "map to native include" do + text = "{{include(#{@project2.identifier}:Second_Page)}}" + formatted = textilizable(text) + + assert formatted.include?('second page') + end + end + end + + context "invalid input" do + should "be escaped" do + text = "{% --- something invalid %}\n" + text << '' + + formatted = textilizable(text) + assert_match '<script>alert("Hello")</script>', formatted + end + + should "have an error flash" do + text = "{% --- something invalid %}\n" + formatted = textilizable(text) + + assert_match /flash error/, formatted + assert_match '[Liquid Syntax Error]', formatted + end + end +end diff --git a/test/unit/lib/redmine/hook_test.rb b/test/unit/lib/redmine/hook_test.rb index 1a99675d..d4e5fbed 100644 --- a/test/unit/lib/redmine/hook_test.rb +++ b/test/unit/lib/redmine/hook_test.rb @@ -144,14 +144,14 @@ class Redmine::Hook::ManagerTest < ActiveSupport::TestCase issue = Issue.find(1) ActionMailer::Base.deliveries.clear - Mailer.deliver_issue_add(issue) + Mailer.deliver_issue_add(issue, 'jsmith@somenet.foo') mail = ActionMailer::Base.deliveries.last @hook_module.add_listener(TestLinkToHook) hook_helper.call_hook(:view_layouts_base_html_head) ActionMailer::Base.deliveries.clear - Mailer.deliver_issue_add(issue) + Mailer.deliver_issue_add(issue, 'jsmith@somenet.foo') mail2 = ActionMailer::Base.deliveries.last assert_equal mail.body, mail2.body diff --git a/test/unit/lib/redmine/i18n_test.rb b/test/unit/lib/redmine/i18n_test.rb index cfbee822..087bf807 100644 --- a/test/unit/lib/redmine/i18n_test.rb +++ b/test/unit/lib/redmine/i18n_test.rb @@ -18,6 +18,7 @@ class Redmine::I18nTest < ActiveSupport::TestCase include ActionView::Helpers::NumberHelper def setup + super @hook_module = Redmine::Hook end diff --git a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb index 6b045de1..d6f216cb 100644 --- a/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/bazaar_adapter_test.rb @@ -55,7 +55,7 @@ begin assert_equal version, @adapter.class.scm_command_version end else - puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" + should "Bazaar test repository NOT FOUND." def test_fake; assert true end end end diff --git a/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb index 9e0b796a..7e8f9ad4 100644 --- a/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb @@ -68,7 +68,7 @@ begin assert_equal version, @adapter.class.scm_command_version end else - puts "Cvs test repository NOT FOUND. Skipping unit tests !!!" + should "CVS test repository not found." def test_fake; assert true end end end diff --git a/test/unit/lib/redmine/scm/adapters/darcs_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/darcs_adapter_test.rb index 0e0e2af8..c6acbb89 100644 --- a/test/unit/lib/redmine/scm/adapters/darcs_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/darcs_adapter_test.rb @@ -54,7 +54,7 @@ begin end else - puts "Darcs test repository NOT FOUND. Skipping unit tests !!!" + should "Darcs test repository not found." def test_fake; assert true end end end diff --git a/test/unit/lib/redmine/scm/adapters/filesystem_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/filesystem_adapter_test.rb index 395c0850..54155f38 100644 --- a/test/unit/lib/redmine/scm/adapters/filesystem_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/filesystem_adapter_test.rb @@ -46,7 +46,7 @@ class FilesystemAdapterTest < ActiveSupport::TestCase assert_equal "TEST CAT\n", @adapter.cat("/test", 1) end else - puts "Filesystem test repository NOT FOUND. Skipping unit tests !!! See doc/RUNNING_TESTS." + should "Filesystem test repository not found." def test_fake; assert true end end end diff --git a/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb index be3c0714..acee7b90 100644 --- a/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/git_adapter_test.rb @@ -239,7 +239,7 @@ begin end else - puts "Git test repository NOT FOUND. Skipping unit tests !!!" + should "Git test repository not found." def test_fake; assert true end end end diff --git a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb index 105a8ff8..761b8968 100644 --- a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb @@ -353,7 +353,7 @@ begin assert File.exist?(@adapter.class.template_path_for(version)) end else - puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" + should "Mercurial test repository not found." def test_fake; assert true end end end diff --git a/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb index bb2f2fa5..57eaaf99 100644 --- a/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/subversion_adapter_test.rb @@ -47,7 +47,7 @@ begin end else - puts "Subversion test repository NOT FOUND. Skipping unit tests !!!" + should "Subversion test repository not found." def test_fake; assert true end end end diff --git a/test/unit/lib/redmine/themes_test.rb b/test/unit/lib/redmine/themes_test.rb index a22bc0c6..33b6ad86 100644 --- a/test/unit/lib/redmine/themes_test.rb +++ b/test/unit/lib/redmine/themes_test.rb @@ -14,6 +14,9 @@ require File.expand_path('../../../../test_helper', __FILE__) class Redmine::ThemesTest < ActiveSupport::TestCase + def setup + Redmine::Themes.rescan + end def test_themes themes = Redmine::Themes.themes diff --git a/test/unit/lib/redmine/wiki_formatting/macros_test.rb b/test/unit/lib/redmine/wiki_formatting/macros_test.rb index 207f2391..de0126c5 100644 --- a/test/unit/lib/redmine/wiki_formatting/macros_test.rb +++ b/test/unit/lib/redmine/wiki_formatting/macros_test.rb @@ -39,9 +39,6 @@ class Redmine::WikiFormatting::MacrosTest < HelperTestCase def test_macro_hello_world text = "{{hello_world}}" assert textilizable(text).match(/Hello world!/) - # escaping - text = "!{{hello_world}}" - assert_equal '

          {{hello_world}}

          ', textilizable(text) end def test_macro_include @@ -59,7 +56,7 @@ class Redmine::WikiFormatting::MacrosTest < HelperTestCase assert textilizable(text).match(/CookBook documentation/) text = "{{include(unknowidentifier:somepage)}}" - assert textilizable(text).match(/Page not found/) + assert textilizable(text).match(/No such page/) end def test_macro_child_pages diff --git a/test/unit/lib/redmine_test.rb b/test/unit/lib/redmine_test.rb index d5d73464..599a04d1 100644 --- a/test/unit/lib/redmine_test.rb +++ b/test/unit/lib/redmine_test.rb @@ -42,9 +42,7 @@ class RedmineTest < ActiveSupport::TestCase end def test_account_menu - assert_number_of_items_in_menu :account_menu, 4 - assert_menu_contains_item_named :account_menu, :login - assert_menu_contains_item_named :account_menu, :register + assert_number_of_items_in_menu :account_menu, 2 assert_menu_contains_item_named :account_menu, :my_account assert_menu_contains_item_named :account_menu, :logout end diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb index 2b9f8854..efa1177a 100644 --- a/test/unit/mail_handler_test.rb +++ b/test/unit/mail_handler_test.rb @@ -14,31 +14,16 @@ require File.expand_path('../../test_helper', __FILE__) class MailHandlerTest < ActiveSupport::TestCase - fixtures :users, :projects, - :enabled_modules, - :roles, - :members, - :member_roles, - :users, - :issues, - :issue_statuses, - :workflows, - :trackers, - :projects_trackers, - :versions, - :enumerations, - :issue_categories, - :custom_fields, - :custom_fields_trackers, - :custom_fields_projects, - :boards, - :messages + fixtures :all FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' def setup + Setting.bcc_recipients = '0' ActionMailer::Base.deliveries.clear Setting.notified_events = Redmine::Notifiable.all.collect(&:name) + Setting.mail_handler_confirmation_on_success = true + Setting.mail_handler_confirmation_on_failure = true end def test_add_issue @@ -67,6 +52,7 @@ class MailHandlerTest < ActiveSupport::TestCase assert !issue.description.match(/^Status:/i) assert !issue.description.match(/^Start Date:/i) # Email notification should be sent + assert_equal 2, ActionMailer::Base.deliveries.size mail = ActionMailer::Base.deliveries.last assert_not_nil mail assert mail.subject.include?('New ticket on a given project') @@ -289,7 +275,7 @@ class MailHandlerTest < ActiveSupport::TestCase # This email contains: 'Project: onlinestore' issue = submit_email('ticket_on_given_project.eml') assert issue.is_a?(Issue) - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_add_issue_note @@ -301,15 +287,6 @@ class MailHandlerTest < ActiveSupport::TestCase assert_equal 'Feature request', journal.issue.tracker.name end - test "reply to issue update (Journal) by message_id" do - journal = submit_email('ticket_reply_by_message_id.eml') - assert journal.is_a?(IssueJournal), "Email was a #{journal.class}" - assert_equal User.find_by_login('jsmith'), journal.user - assert_equal Issue.find(2), journal.journaled - assert_match /This is reply/, journal.notes - assert_equal 'Feature request', journal.issue.tracker.name - end - def test_add_issue_note_with_attribute_changes # This email contains: 'Status: Resolved' journal = submit_email('ticket_reply_with_status.eml') @@ -333,7 +310,7 @@ class MailHandlerTest < ActiveSupport::TestCase ActionMailer::Base.deliveries.clear journal = submit_email('ticket_reply.eml') assert journal.is_a?(Journal) - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 4, ActionMailer::Base.deliveries.size end def test_add_issue_note_should_not_set_defaults @@ -456,6 +433,116 @@ class MailHandlerTest < ActiveSupport::TestCase assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255] end + context "with an email that performs an unauthorized action" do + should "deliver an email error confirmation for an unknown user" do + ActionMailer::Base.deliveries.clear + issue = submit_email('ticket_by_unknown_user.eml') + assert_equal false, issue + + assert_equal 1, ActionMailer::Base.deliveries.size + mail = ActionMailer::Base.deliveries.last + assert_not_nil mail + assert mail.to.include?('john.doe@somenet.foo') + assert mail.subject.include?('Failed email submission: Ticket by unknown user') + assert mail.body.include?('You are not authorized to perform this action') + end + + should "deliver an email error confirmation for a user without permission" do + ActionMailer::Base.deliveries.clear + # Clear memberships for the sending user so they fail permission checks + Project.find(1).update_attributes(:is_public => false) + Member.all(:conditions => {:user_id => 2}).collect(&:destroy) + assert_no_difference('Journal.count') do + assert_equal false, submit_email('ticket_reply.eml') + end + + assert_equal 1, ActionMailer::Base.deliveries.size + mail = ActionMailer::Base.deliveries.last + assert_not_nil mail + assert mail.to.include?('jsmith@somenet.foo') + assert mail.subject.include?('Failed email submission: Re: Add ingredients categories') + assert mail.body.include?('You are not authorized to perform this action') + end + end + + context "with an email that is missing required information" do + should "deliver an email error confirmation to the sender for a missing project" do + ActionMailer::Base.deliveries.clear + issue = submit_email('ticket_with_attachment.eml') # No project set + assert_equal false, issue + + assert_equal 1, ActionMailer::Base.deliveries.size + mail = ActionMailer::Base.deliveries.last + assert_not_nil mail + assert mail.to.include?('jsmith@somenet.foo') + assert mail.subject.include?('Failed email submission: Ticket created by email with attachment') + assert mail.body.include?('There were errors with your email submission') + assert mail.body.include?('Unable to determine target project') + + end + + should "deliver an email error confirmation to the sender for a missing other attributes" do + # Add a required custom field to simulate the error + project = Project.find('onlinestore') + project.issue_custom_fields << IssueCustomField.generate(:name => 'Required Custom Field0', :is_required => true, :trackers => project.trackers) + project.save + + ActionMailer::Base.deliveries.clear + issue = submit_email('ticket_on_project_with_missing_information.eml') + assert_equal false, issue + + assert_equal 1, ActionMailer::Base.deliveries.size + mail = ActionMailer::Base.deliveries.last + assert_not_nil mail + assert mail.to.include?('jsmith@somenet.foo') + assert mail.subject.include?('Failed email submission: New ticket on a given project') + assert mail.body.include?('There were errors with your email submission') + assert mail.body.include?('Required Custom Field0 can\'t be blank') + end + end + + context "#receive_issue" do + should "deliver an email confirmation when configured" do + ActionMailer::Base.deliveries.clear + issue = submit_email('ticket_on_given_project.eml') + + assert_equal 2, ActionMailer::Base.deliveries.size + mail = ActionMailer::Base.deliveries.last + assert_not_nil mail + assert mail.subject.include?('[OnlineStore]'), "Project name missing" + assert mail.subject.include?('Confirmation of email submission: New ticket on a given project'), "Main subject missing" + assert mail.body.include?("/issues/#{issue.reload.id}"), "Link to issue missing" + end + end + + context "#receive_issue_reply" do + should "deliver an email confirmation when configured" do + journal = submit_email('ticket_reply.eml') + + assert_equal 4, ActionMailer::Base.deliveries.size + mail = ActionMailer::Base.deliveries.last + assert_not_nil mail + assert mail.subject.include?('[eCookbook]'), "Project name missing" + assert mail.subject.include?('Confirmation of email submission: Re: Add ingredients categories'), "Main subject missing" + assert mail.body.include?("/issues/2"), "Link to issue missing" + end + + end + + context "#receive_message_reply" do + should "deliver an email confirmation when configured" do + ActionMailer::Base.deliveries.clear + m = submit_email('message_reply.eml') + + assert_equal 4, ActionMailer::Base.deliveries.size + mail = ActionMailer::Base.deliveries.last + assert_not_nil mail + assert mail.subject.include?('[eCookbook]'), "Project name missing" + assert mail.subject.include?('Confirmation of email submission: Reply via email'), "Main subject missing" + assert mail.body.include?("/boards/1/topics/1"), "Link to message missing" + end + end + private def submit_email(filename, options={}) diff --git a/test/unit/mailer_test.rb b/test/unit/mailer_test.rb index 793c5e2b..341c62b8 100644 --- a/test/unit/mailer_test.rb +++ b/test/unit/mailer_test.rb @@ -19,10 +19,12 @@ class MailerTest < ActiveSupport::TestCase fixtures :all def setup + User.current = nil # Clear current user in case of tests setting it and leaking data ActionMailer::Base.deliveries.clear Setting.host_name = 'mydomain.foo' Setting.protocol = 'http' Setting.plain_text_mail = '0' + Setting.default_language = 'en' end def test_generated_links_in_emails @@ -30,7 +32,7 @@ class MailerTest < ActiveSupport::TestCase Setting.protocol = 'https' journal = Journal.find(2) - assert Mailer.deliver_issue_edit(journal) + assert Mailer.deliver_issue_edit(journal,'dlopper@somenet.foo') mail = ActionMailer::Base.deliveries.last assert_kind_of TMail::Mail, mail @@ -52,7 +54,7 @@ class MailerTest < ActiveSupport::TestCase Redmine::Utils.relative_url_root = '/rdm' journal = Journal.find(2) - assert Mailer.deliver_issue_edit(journal) + assert Mailer.deliver_issue_edit(journal,'dlopper@somenet.foo') mail = ActionMailer::Base.deliveries.last assert_kind_of TMail::Mail, mail @@ -77,7 +79,7 @@ class MailerTest < ActiveSupport::TestCase Redmine::Utils.relative_url_root = nil journal = Journal.find(2) - assert Mailer.deliver_issue_edit(journal) + assert Mailer.deliver_issue_edit(journal,'dlopper@somenet.foo') mail = ActionMailer::Base.deliveries.last assert_kind_of TMail::Mail, mail @@ -97,7 +99,7 @@ class MailerTest < ActiveSupport::TestCase def test_email_headers issue = Issue.find(1) - Mailer.deliver_issue_add(issue) + Mailer.deliver_issue_add(issue,'dlopper@somenet.foo') mail = ActionMailer::Base.deliveries.last assert_not_nil mail assert_equal 'bulk', mail.header_string('Precedence') @@ -107,7 +109,7 @@ class MailerTest < ActiveSupport::TestCase def test_plain_text_mail Setting.plain_text_mail = 1 journal = Journal.find(2) - Mailer.deliver_issue_edit(journal) + Mailer.deliver_issue_edit(journal,'dlopper@somenet.foo') mail = ActionMailer::Base.deliveries.last assert_equal "text/plain", mail.content_type assert_equal 0, mail.parts.size @@ -117,7 +119,7 @@ class MailerTest < ActiveSupport::TestCase def test_html_mail Setting.plain_text_mail = 0 journal = Journal.find(2) - Mailer.deliver_issue_edit(journal) + Mailer.deliver_issue_edit(journal,'dlopper@somenet.foo') mail = ActionMailer::Base.deliveries.last assert_equal 2, mail.parts.size assert mail.encoded.include?('href') @@ -141,21 +143,21 @@ class MailerTest < ActiveSupport::TestCase user.pref[:no_self_notified] = false user.pref.save User.current = user - Mailer.deliver_news_added(news.reload) - assert_equal 1, last_email.bcc.size + Mailer.deliver_news_added(news.reload, user.mail) + assert_equal 1, last_email.to.size # nobody to notify user.pref[:no_self_notified] = true user.pref.save User.current = user ActionMailer::Base.deliveries.clear - Mailer.deliver_news_added(news.reload) + Mailer.deliver_news_added(news.reload, user.mail) assert ActionMailer::Base.deliveries.empty? end def test_issue_add_message_id issue = Issue.find(1) - Mailer.deliver_issue_add(issue) + Mailer.deliver_issue_add(issue, 'dlopper@somenet.foo') mail = ActionMailer::Base.deliveries.last assert_not_nil mail assert_equal Mailer.message_id_for(issue), mail.message_id @@ -164,7 +166,7 @@ class MailerTest < ActiveSupport::TestCase def test_issue_edit_message_id journal = Journal.find(1) - Mailer.deliver_issue_edit(journal) + Mailer.deliver_issue_edit(journal, "jsmith@somenet.foo") mail = ActionMailer::Base.deliveries.last assert_not_nil mail assert_equal Mailer.message_id_for(journal), mail.message_id @@ -173,7 +175,7 @@ class MailerTest < ActiveSupport::TestCase def test_message_posted_message_id message = Message.find(1) - Mailer.deliver_message_posted(message) + Mailer.deliver_message_posted(message, "jsmith@somenet.foo") mail = ActionMailer::Base.deliveries.last assert_not_nil mail assert_equal Mailer.message_id_for(message), mail.message_id @@ -186,7 +188,7 @@ class MailerTest < ActiveSupport::TestCase def test_reply_posted_message_id message = Message.find(3) - Mailer.deliver_message_posted(message) + Mailer.deliver_message_posted(message, "jsmith@somenet.foo") mail = ActionMailer::Base.deliveries.last assert_not_nil mail assert_equal Mailer.message_id_for(message), mail.message_id @@ -204,154 +206,82 @@ class MailerTest < ActiveSupport::TestCase @issue = Issue.find(1) end - should "notify project members" do - assert Mailer.deliver_issue_add(@issue) - assert last_email.bcc.include?('dlopper@somenet.foo') - end - - should "not notify project members that are not allow to view the issue" do - Role.find(2).remove_permission!(:view_issues) - assert Mailer.deliver_issue_add(@issue) - assert !last_email.bcc.include?('dlopper@somenet.foo') - end - - should "notify issue watchers" do - user = User.find(9) - # minimal email notification options - user.pref[:no_self_notified] = '1' - user.pref.save - user.mail_notification = false - user.save - - Watcher.create!(:watchable => @issue, :user => user) - assert Mailer.deliver_issue_add(@issue) - assert last_email.bcc.include?(user.mail) - end - - should "not notify watchers not allowed to view the issue" do - user = User.find(9) - Watcher.create!(:watchable => @issue, :user => user) - Role.non_member.remove_permission!(:view_issues) - assert Mailer.deliver_issue_add(@issue) - assert !last_email.bcc.include?(user.mail) + should "send one email per recipient" do + assert Mailer.deliver_issue_add(@issue, 'dlopper@somenet.foo') + assert_equal 1, ActionMailer::Base.deliveries.length + assert_equal ['dlopper@somenet.foo'], last_email.to end end - # test mailer methods for each language def test_issue_add issue = Issue.find(1) - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert Mailer.deliver_issue_add(issue) - end + assert Mailer.deliver_issue_add(issue, 'dlopper@somenet.foo') end def test_issue_edit journal = Journal.find(1) - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert Mailer.deliver_issue_edit(journal) - end + assert Mailer.deliver_issue_edit(journal, "jsmith@somenet.foo") end def test_document_added document = Document.find(1) - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert Mailer.deliver_document_added(document) - end + assert Mailer.deliver_document_added(document, "jsmith@somenet.foo") end def test_attachments_added attachements = [ Attachment.find_by_container_type('Document') ] - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert Mailer.deliver_attachments_added(attachements) - end + assert Mailer.deliver_attachments_added(attachements, "jsmith@somenet.foo") end def test_version_file_added attachements = [ Attachment.find_by_container_type('Version') ] - assert Mailer.deliver_attachments_added(attachements) - assert_not_nil last_email.bcc - assert last_email.bcc.any? - assert_select_email do - assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files" - end + assert Mailer.deliver_attachments_added(attachements, "jsmith@somenet.foo") + assert_equal ["jsmith@somenet.foo"], last_email.to end def test_project_file_added attachements = [ Attachment.find_by_container_type('Project') ] - assert Mailer.deliver_attachments_added(attachements) - assert_not_nil last_email.bcc - assert last_email.bcc.any? - assert_select_email do - assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files" - end + assert Mailer.deliver_attachments_added(attachements, "jsmith@somenet.foo") + assert_equal ["jsmith@somenet.foo"], last_email.to end def test_news_added news = News.find(:first) - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert Mailer.deliver_news_added(news) - end + assert Mailer.deliver_news_added(news, "jsmith@somenet.foo") end def test_news_comment_added comment = Comment.find(2) - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert Mailer.deliver_news_comment_added(comment) - end + assert Mailer.deliver_news_comment_added(comment) end def test_message_posted message = Message.find(:first) - recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author} - recipients = recipients.compact.uniq - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert Mailer.deliver_message_posted(message) - end + assert Mailer.deliver_message_posted(message, "jsmith@somenet.foo") end def test_wiki_content_added content = WikiContent.find(1) - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert_difference 'ActionMailer::Base.deliveries.size' do - assert Mailer.deliver_wiki_content_added(content) - end + assert_difference 'ActionMailer::Base.deliveries.size' do + assert Mailer.deliver_wiki_content_added(content, "jsmith@somenet.foo") end end def test_wiki_content_updated content = WikiContent.find(1) - valid_languages.each do |lang| - Setting.default_language = lang.to_s - assert_difference 'ActionMailer::Base.deliveries.size' do - assert Mailer.deliver_wiki_content_updated(content) - end + assert_difference 'ActionMailer::Base.deliveries.size' do + assert Mailer.deliver_wiki_content_updated(content, "jsmith@somenet.foo") end end def test_account_information user = User.find(2) - valid_languages.each do |lang| - user.update_attribute :language, lang.to_s - user.reload - assert Mailer.deliver_account_information(user, 'pAsswORd') - end + assert Mailer.deliver_account_information(user, 'pAsswORd') end def test_lost_password token = Token.find(2) - valid_languages.each do |lang| - token.user.update_attribute :language, lang.to_s - token.reload - assert Mailer.deliver_lost_password(token) - end + assert Mailer.deliver_lost_password(token) end def test_register @@ -359,40 +289,33 @@ class MailerTest < ActiveSupport::TestCase Setting.host_name = 'redmine.foo' Setting.protocol = 'https' - valid_languages.each do |lang| - token.user.update_attribute :language, lang.to_s - token.reload - ActionMailer::Base.deliveries.clear - assert Mailer.deliver_register(token) - mail = ActionMailer::Base.deliveries.last - assert mail.body.include?("https://redmine.foo/account/activate?token=#{token.value}") - end + ActionMailer::Base.deliveries.clear + assert Mailer.deliver_register(token) + mail = ActionMailer::Base.deliveries.last + assert mail.body.include?("https://redmine.foo/account/activate?token=#{token.value}") end def test_test user = User.find(1) - valid_languages.each do |lang| - user.update_attribute :language, lang.to_s - assert Mailer.deliver_test(user) - end + assert Mailer.deliver_test(user) end def test_reminders Mailer.reminders(:days => 42) assert_equal 1, ActionMailer::Base.deliveries.size mail = ActionMailer::Base.deliveries.last - assert mail.bcc.include?('dlopper@somenet.foo') + assert mail.to.include?('dlopper@somenet.foo') assert mail.body.include?('Bug #3: Error 281 when updating a recipe') assert_equal '1 issue(s) due in the next 42 days', mail.subject end def test_reminders_for_users Mailer.reminders(:days => 42, :users => ['5']) - assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper + assert_equal 0, ActionMailer::Base.deliveries.size Mailer.reminders(:days => 42, :users => ['3']) - assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper + assert_equal 1, ActionMailer::Base.deliveries.size mail = ActionMailer::Base.deliveries.last - assert mail.bcc.include?('dlopper@somenet.foo') + assert mail.to.include?('dlopper@somenet.foo') assert mail.body.include?('Bug #3: Error 281 when updating a recipe') end diff --git a/test/unit/member_test.rb b/test/unit/member_test.rb index 5c3dfa73..4f96cd1c 100644 --- a/test/unit/member_test.rb +++ b/test/unit/member_test.rb @@ -89,6 +89,15 @@ class MemberTest < ActiveSupport::TestCase @member.destroy end end + + should "not prune watchers if the user still has permission to watch as a non-member" do + @member_on_public_project = Member.create!(:project => Project.find(1), :principal => User.find(9), :role_ids => [1, 2]) + + assert_no_difference 'Watcher.count' do + @member_on_public_project.destroy + end + end + end context "by updating roles" do diff --git a/test/unit/message_test.rb b/test/unit/message_test.rb index 0060170c..d4895b4d 100644 --- a/test/unit/message_test.rb +++ b/test/unit/message_test.rb @@ -14,7 +14,7 @@ require File.expand_path('../../test_helper', __FILE__) class MessageTest < ActiveSupport::TestCase - fixtures :projects, :roles, :members, :member_roles, :boards, :messages, :users, :watchers + fixtures :all def setup Setting.notified_events = ['message_posted'] @@ -147,7 +147,7 @@ class MessageTest < ActiveSupport::TestCase end test "email notifications for creating a message" do - assert_difference("ActionMailer::Base.deliveries.count") do + assert_difference("ActionMailer::Base.deliveries.count", 3) do message = Message.new(:board => @board, :subject => 'Test message', :content => 'Test message content', :author => @user) assert message.save end diff --git a/test/unit/news_test.rb b/test/unit/news_test.rb index 056b1cc3..2d00952e 100644 --- a/test/unit/news_test.rb +++ b/test/unit/news_test.rb @@ -22,6 +22,7 @@ class NewsTest < ActiveSupport::TestCase def setup + super end def test_create_should_send_email_notification @@ -30,7 +31,7 @@ class NewsTest < ActiveSupport::TestCase news = Project.find(:first).news.new(valid_news) assert news.save - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_should_include_news_for_projects_with_news_enabled diff --git a/test/unit/principal_drop_test.rb b/test/unit/principal_drop_test.rb new file mode 100644 index 00000000..96d02f15 --- /dev/null +++ b/test/unit/principal_drop_test.rb @@ -0,0 +1,29 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require File.expand_path('../../test_helper', __FILE__) + +class PrincipalDropTest < ActiveSupport::TestCase + def setup + @principal = Principal.generate! + @drop = @principal.to_liquid + end + + + context "#name" do + should "return the name" do + assert_equal @principal.name, @drop.name + end + end +end diff --git a/test/unit/project_drop_test.rb b/test/unit/project_drop_test.rb new file mode 100644 index 00000000..008c8452 --- /dev/null +++ b/test/unit/project_drop_test.rb @@ -0,0 +1,56 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require File.expand_path('../../test_helper', __FILE__) + +class ProjectDropTest < ActiveSupport::TestCase + def setup + @project = Project.generate! + User.current = @user = User.generate! + @role = Role.generate! + Member.generate!(:principal => @user, :project => @project, :roles => [@role]) + @drop = @project.to_liquid + end + + context "drop" do + should "be a ProjectDrop" do + assert @drop.is_a?(ProjectDrop), "drop is not a ProjectDrop" + end + end + + + context "#name" do + should "return the project name" do + assert_equal @project.name, @drop.name + end + end + + context "#identifier" do + should "return the project identifier" do + assert_equal @project.identifier, @drop.identifier + end + end + + should "only load an object if it's visible to the current user" do + assert User.current.logged? + assert @project.visible? + + @private_project = Project.generate!(:is_public => false) + + assert !@private_project.visible?, "Project is visible" + @private_drop = ProjectDrop.new(@private_project) + assert_equal nil, @private_drop.instance_variable_get("@object") + assert_equal nil, @private_drop.name + end +end diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index 944608bc..398a2001 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -104,6 +104,34 @@ class QueryTest < ActiveSupport::TestCase find_issues_with_query(query) end + def test_operator_date_equals + query = Query.new(:name => '_') + query.add_filter('due_date', '=', ['2011-07-10']) + assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement + find_issues_with_query(query) + end + + def test_operator_date_lesser_than + query = Query.new(:name => '_') + query.add_filter('due_date', '<=', ['2011-07-10']) + assert_match /issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement + find_issues_with_query(query) + end + + def test_operator_date_greater_than + query = Query.new(:name => '_') + query.add_filter('due_date', '>=', ['2011-07-10']) + assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?'/, query.statement + find_issues_with_query(query) + end + + def test_operator_date_between + query = Query.new(:name => '_') + query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10']) + assert_match /issues\.due_date > '2011-06-22 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement + find_issues_with_query(query) + end + def test_operator_in_more_than Issue.find(7).update_attribute(:due_date, (Date.today + 15)) query = Query.new(:project => Project.find(1), :name => '_') @@ -383,6 +411,50 @@ class QueryTest < ActiveSupport::TestCase assert !q.editable_by?(developer) end + context "#display_subprojects" do + setup do + Setting.display_subprojects_issues = 0 + User.current = nil + end + + should "not include subprojects when false" do + query = Query.new(:project => Project.find(1), :name => '_') + query.display_subprojects = false + + issues = find_issues_with_query(query) + issue_ids = issues.collect(&:id) + + assert issue_ids.include?(1), "Didn't find issue 1 on current project" + assert !issue_ids.include?(5), "Issue 5 on sub-project included when it shouldn't be" + assert !issue_ids.include?(6), "Issue 6 on a private sub-project included when it shouldn't be" + end + + should "include subprojects when true" do + query = Query.new(:project => Project.find(1), :name => '_') + query.display_subprojects = true + + issues = find_issues_with_query(query) + issue_ids = issues.collect(&:id) + + assert issue_ids.include?(1), "Didn't find issue 1 on current project" + assert issue_ids.include?(5), "Didn't find issue 5 on sub-project" + assert !issue_ids.include?(6), "Issue 6 on a private sub-project included when it shouldn't be" + end + + should "include private subprojects automatically when true" do + User.current = User.find(2) + query = Query.new(:project => Project.find(1), :name => '_') + query.display_subprojects = true + + issues = find_issues_with_query(query) + issue_ids = issues.collect(&:id) + + assert issue_ids.include?(1), "Didn't find issue 1 on current project" + assert issue_ids.include?(5), "Didn't find issue 5 on sub-project" + assert issue_ids.include?(6), "Didn't find issue 6 on a private sub-project" + end + end + context "#available_filters" do setup do @query = Query.new(:name => "_") diff --git a/test/unit/repository_bazaar_test.rb b/test/unit/repository_bazaar_test.rb index 39db6a8f..944a55eb 100644 --- a/test/unit/repository_bazaar_test.rb +++ b/test/unit/repository_bazaar_test.rb @@ -96,7 +96,7 @@ class RepositoryBazaarTest < ActiveSupport::TestCase assert_nil changeset.next end else - puts "Bazaar test repository NOT FOUND. Skipping unit tests !!!" + should "Bazaar test repository not found." def test_fake; assert true end end end diff --git a/test/unit/repository_cvs_test.rb b/test/unit/repository_cvs_test.rb index 910cb80e..13aa606b 100644 --- a/test/unit/repository_cvs_test.rb +++ b/test/unit/repository_cvs_test.rb @@ -98,7 +98,7 @@ class RepositoryCvsTest < ActiveSupport::TestCase assert_equal entries[2].lastrev.author, 'LANG' end else - puts "CVS test repository NOT FOUND. Skipping unit tests !!!" + should "CVS test repository not found." def test_fake; assert true end end end diff --git a/test/unit/repository_darcs_test.rb b/test/unit/repository_darcs_test.rb index 8f9d065e..183c02d4 100644 --- a/test/unit/repository_darcs_test.rb +++ b/test/unit/repository_darcs_test.rb @@ -65,7 +65,7 @@ class RepositoryDarcsTest < ActiveSupport::TestCase end end else - puts "Darcs test repository NOT FOUND. Skipping unit tests !!!" + should "Darcs test repository not found." def test_fake; assert true end end end diff --git a/test/unit/repository_filesystem_test.rb b/test/unit/repository_filesystem_test.rb index 15465af6..483a0434 100644 --- a/test/unit/repository_filesystem_test.rb +++ b/test/unit/repository_filesystem_test.rb @@ -45,7 +45,7 @@ class RepositoryFilesystemTest < ActiveSupport::TestCase end else - puts "Filesystem test repository NOT FOUND. Skipping unit tests !!! See doc/RUNNING_TESTS." + should "Filesystem test repository not found." def test_fake; assert true end end end diff --git a/test/unit/repository_git_test.rb b/test/unit/repository_git_test.rb index b0469d10..1d5bc9a1 100644 --- a/test/unit/repository_git_test.rb +++ b/test/unit/repository_git_test.rb @@ -305,7 +305,7 @@ class RepositoryGitTest < ActiveSupport::TestCase end end else - puts "Git test repository NOT FOUND. Skipping unit tests !!!" + should "Git test repository not found." def test_fake; assert true end end end diff --git a/test/unit/repository_mercurial_test.rb b/test/unit/repository_mercurial_test.rb index 98d37c1f..815d2809 100644 --- a/test/unit/repository_mercurial_test.rb +++ b/test/unit/repository_mercurial_test.rb @@ -273,7 +273,7 @@ class RepositoryMercurialTest < ActiveSupport::TestCase end end else - puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!" + should "Mercurial test repository not found." def test_fake; assert true end end end diff --git a/test/unit/repository_subversion_test.rb b/test/unit/repository_subversion_test.rb index af6b1ef1..fe92629b 100644 --- a/test/unit/repository_subversion_test.rb +++ b/test/unit/repository_subversion_test.rb @@ -183,7 +183,7 @@ class RepositorySubversionTest < ActiveSupport::TestCase assert_nil changeset.next end else - puts "Subversion test repository NOT FOUND. Skipping unit tests !!!" + should "Subversion test repository not found." def test_fake; assert true end end end diff --git a/test/unit/repository_test.rb b/test/unit/repository_test.rb index 7b2d34a3..117341f1 100644 --- a/test/unit/repository_test.rb +++ b/test/unit/repository_test.rb @@ -97,8 +97,8 @@ class RepositoryTest < ActiveSupport::TestCase assert_equal User.find_by_login('dlopper'), journal.user assert_equal 'Applied in changeset r2.', journal.notes - # 2 email notifications - assert_equal 2, ActionMailer::Base.deliveries.size + # 2 email notifications to 5 users + assert_equal 5, ActionMailer::Base.deliveries.size mail = ActionMailer::Base.deliveries.first assert_kind_of TMail::Mail, mail assert mail.subject.starts_with?("[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]") diff --git a/test/unit/tracker_drop_test.rb b/test/unit/tracker_drop_test.rb new file mode 100644 index 00000000..11f1e936 --- /dev/null +++ b/test/unit/tracker_drop_test.rb @@ -0,0 +1,34 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require File.expand_path('../../test_helper', __FILE__) + +class TrackerDropTest < ActiveSupport::TestCase + def setup + @tracker = Tracker.generate! + @drop = @tracker.to_liquid + end + + context "drop" do + should "be a TrackerDrop" do + assert @drop.is_a?(TrackerDrop), "drop is not a TrackerDrop" + end + end + + context "#name" do + should "return the name" do + assert_equal @tracker.name, @drop.name + end + end +end diff --git a/test/unit/watcher_test.rb b/test/unit/watcher_test.rb index e061fd2c..6fe87823 100644 --- a/test/unit/watcher_test.rb +++ b/test/unit/watcher_test.rb @@ -14,11 +14,7 @@ require File.expand_path('../../test_helper', __FILE__) class WatcherTest < ActiveSupport::TestCase - fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, - :issues, - :boards, :messages, - :wikis, :wiki_pages, - :watchers + fixtures :all def setup @user = User.find(1) @@ -125,4 +121,34 @@ class WatcherTest < ActiveSupport::TestCase assert Issue.find(1).watched_by?(user) assert !Issue.find(4).watched_by?(user) end + + context "group watch" do + setup do + @group = Group.generate! + Member.generate!(:project => Project.find(1), :roles => [Role.find(1)], :principal => @group) + @group.users << @user = User.find(1) + @group.users << @user2 = User.find(2) + end + + should "be valid" do + assert @issue.add_watcher(@group) + @issue.reload + assert @issue.watchers.detect { |w| w.user == @group } + end + + should "add all group members to recipients" do + @issue.watchers.delete_all + @issue.reload + + assert @issue.watcher_recipients.empty? + assert @issue.add_watcher(@group) + + @user.save + @issue.reload + assert @issue.watcher_recipients.include?(@user.mail) + assert @issue.watcher_recipients.include?(@user2.mail) + + end + + end end diff --git a/test/unit/wiki_content_test.rb b/test/unit/wiki_content_test.rb index 5c6444d2..c74cec19 100644 --- a/test/unit/wiki_content_test.rb +++ b/test/unit/wiki_content_test.rb @@ -44,7 +44,7 @@ class WikiContentTest < ActiveSupport::TestCase page.content = WikiContent.new(:text => "Content text", :author => User.find(1), :comments => "My comment") assert page.save - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_update @@ -64,7 +64,7 @@ class WikiContentTest < ActiveSupport::TestCase content.text = "My new content" assert content.save - assert_equal 1, ActionMailer::Base.deliveries.size + assert_equal 2, ActionMailer::Base.deliveries.size end def test_fetch_history diff --git a/test/unit/wiki_page_drop_test.rb b/test/unit/wiki_page_drop_test.rb new file mode 100644 index 00000000..159455aa --- /dev/null +++ b/test/unit/wiki_page_drop_test.rb @@ -0,0 +1,54 @@ +#-- encoding: UTF-8 +#-- copyright +# ChiliProject is a project management system. +# +# Copyright (C) 2010-2012 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require File.expand_path('../../test_helper', __FILE__) + +class WikiPageDropTest < ActiveSupport::TestCase + def setup + @project = Project.generate! + @wiki = Wiki.generate(:project => @project) + @wiki_page = WikiPage.generate!(:wiki => @wiki) + User.current = @user = User.generate! + @role = Role.generate!(:permissions => [:view_wiki_pages]) + Member.generate!(:principal => @user, :project => @project, :roles => [@role]) + @drop = @wiki_page.to_liquid + end + + context "drop" do + should "be a WikiPageDrop" do + assert @drop.is_a?(WikiPageDrop), "drop is not a WikiPageDrop" + end + end + + + context "#title" do + should "return the title of the wiki page" do + assert_equal @wiki_page.title, @drop.title + end + end + + should "only load an object if it's visible to the current user" do + assert User.current.logged? + assert @wiki_page.visible? + + @private_project = Project.generate!(:is_public => false) + @private_wiki = Wiki.generate!(:project => @private_project) + @private_wiki_page = WikiPage.generate!(:wiki => @private_wiki) + + assert !@private_wiki_page.visible?, "WikiPage is visible" + @private_drop = WikiPageDrop.new(@private_wiki_page) + assert_equal nil, @private_drop.instance_variable_get("@object") + assert_equal nil, @private_drop.title + end +end diff --git a/vendor/plugins/acts_as_journalized/lib/journal_formatter.rb b/vendor/plugins/acts_as_journalized/lib/journal_formatter.rb index 44c6bf36..9fd7d644 100644 --- a/vendor/plugins/acts_as_journalized/lib/journal_formatter.rb +++ b/vendor/plugins/acts_as_journalized/lib/journal_formatter.rb @@ -28,6 +28,7 @@ module JournalFormatter include CustomFieldsHelper include ActionView::Helpers::TagHelper include ActionView::Helpers::UrlHelper + include ActionView::Helpers::TextHelper include ActionController::UrlWriter extend Redmine::I18n @@ -122,6 +123,16 @@ module JournalFormatter [label, old_value, value] end + # Formats a detail to be used with a Journal diff + # + # Truncates the content. Adds a link to view a diff. + def format_html_diff_detail(key, label, old_value, value) + link = link_to(l(:label_more), {:controller => 'journals', :action => 'diff', :id => id, :field => key.to_s}, :class => 'lightbox-ajax') + old_value = truncate(old_value, :length => 80) + value = truncate(value, :length => 80) + " " + link + [old_value, value] + end + def property(detail) key = prop_key(detail) if key.start_with? "custom_values" @@ -186,6 +197,9 @@ module JournalFormatter unless no_html label, old_value, value = *format_html_detail(label, old_value, value) value = format_html_attachment_detail(key.sub("attachments", ""), value) if attachment_detail + if property(detail) == :attribute && key == "description" + old_value, value = *format_html_diff_detail(key, label, old_value, value) + end end unless value.blank? diff --git a/vendor/plugins/acts_as_watchable/lib/acts_as_watchable.rb b/vendor/plugins/acts_as_watchable/lib/acts_as_watchable.rb index 2b158383..9d99d2b7 100644 --- a/vendor/plugins/acts_as_watchable/lib/acts_as_watchable.rb +++ b/vendor/plugins/acts_as_watchable/lib/acts_as_watchable.rb @@ -42,7 +42,7 @@ module Redmine # Removes user from the watchers list def remove_watcher(user) - return nil unless user && user.is_a?(User) + return nil unless user && user.is_a?(Principal) Watcher.delete_all "watchable_type = '#{self.class}' AND watchable_id = #{self.id} AND user_id = #{user.id}" end @@ -64,7 +64,14 @@ module Redmine if respond_to?(:visible?) notified.reject! {|user| !visible?(user)} end - notified.collect(&:mail).compact + + notified.collect {|w| + if w.respond_to?(:mail) && w.mail.present? # Single mail + w.mail + elsif w.respond_to?(:mails) && w.mails.present? # Multiple mail + w.mails + end + }.flatten.compact end module ClassMethods; end