diff --git a/.gitignore b/.gitignore index d6d744a..9dbaf0c 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ # Ignore the test DB db/test.sqlite3 +db/test.sqlite3-journal # Ignore the SimpleCov output coverage @@ -64,4 +65,4 @@ # ignore auto-generated gettext files when running gettext:find config/locale/*/app.edit.po -config/locale/*/app.po.time_stamp \ No newline at end of file +config/locale/*/app.po.time_stamp diff --git a/app/controllers/orgs_controller.rb b/app/controllers/orgs_controller.rb index a8f9835..7b4397e 100644 --- a/app/controllers/orgs_controller.rb +++ b/app/controllers/orgs_controller.rb @@ -1,5 +1,5 @@ class OrgsController < ApplicationController - after_action :verify_authorized + after_action :verify_authorized, except: ['shibboleth_ds', 'shibboleth_ds_passthru'] respond_to :html ## @@ -44,6 +44,48 @@ end end + # GET /orgs/shibboleth_ds + # ---------------------------------------------------------------- + def shibboleth_ds + redirect_to root_path unless current_user.nil? + + @user = User.new + # Display the custom Shibboleth discovery service page. + @orgs = Org.joins(:identifier_schemes).where('identifier_schemes.name = ?', 'shibboleth').sort{|x,y| x.name <=> y.name } + + if @orgs.empty? + flash[:notice] = _('No institutions are currently registered.') + redirect_to user_shibboleth_omniauth_authorize_path + end + end + + # POST /orgs/shibboleth_ds + # ---------------------------------------------------------------- + def shibboleth_ds_passthru + if !params[:org_name].blank? + session['org_id'] = params[:org_name] + + scheme = IdentifierScheme.find_by(name: 'shibboleth') + shib_entity = OrgIdentifier.where(org_id: params[:org_name], identifier_scheme: scheme) + + if !shib_entity.empty? + # Force SSL + url = "#{request.base_url.gsub('http:', 'https:')}#{Rails.application.config.shibboleth_login}" + target = "#{user_shibboleth_omniauth_callback_url.gsub('http:', 'https:')}" + + #initiate shibboleth login sequence + redirect_to "#{url}?target=#{target}&entityID=#{shib_entity.first.identifier}" + else + flash[:notice] = _('Your institution does not seem to be properly configured.') + redirect_to shibboleth_ds_path + end + + else + flash[:notice] = _('Please choose an institution') + redirect_to shibboleth_ds_path + end + end + private def org_params params.require(:org).permit(:name, :abbreviation, :target_url, :is_other, :banner_text, :language_id, diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index b0f3465..9e7a7d5 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -16,14 +16,16 @@ def new @plan = Plan.new authorize @plan - + # Get all of the available funders and non-funder orgs @funders = Org.funders.joins(:templates).where(templates: {published: true}).uniq.sort{|x,y| x.name <=> y.name } @orgs = (Org.institutions + Org.managing_orgs).flatten.uniq.sort{|x,y| x.name <=> y.name } - + # Get the current user's org @default_org = current_user.org if @orgs.include?(current_user.org) - + + @is_test = params[:test] ||= false + respond_to :html end @@ -32,31 +34,34 @@ def create @plan = Plan.new authorize @plan - + @plan.principal_investigator = current_user.surname.blank? ? nil : "#{current_user.firstname} #{current_user.surname}" @plan.data_contact = current_user.email @plan.funder_name = plan_params[:funder_name] - + + @plan.visibility = (plan_params['visibility'].blank? ? Rails.application.config.default_plan_visibility : + plan_params[:visibility]) + # If a template hasn't been identified look for the available templates if plan_params[:template_id].blank? template_options(plan_params[:org_id], plan_params[:funder_id]) # Return the 'Select a template' section respond_to do |format| - format.js {} + format.js {} end - + # Otherwise create the plan else @plan.template = Template.find(plan_params[:template_id]) - + if plan_params[:title].blank? - @plan.title = current_user.firstname.blank? ? _('My Plan') + '(' + @plan.template.title + ')' : + @plan.title = current_user.firstname.blank? ? _('My Plan') + '(' + @plan.template.title + ')' : current_user.firstname + "'s" + _(" Plan") else @plan.title = plan_params[:title] end - + if @plan.save @plan.assign_creator(current_user) @@ -67,13 +72,13 @@ if !ggs.blank? then @plan.guidance_groups << ggs end default = Template.find_by(is_default: true) - + msg = "#{_('Plan was successfully created.')} " - + if !default.nil? && default == @plan.template # We used the generic/default template msg += _('This plan is based on the default template.') - + elsif !@plan.template.customization_of.nil? # We used a customized version of the the funder template msg += "#{_('This plan is based on the')} #{plan_params[:funder_name]} #{_('template with customisations by the')} #{plan_params[:org_name]}" @@ -82,9 +87,9 @@ # We used the specified org's or funder's template msg += "#{_('This plan is based on the')} #{@plan.template.org.name} template." end - + flash[:notice] = msg - + respond_to do |format| format.js { render js: "window.location='#{plan_url(@plan)}?editing=true'" } end @@ -93,7 +98,7 @@ # Something went wrong so report the issue to the user flash[:notice] = failed_create_error(@plan, 'Plan') respond_to do |format| - format.js {} + format.js {} end end end @@ -105,6 +110,7 @@ def show @plan = Plan.eager_load(params[:id]) authorize @plan + @editing = (!params[:editing].nil? && @plan.administerable_by?(current_user.id)) # Get all Guidance Groups applicable for the plan and group them by org @@ -115,8 +121,8 @@ @important_ggs = [] @important_ggs << [current_user.org, @all_ggs_grouped_by_org.delete(current_user.org)] @all_ggs_grouped_by_org.each do |org, ggs| - if org.organisation? - @important_ggs << [org,ggs] + if org.organisation? + @important_ggs << [org,ggs] @all_ggs_grouped_by_org.delete(org) end end @@ -157,8 +163,11 @@ @plan = Plan.find(params[:id]) authorize @plan + attrs = plan_params + attrs['visibility'] = Rails.application.config.default_plan_visibility if plan_params['visibility'].blank? + respond_to do |format| - if @plan.update_attributes(params[:plan]) + if @plan.update_attributes(attrs) format.html { redirect_to @plan, :editing => false, notice: _('Plan was successfully updated.') } format.json { head :no_content } else @@ -384,12 +393,42 @@ end end - + def duplicate + plan = Plan.find(params[:id]) + authorize plan + @plan = Plan.deep_copy(plan) + respond_to do |format| + if @plan.save + @plan.assign_creator(current_user) + flash[:notice] = 'Plan was successfully duplicated.' + format.js { render js: "window.location='#{plan_url(@plan)}?editing=true'" } + # format.html { redirect_to @plan, notice: _('Plan was successfully duplicated.') } + # format.json { head :no_content } + else + flash[:notice] = failed_create_error(@plan, 'Plan') + format.js {} + end + end + end + + # AJAX access to update the plan's visibility + # POST /plans/:id + def visibility + plan = Plan.find(params[:id]) + authorize plan + plan.visibility = "#{plan_params[:visibility]}" + if plan.save + render json: {code: 1, msg: ''} + else + render json: {code: 0, msg: _("Unable to change the plan's Test status")} + end + end + private - def plan_params - params.require(:plan).permit(:org_id, :org_name, :funder_id, :funder_name, :template_id, :title) + def plan_params + params.require(:plan).permit(:org_id, :org_name, :funder_id, :funder_name, :template_id, :title, :visibility) end # different versions of the same template have the same dmptemplate_id @@ -459,7 +498,7 @@ # -------------------------------------------------------------------------- def template_options(org_id, funder_id) @templates = [] - + if !org_id.blank? || !funder_id.blank? if funder_id.blank? # Load the org's template(s) @@ -468,7 +507,7 @@ @templates = Template.valid.where(published: true, org: org, customization_of: nil).to_a @msg = _("We found multiple DMP templates corresponding to the research organisation.") if @templates.count > 1 end - + else funder = Org.find(funder_id) # Load the funder's template(s) @@ -476,7 +515,7 @@ unless org_id.blank? org = Org.find(org_id) - + # Swap out any organisational cusotmizations of a funder template @templates.each do |tmplt| customization = Template.valid.find_by(published: true, org: org, customization_of: tmplt.dmptemplate_id) @@ -486,17 +525,17 @@ end end end - + msg = _("We found multiple DMP templates corresponding to the funder.") if @templates.count > 1 end end - + # If no templates were available use the generic templates if @templates.empty? @msg = _("Using the generic Data Management Plan") @templates << Template.find_by(is_default: true) end - + @templates = @templates.sort{|x,y| x.title <=> y.title } if @templates.count > 1 end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index a628929..78ecb7c 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -15,9 +15,9 @@ IdentifierScheme.all.each do |scheme| oauth = session["devise.#{scheme.name.downcase}_data"] unless session["devise.#{scheme.name.downcase}_data"].nil? end - + @user = User.new - + unless oauth.nil? # The OAuth provider could not be determined or there was no unique UID! if oauth[:provider].nil? || oauth[:uid].nil? @@ -26,7 +26,7 @@ else # Connect the new user with the identifier sent back by the OAuth provider flash[:notice] = t('identifier_schemes.new_login_success') - UserIdentifier.create(identifier_scheme: oauth[:provider].upcase, + UserIdentifier.create(identifier_scheme: oauth[:provider].upcase, identifier: oauth[:uid], user: @user) end @@ -36,9 +36,10 @@ # POST /resource def create #logger.debug "#{sign_up_params}" - if sign_up_params[:accept_terms] != "1" then + if sign_up_params[:accept_terms] != "on" then redirect_to after_sign_up_error_path_for(resource), alert: _('You must accept the terms and conditions to register.') else + sign_up_params[:accept_terms] = "1" # Convert the html 'on' to '1' existing_user = User.find_by_email(sign_up_params[:email]) if !existing_user.nil? # If email exists if (existing_user.password == "" || existing_user.password.nil?) && existing_user.confirmed_at.nil? # If user has not accepted invitation yet @@ -77,6 +78,7 @@ @identifier_schemes = IdentifierScheme.where(active: true).order(:name) @languages = Language.sorted_by_abbreviation do_update(require_password=needs_password?(current_user, params)) + update_preferences(current_user, params) else render(:file => File.join(Rails.root, 'public/403.html'), :status => 403, :layout => false) end @@ -164,21 +166,43 @@ end end + def update_preferences(current_user, params) + prefs = params[:prefs] + # Set all preferences to false + current_user.prefs.each do |key, value| + value.each_key do |k| + current_user.prefs[key][k] = false + end + end + + # Sets the preferences the user wants to true + if prefs + prefs.each_key do |key| + prefs[key].each_key do |k| + current_user.prefs[key.to_sym][k.to_sym] = true + end + end + end + + current_user.save + end + def sign_up_params - params.require(:user).permit(:email, :password, :password_confirmation, :firstname, :surname, - :accept_terms, :org_id, :other_organisation) + params.require(:user).permit(:email, :password, :password_confirmation, + :firstname, :surname, :recovery_email, + :accept_terms, :other_organisation, :prefs) end def update_params params.require(:user).permit(:firstname, :org_id, :other_organisation, - :language_id, :surname) + :language_id, :surname, :prefs) end def password_update params.require(:user).permit(:email, :firstname, :current_password, :org_id, :language_id, :password, :password_confirmation, :surname, - :other_organisation) + :other_organisation, :prefs) end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a4154d9..0572dd2 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -35,4 +35,5 @@ session[:locale] = nil set_gettext_locale #Method defined at controllers/application_controller.rb end + end \ No newline at end of file diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index d50e397..05796af 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -1,4 +1,5 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController + ## # Dynamically build a handler for each omniauth provider # ------------------------------------------------------------- @@ -59,9 +60,8 @@ redirect_to edit_user_registration_path end end + # ------------------------------------------------------------- - - def failure redirect_to root_path end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8c37586..80eb17a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -26,6 +26,7 @@ # Determines whether or not the URL path passed matches with the full path (including params) of the last URL requested. # see http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-fullpath for details + # --------------------------------------------------------------------------- def isActivePage(path) return request.fullpath() == path end diff --git a/app/helpers/plans_helper.rb b/app/helpers/plans_helper.rb index cf5a0c6..d14421a 100644 --- a/app/helpers/plans_helper.rb +++ b/app/helpers/plans_helper.rb @@ -1,74 +1,4 @@ module PlansHelper - - # Build variable column headings for the project list - # -------------------------------------------------------- - def plan_list_column_heading(column) - if column.kind_of?(Array) - heading = (column.first.kind_of?(String) ? column.first : _(' - ')) - - elsif column.kind_of?(String) - heading = column - - else - heading = _(' - ') - end - - klass = (['name', 'description'].include?(heading) ? :dmp_th_big : :dmp_th_small) - - content_tag(:th, t("helpers.project.columns.#{heading}"), class: klass) # parametrised YAML keys are no longer possible with gettext, TODO - end - - # Populate a variable column for the project list - # -------------------------------------------------------- - def plan_list_column_body(column, plan) - - col = (column.kind_of?(Array) ? column[0] : column) - - klass, content = case col - when 'name' - [ "dmp_td_big", link_to(plan.title, plan_path(plan), class: "dmp_table_link") ] - when 'owner' - user = plan.owner - text = if user.nil? - _(' - ') - elsif user == current_user - _('Me') - else - user.name - end - - [ "tmp_td_small", text ] - when 'shared' - shared_num = plan.users.count - 1 - text = shared_num > 0 ? (_('Yes') + " (with #{shared_num} people) ") : _('No') # Hardcoded strings are not internationalised - [ "dmp_td_small", text ] - when 'visibility' - text = if plan.visibility == 'organisationally_visible' - _('Organisational') - elsif plan.visibility == 'publicly_visible' - _('Public') - elsif plan.visibility == 'is_test' - _('Test/Practice') - elsif plan.visibility == 'privately_visible' - _('Private') - end - ["dmp_td_small", text ] - when 'last_edited' - [ "dmp_td_small", l(plan.latest_update.to_date, formats: :short) ] - when 'description' - [ "dmp_td_medium", (plan.try(col) || _(' - ')) ] - when 'non_link_name' - [ "dmp_td_big", plan.title ] - when 'template' - ["dmp_td_big", plan.template.title] - when 'organisation' - ["dmp_td_medium", plan.template.org.name] # This will trigger 2 queries for each function call, i.e. one for templates and another for orgs tables - else - [ "dmp_td_small", (plan.try(col) || _(' - ')) ] - end - content_tag(:td, content, class: klass) - end - # Shows whether the user has default, template-default or custom settings # for the given plan. # -------------------------------------------------------- @@ -87,4 +17,34 @@ content_tag(:small, t("helpers.settings.plans.#{key}")) end + # display the role of the user for a given plan + def display_role(role) + if role.creator? + access = _('Owner') + + else + case role.access_level + when 3 + access = _('Co-owner') + when 2 + access = _('Editor') + when 1 + access = _('Read only') + end + end + return access + end + + # display the visibility of the plan + def display_visibility(val) + case val + when 'organisationally_visible' + return _('My Inst.') + when 'publicly_visible' + return _('Public') + else + return _('Private') # Both Test and Private + end + end + end diff --git a/app/models/answer.rb b/app/models/answer.rb index d46ad59..ddce6f7 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -1,5 +1,5 @@ class Answer < ActiveRecord::Base - + ## # Associations belongs_to :question @@ -13,30 +13,41 @@ ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 - attr_accessible :text, :plan_id, :lock_version, :question_id, :user_id, :question_option_ids, + attr_accessible :text, :plan_id, :lock_version, :question_id, :user_id, :question_option_ids, :question, :user, :plan, :question_options, :notes, :note_ids, :id, :as => [:default, :admin] ## # Validations # validates :user, :plan, :question, presence: true -# +# # # Make sure there is only one answer per question! -# validates :question, uniqueness: {scope: [:plan], +# validates :question, uniqueness: {scope: [:plan], # message: I18n.t('helpers.answer.only_one_per_question')} -# +# # # The answer MUST have a text value if the question is NOT option based or a question_option if -# # it is option based. -# validates :text, presence: true, if: Proc.new{|a| +# # it is option based. +# validates :text, presence: true, if: Proc.new{|a| # (a.question.nil? ? false : !a.question.question_format.option_based?) # } -# validates :question_options, presence: true, if: Proc.new{|a| +# validates :question_options, presence: true, if: Proc.new{|a| # (a.question.nil? ? false : a.question.question_format.option_based?) # } -# +# # # Make sure the plan and question are associated with the same template! # validates :plan, :question, answer_for_correct_template: true + ## + # deep copy the given answer + # + # @params [Answer] question_option to be deep copied + # @return [Answer] the saved, copied answer + def self.deep_copy(answer) + answer_copy = answer.dup + answer_copy.save! + return answer_copy + end + # This method helps to decide if an answer option (:radiobuttons, :checkbox, etc ) in form views should be checked or not # Returns true if the given option_id is present in question_options, otherwise returns false def has_question_option(option_id) diff --git a/app/models/guidance.rb b/app/models/guidance.rb index 8ba321b..ca9ab48 100644 --- a/app/models/guidance.rb +++ b/app/models/guidance.rb @@ -129,5 +129,4 @@ # pass the list of viewable guidances to the view return all_viewable_guidances.flatten end - end diff --git a/app/models/guidance_group.rb b/app/models/guidance_group.rb index ee3e685..c4bc700 100644 --- a/app/models/guidance_group.rb +++ b/app/models/guidance_group.rb @@ -7,7 +7,7 @@ has_and_belongs_to_many :plans, join_table: :plans_guidance_groups # depricated but needed for migration "single_group_for_guidance" # has_and_belongs_to_many :guidances, join_table: "guidance_in_group" - + ## # Possibly needed for active_admin @@ -25,7 +25,7 @@ - + ## # Converts the current guidance group to a string containing the display name. @@ -49,7 +49,7 @@ # @return [Array] a list of guidance groups def self.guidance_groups_excluding(excluded_orgs) excluded_org_ids = Array.new - + if excluded_orgs.is_a?(Array) excluded_orgs.each do |org| excluded_org_ids << org.id @@ -57,7 +57,7 @@ else excluded_org_ids << excluded_orgs end - + return_orgs = GuidanceGroup.where("org_id NOT IN (?)", excluded_org_ids) return return_orgs end diff --git a/app/models/org.rb b/app/models/org.rb index 9abbe2e..807d4b3 100644 --- a/app/models/org.rb +++ b/app/models/org.rb @@ -19,6 +19,9 @@ has_and_belongs_to_many :token_permission_types, join_table: "org_token_permissions", unique: true + has_many :org_identifiers + has_many :identifier_schemes, through: :org_identifiers + ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 @@ -63,7 +66,7 @@ # What do they do? do they do it efficiently, and do we need them? # Determines the locale set for the organisation - # @return String or nil + # @return String or nil def get_locale if !self.language.nil? return self.language.abbreviation @@ -134,7 +137,7 @@ end return children end - + ## # returns a list of all guidance groups belonging to other organisations # @@ -169,7 +172,7 @@ end return organisations_list end - + ## # returns a list of all sections of a given version from this organisation and it's parents # @@ -186,7 +189,7 @@ return sections.where("version_id = ? ", version_id).all + parent.all_sections(version_id) end end - + ## # returns the guidance groups of this organisation and all of it's children # @@ -198,7 +201,7 @@ end return ggs end - + ## # returns the highest parent organisation in the tree # @@ -211,7 +214,7 @@ end end =end - + ## # returns all published templates belonging to the organisation # @@ -228,7 +231,7 @@ end end end - + private ## # checks size of logo and resizes if necessary @@ -239,5 +242,5 @@ self.logo = logo.thumb('x100') # resize height and maintain aspect ratio end end - end + end end diff --git a/app/models/org_identifier.rb b/app/models/org_identifier.rb new file mode 100644 index 0000000..872abe2 --- /dev/null +++ b/app/models/org_identifier.rb @@ -0,0 +1,14 @@ +class OrgIdentifier < ActiveRecord::Base + belongs_to :org + belongs_to :identifier_scheme + + # Should only be able to have one identifier per scheme! + validates_uniqueness_of :identifier_scheme, scope: :org + + validates :identifier, :org, :identifier_scheme, presence: {message: _("can't be blank")} + + def attrs=(hash) + # Make sure that the attributes are stored as a hash! + self.attrs = (hash.is_a?(Hash) ? hash : {attribute: hash.to_s}) + end +end \ No newline at end of file diff --git a/app/models/plan.rb b/app/models/plan.rb index f3974d2..b2691ff 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -28,7 +28,7 @@ ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 - attr_accessible :locked, :project_id, :version_id, :version, :plan_sections, + attr_accessible :locked, :project_id, :version_id, :version, :plan_sections, :exported_plans, :project, :title, :template, :grant_number, :identifier, :principal_investigator, :principal_investigator_identifier, :description, :data_contact, :funder_name, :visibility, :exported_plans, @@ -38,7 +38,7 @@ # public is a Ruby keyword so using publicly enum visibility: [:organisationally_visible, :publicly_visible, :is_test, :privately_visible] - #TODO: work out why this messes up plan creation : + #TODO: work out why this messes up plan creation : # briley: Removed reliance on :users, its really on :roles (shouldn't have a plan without at least a creator right?) It should be ok like this though now # validates :template, :title, presence: true @@ -208,7 +208,7 @@ end end end - + # Get guidance by theme from any guidance groups currently selected self.guidance_groups.each do |group| group.guidances.each do |guidance| @@ -290,6 +290,16 @@ return role.present? && role.administrator? end + ## + # determines if the plan is owned by the specified user + # + # @param user_id [Integer] the id for the user + # @return [Boolean] true if the user can administer the plan + def owned_by?(user_id) + user_id = user_id.id if user_id.is_a?(User) + role = roles.where(user_id: user_id).first + return role.present? && role.creator? + end ## # defines and returns the status of the plan @@ -367,7 +377,7 @@ format = rec.qformat answer = nil - if qa_map.has_key?(qid) + if qa_map.has_key?(qid) answer = qa_map[qid] end @@ -421,7 +431,7 @@ opt_hash[aid] << optid end - status["questions"].each_key do |questionid| + status["questions"].each_key do |questionid| answerid = status["questions"][questionid]["answer_id"] status["questions"][questionid]["answer_option_ids"] = opt_hash[answerid] end @@ -634,10 +644,10 @@ user_id = user_id.id if user_id.is_a?(User) add_user(user_id, true, true, true) end - -# TODO: commenting these out because they are overriden by private methods below, so this + +# TODO: commenting these out because they are overriden by private methods below, so this # is unreachable =begin ## @@ -694,7 +704,7 @@ end =end -# TODO: What are these used for? Should just be using self.org and self.org.funder? +# TODO: What are these used for? Should just be using self.org and self.org.funder? =begin ## # sets a new funder for the project @@ -948,7 +958,7 @@ self.dmptemplate.try(:organisation).try(:abbreviation) end =end - + # Returns the number of answered questions from the entire plan def num_answered_questions n = 0 @@ -1000,6 +1010,26 @@ ]).find(id) end + # deep copy the given plan and all of it's associations + # + # @params [Plan] plan to be deep copied + # @return [Plan] saved copied plan + def self.deep_copy(plan) + plan_copy = plan.dup + plan_copy.title = "Copy of " + plan.title + plan_copy.save! + plan.answers.each do |answer| + answer_copy = Answer.deep_copy(answer) + answer_copy.plan_id = plan_copy.id + answer_copy.save! + end + plan.guidance_groups.each do |guidance_group| + if guidance_group.present? + plan_copy.guidance_groups << GuidanceGroup.where(id: guidance_group.id).first + end + end + return plan_copy + end private @@ -1047,12 +1077,12 @@ end role.save - - # This is necessary because we're creating the associated record but not assigning it + + # This is necessary because we're creating the associated record but not assigning it # to roles. Auto-saving like this may be confusing when coding upstream in a controller, - # view or api. Should probably change this to: + # view or api. Should probably change this to: # self.roles << role - # and then let the save be called manually via: + # and then let the save be called manually via: # plan.save! #self.reload end @@ -1132,9 +1162,8 @@ # -------------------------------------------------------- def set_creation_defaults # Only run this before_validation because rails fires this before save/create - if self.id.nil? + if self.id.nil? self.title = "My plan (#{self.template.title})" if self.title.nil? && !self.template.nil? - self.visibility = 3 end end diff --git a/app/models/user.rb b/app/models/user.rb index c1350f6..9103a5e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,10 +6,15 @@ # Include default devise modules. Others available are: # :token_authenticatable, :confirmable, # :lockable, :timeoutable and :omniauthable - devise :invitable, :database_authenticatable, :registerable, :recoverable, - :rememberable, :trackable, :validatable, :omniauthable, + devise :invitable, :database_authenticatable, :registerable, :recoverable, + :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:shibboleth, :orcid] + + ## + # User Notification Preferences + serialize :prefs, Hash + ## # Associations has_and_belongs_to_many :perms, join_table: :users_perms @@ -26,14 +31,14 @@ q = "%#{query}%" conditions = t[:title].matches(q) columns = %i( - grant_number identifier description principal_investigator data_contact + grant_number identifier description principal_investigator data_contact ) columns = ['grant_number', 'identifier', 'description', 'principal_investigator', 'data_contact'] columns.each {|col| conditions = conditions.or(t[col].matches(q)) } self.where(conditions) end end - + has_many :user_identifiers has_many :identifier_schemes, through: :user_identifiers @@ -41,16 +46,20 @@ # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 #accepts_nested_attributes_for :roles - #attr_accessible :password_confirmation, :encrypted_password, :remember_me, - # :id, :email, :firstname, :last_login,:login_count, :orcid_id, - # :password, :shibboleth_id, :user_status_id, :surname, - # :user_type_id, :org_id, :skip_invitation, :other_organisation, + #attr_accessible :password_confirmation, :encrypted_password, :remember_me, + # :id, :email, :firstname, :last_login,:login_count, :orcid_id, + # :password, :shibboleth_id, :user_status_id, :surname, + # :user_type_id, :org_id, :skip_invitation, :other_organisation, # :accept_terms, :role_ids, :dmponline3, :api_token, - # :organisation, :language, :language_id, :org, :perms, + # :organisation, :language, :language_id, :org, :perms, # :confirmed_at, :org_id validates :email, email: true, allow_nil: true, uniqueness: {message: _("must be unique")} + + validates :prefs, presence: true + before_validation :create_default_preferences + ## # Scopes default_scope { includes(:org, :perms, :plans) } @@ -62,13 +71,13 @@ # What do they do? do they do it efficiently, and do we need them? # Determines the locale set for the user or the organisation he/she belongs - # @return String or nil + # @return String or nil def get_locale if !self.language.nil? return self.language.abbreviation elsif !self.org.nil? return self.org.get_locale - else + else return nil end end @@ -126,7 +135,7 @@ def organisation=(new_org) org_id = new_org.id unless new_org.nil? end - + ## # checks if the user is a super admin # if the user has any privelege which requires them to see the super admin page @@ -144,7 +153,7 @@ # # @return [Boolean] true if the user is an organisation admin def can_org_admin? - return self.can_grant_permissions? || self.can_modify_guidance? || + return self.can_grant_permissions? || self.can_modify_guidance? || self.can_modify_templates? || self.can_modify_org_details? end @@ -223,7 +232,7 @@ return org_type end =end - + ## # removes the api_token from the user # modifies the user model @@ -254,11 +263,11 @@ # -------------------------------------------------------------- def self.from_omniauth(auth) scheme = IdentifierScheme.find_by(name: auth.provider.downcase) - + if scheme.nil? throw Exception.new('Unknown OAuth provider: ' + auth.provider) else - joins(:user_identifiers).where('user_identifiers.identifier': auth.uid, + joins(:user_identifiers).where('user_identifiers.identifier': auth.uid, 'user_identifiers.identifier_scheme_id': scheme.id).first end end @@ -271,6 +280,30 @@ end + ## + # User Notification Preferences + def create_default_preferences + # Set the default preferences if this is a new user + self.prefs = self.class.create_default_preferences if self.id.nil? + end + + def self.create_default_preferences + default_prefs = { + users: { + permission_granted: true, + new_comment: true + }, + owners_and_coowners: { + visibility_changed: true, + user_added: true + }, + admins: { + template_published: true, + template_unpublished: true + } + } + end + # TODO: Remove this, its never called. # this generates a reset password link for a given user # which can then be sent to them with the appropriate host @@ -278,12 +311,12 @@ =begin def reset_password_link raw, enc = Devise.token_generator.generate(self.class, :reset_password_token) - self.reset_password_token = enc + self.reset_password_token = enc self.reset_password_sent_at = Time.now.utc save(validate: false) edit_user_password_path + '?reset_password_token=' + raw end =end - + end diff --git a/app/policies/org_policy.rb b/app/policies/org_policy.rb index 311d389..fd67d33 100644 --- a/app/policies/org_policy.rb +++ b/app/policies/org_policy.rb @@ -30,5 +30,4 @@ def templates? true end - end \ No newline at end of file diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb index aa99a2b..e6d6d15 100644 --- a/app/policies/plan_policy.rb +++ b/app/policies/plan_policy.rb @@ -39,15 +39,23 @@ def destroy? @plan.editable_by?(@user.id) end - + def status? @plan.readable_by?(@user.id) end - + def possible_templates? @plan.id.nil? end + def duplicate? + @plan.editable_by?(@user.id) + end + + def visibility? + @plan.administerable_by?(@user.id) + end + # TODO: These routes are no lonmger used =begin def section_answers? diff --git a/app/policies/role_policy.rb b/app/policies/role_policy.rb index cf2b691..00e2b79 100644 --- a/app/policies/role_policy.rb +++ b/app/policies/role_policy.rb @@ -17,6 +17,6 @@ end def destroy? - @role.plan.administerable_by?(@user.id) + @role.plan.owned_by?(@user.id) end end \ No newline at end of file diff --git a/app/views/contact_us/contacts/new.html.erb b/app/views/contact_us/contacts/new.html.erb index c4d2f30..4d704f0 100644 --- a/app/views/contact_us/contacts/new.html.erb +++ b/app/views/contact_us/contacts/new.html.erb @@ -8,64 +8,52 @@ organisation_url: Rails.configuration.branding[:organisation][:url], application_name: Rails.configuration.branding[:application][:name]} %>

-
+
<%= form_for @contact, url: contacts_path, html: {class: "roadmap-form"} do |f| %>
<% if ContactUs.require_name %> -
- <%= f.label :name, (_('Name') + content_tag(:abbr, "*", class: "required")).html_safe %> - <% if user_signed_in? then %> - <%= f.text_field :name, value: current_user.name(false) %> - <% else %> - <%= f.text_field :name %> - <% end %> - <% if f.object.errors[:name].present? %> -

<%= f.object.errors[:name].join(_(" and ")) %>

- <% end %> +
+ + />
<% end %> -
- <%= f.label :email, (_('Email') + content_tag(:abbr, "*", class: "required")).html_safe %> - <% if user_signed_in? then %> - <%= f.email_field :email, value: current_user.email %> - <% else %> - <%= f.email_field :email %> - <% end %> - <% if f.object.errors[:email].present? %> -

<%= f.object.errors[:email].join(_(" and ")) %>

- <% end %> +
+ + /> +
<% if ContactUs.require_subject %> -
- <%= f.label :subject, (_('Subject') + content_tag(:abbr, "*", class: "required")).html_safe %> - <%= f.text_field :subject %> - <% if f.object.errors[:subject].present? %> -

<%= f.object.errors[:subject].join(_(" and ")) %>

- <% end %> +
+ +
<% end %> -
- <%= f.label :message, (_('Message') + content_tag(:abbr, "*", class: "required")).html_safe %> - <%= f.text_area :message, rows: 10, class: "input-large" %> - <% if f.object.errors[:message].present? %> -

<%= f.object.errors[:message].join(_(" and ")) %>

- <% end %> +
+ +
<% if !user_signed_in? then %> -
- <%= t('helpers.security_check') %> - <%= recaptcha_tags %> +
+ +
+ <%= recaptcha_tags %> +
<% end %> -
+
<%= render partial: 'shared/accessible_submit_button', locals: {id: 'create_contact_submit', diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index f95b651..109a108 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -1,26 +1,33 @@ +<% javascript "devise/passwords/new.js" %> + +<% unless @user.errors[:email].empty? %> +

<%= _('The email address you entered is not registered.') %>

+<% end %> +

<%= t('helpers.forgot_password') %>

-
- <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> -
- - - - - -
<%= t("helpers.email") %> -

<%= t("helpers.send_password_info")%>

- -
<%= f.email_field :email, :autofocus => true , :style => "width:95%;" %>
- - -
-
- <%= f.submit t("helpers.send"), :class => "btn btn-primary", :id => "send_btn" %> -
- <% end %> +

<%= _('Please enter your email below and we will send you instructions on how to reset your password.') %>

-<%= render "devise/shared/links" %> - -
+
+ <%= form_for resource, as: 'user', url: user_password_path, html: {class: "password-reset roadmap-form", method: :post} do |f| %> + +
+
+ + + +
+ +
+ <%= render partial: 'shared/accessible_submit_button', + locals: {id: 'password-reset-button', + val: _('Send'), + disabled_initially: true, + classes: 'small-input-button', + tooltip: _('Enter a valid email.')} %> +
+
+ <% end %> +
+ diff --git a/app/views/devise/registrations/_password_details.html.erb b/app/views/devise/registrations/_password_details.html.erb new file mode 100644 index 0000000..8ac19a3 --- /dev/null +++ b/app/views/devise/registrations/_password_details.html.erb @@ -0,0 +1,37 @@ +
+ <%= _('If you would like to change your password please complete the following fields.') %> + +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/app/views/devise/registrations/_personal_details.html.erb b/app/views/devise/registrations/_personal_details.html.erb new file mode 100644 index 0000000..7a0bd0a --- /dev/null +++ b/app/views/devise/registrations/_personal_details.html.erb @@ -0,0 +1,122 @@ +
+ +
+ <%= _('You can edit any of the details below.') %> + + <%#= hidden_field_tag :unlink_flag, "false", id: "unlink_flag" %> + +
+ + + +
+ +
+ + +
+
+ + +
+ +
+ + + +
+ +
+ + <%= render partial: "shared/accessible_combobox", + locals: {name: "#{resource_name}[org_name]", + id: "#{resource_name}_org_name", + default_selection: @default_org, + models: @orgs, + attribute: 'name', + classes: 'fixed-width-large left-indent'} %> +
+ + <% if MANY_LANGUAGES %> +
+ <% lang = current_user.language.nil? ? FastGettext.default_locale : current_user.language.abbreviation %> + + +
+ <% end %> + + <% @identifier_schemes.each do |scheme| %> + <% + if scheme.name != 'shibboleth' || + (scheme.name == 'shibboleth' && Rails.application.config.shibboleth_enabled) + %> +
+ +
+ <%= render partial: 'external_identifier', + locals: {scheme: scheme, + id: current_user.identifier_for(scheme)} %> +
+
+ <% end %> + <% end %> + + <% unless @user.api_token.blank? %> +
+ <%= f.label :api_token, _('API token') %> +
<%= @user.api_token %>
+ + +
<%= link_to( _('How to use the API'), controller: "token_permission_types", action: "index")%>
+
+ <% end %> +
+ + + + + +
+ +
+

+ <%= _("Please note that your email address is used as your username. + If you change this, remember to use your new email address on sign in.") %> +

+
+ diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index a9c5b2f..7bd4782 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -1,132 +1,73 @@ -
+<% javascript "devise/registrations/edit.js" %> +

<%= _('Edit profile') %>

-

- <%= _("Please note that your email address is used as your username. If you change this, remember to use your new email address on sign in.") %> -

- <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {method: :put, class: "roadmap-form white_background"}) do |f| %> - -
- <%= _('You can edit any of the details below.') %> - - <%= hidden_field_tag :unlink_flag, "false", id: "unlink_flag" %> +
+ -
- <%= f.label :email, _('Email') + " *" %> - <%= f.email_field :email, as: :email, class: "input-medium has-tooltip", - "data-toggle": "tooltip", "data-trigger": "focus", - "title": _('Please enter your current password below when changing your email address.') %> +
+
+ <%= render partial: 'devise/registrations/personal_details', f: f %>
-
- <%= f.label :firstname, _('First name')+ " *" %> - <%= f.text_field :firstname, as: :string, - id: "first_time_login_firstname", - autofocus: true, - class: "input-medium has-tooltip", - "data-toggle" => "tooltip", - "data-trigger" => "focus" , - "title" => _('Please enter your first name.') %> -
-
- <%= f.label :surname, _('Last name') + " *" %> - <%= f.text_field :surname, as: :string, id: "first_time_login_surname", - class: "input-medium has-tooltip", "data-toggle" => "tooltip", - "data-trigger" => "focus" , - "title" => _('Please enter your surname or family name.') %> -
-
- <%= f.label :org, _('Organisation') + " *" %> - <%= render partial: "shared/accessible_combobox", - locals: {name: "#{resource_name}[org_name]", - id: "#{resource_name}_org_name", - default_selection: @default_org, - models: @orgs, - attribute: 'name', - classes: 'fixed-width-large'} %> -
- - <% if MANY_LANGUAGES %> -
- <% lang = current_user.language.nil? ? FastGettext.default_locale : current_user.language.abbreviation %> - <%= f.label :language, _('Language') %> - -
- <% end %> - <% @identifier_schemes.each do |scheme| %> -
- -
- <%= render partial: 'external_identifier', - locals: {scheme: scheme, - id: current_user.identifier_for(scheme)} %> -
-
- <% end %> - - <% unless @user.api_token.blank? %> -
- <%= f.label :api_token, _('API token') %><%= @user.api_token %> -
-
- <%= link_to( _('How to use the API'), controller: "token_permission_types", action: "index")%> -
- <% end %> - -
- - <%= _('If you would like to change your password please complete the following fields.') %> - -
- <%= f.label :current_password, _('Current password') %> - <%= f.password_field :current_password, as: :password, class: 'input-medium' %> + - -
- <%= f.label :password, _('New password') %> - <%= f.password_field :password, as: :password, autocomplete: "off", class: 'input-medium' %> -
- -
- <%= f.label :password_confirmation, _('Password confirmation') %> - <%= f.password_field :password_confirmation, as: :password, autocomplete: "off", - class: 'input-medium' %> -
- -
- - -
-
+ +
+
+ +
+ +
+ + <%= render partial: 'shared/accessible_submit_button', + locals: {id: 'update', + val: 'Save', + disabled_initially: true, + classes: 'small-input-button', + tooltip: _('Enter all of the required information above')} %> + <%= link_to 'Cancel', '#', style: 'text-decoration:none;' %> +
<% end %>
+ \ No newline at end of file diff --git a/app/views/guidance_groups/admin_edit.html.erb b/app/views/guidance_groups/admin_edit.html.erb index 77146d5..4edeb0f 100644 --- a/app/views/guidance_groups/admin_edit.html.erb +++ b/app/views/guidance_groups/admin_edit.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript 'admin.js' %>

@@ -16,6 +15,7 @@
+
<%= form_for(@guidance_group, url: admin_update_guidance_group_path(@guidance_group), html: {method: :put}) do |f| %> @@ -74,5 +74,6 @@
<% end %> + diff --git a/app/views/guidance_groups/admin_new.html.erb b/app/views/guidance_groups/admin_new.html.erb index 8ea92b8..922ab5d 100644 --- a/app/views/guidance_groups/admin_new.html.erb +++ b/app/views/guidance_groups/admin_new.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript 'admin.js' %>

@@ -16,6 +15,7 @@
+
<%= form_for :guidance_group, url: {action: "admin_create"} do |f| %>

@@ -48,5 +48,6 @@
<% end %> + diff --git a/app/views/guidance_groups/admin_show.html.erb b/app/views/guidance_groups/admin_show.html.erb index d73054d..318e8f9 100644 --- a/app/views/guidance_groups/admin_show.html.erb +++ b/app/views/guidance_groups/admin_show.html.erb @@ -1,5 +1,3 @@ -<%= stylesheet_link_tag "admin" %> -

<%= _('Guidance group') %> @@ -16,7 +14,7 @@
- +

@@ -58,5 +56,6 @@ <%= link_to _('Back'), :back, class: "btn cancel" %>
+ \ No newline at end of file diff --git a/app/views/guidances/admin_edit.html.erb b/app/views/guidances/admin_edit.html.erb index 1a04cc8..4162018 100644 --- a/app/views/guidances/admin_edit.html.erb +++ b/app/views/guidances/admin_edit.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript 'admin.js' %>

diff --git a/app/views/guidances/admin_index.html.erb b/app/views/guidances/admin_index.html.erb index 6010b35..ebd3bd6 100644 --- a/app/views/guidances/admin_index.html.erb +++ b/app/views/guidances/admin_index.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript "admin.js" %> diff --git a/app/views/guidances/admin_new.html.erb b/app/views/guidances/admin_new.html.erb index f77a3f6..cea5678 100644 --- a/app/views/guidances/admin_new.html.erb +++ b/app/views/guidances/admin_new.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript 'admin.js' %>

diff --git a/app/views/guidances/admin_show.html.erb b/app/views/guidances/admin_show.html.erb index 4742641..08054c1 100644 --- a/app/views/guidances/admin_show.html.erb +++ b/app/views/guidances/admin_show.html.erb @@ -1,5 +1,3 @@ -<%= stylesheet_link_tag "admin" %> -

<%= _('Guidance') %> diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 92bfeb2..ba09b54 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -1,56 +1,52 @@ -
-
-
- -

<%= _('Welcome.')%>

- <%= raw _('

%{application_name} has been jointly developed by the %{organisation_name} to help you write data management plans.

') % {:application_name => Rails.configuration.branding[:application][:name], :organisation_name => Rails.configuration.branding[:organisation][:name]} %> -
-

<%= _('Screencast on how to use %{application_name}') % {:application_name => Rails.configuration.branding[:application][:name]} %>

-
- -
-
-
-
-
-
- -
-
- <%= render :partial => 'shared/login_form' %> -
-
-
-
-
- - <%= _('Create account') %> -

- <%= _('New to %{application_name}? Create an account today.') % {:application_name => Rails.configuration.branding[:application][:name]} %> -

- -
-
-
- <%= render :partial => 'shared/register_form', locals: {extended: false} %> -
-
-
-
-
-
-
\ No newline at end of file +<%= javascript 'home/index.js' %> + +

<%= _('Welcome.')%>

+ +
+
+ <%= raw _('

%{application_name} has been jointly developed by the %{organisation_name} to help you write data management plans.

') % {:application_name => Rails.configuration.branding[:application][:name], :organisation_name => Rails.configuration.branding[:organisation][:name]} %> +
+

<%= _('Screencast on how to use %{application_name}') % {:application_name => Rails.configuration.branding[:application][:name]} %>

+
+ +
+
+ +
+
+ + +
+
+ <%= render :partial => 'shared/login_form' %> +
+ + +
+
+
+
+
+ diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index 4866e06..1e8c57a 100644 --- a/app/views/layouts/_footer.html.erb +++ b/app/views/layouts/_footer.html.erb @@ -1,43 +1,44 @@
- + +
+ +
+ +
<% if !user_signed_in? then %> - - - + + <% end %> diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 09bc946..4c6cbec 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -3,7 +3,7 @@
@@ -11,13 +11,12 @@ <%= render "layouts/branding" %>
- -
+ diff --git a/app/views/layouts/_navigation.html.erb b/app/views/layouts/_navigation.html.erb index 57bfe0b..c980f4f 100644 --- a/app/views/layouts/_navigation.html.erb +++ b/app/views/layouts/_navigation.html.erb @@ -27,7 +27,7 @@
  • > <%= link_to _('View plans'), plans_path %>
  • -
  • > +
  • > <%= link_to _('Create plan'), new_plan_path %>
  • <% else %> diff --git a/app/views/layouts/_signin_signout.html.erb b/app/views/layouts/_signin_signout.html.erb index ec99f37..e7c3d6c 100644 --- a/app/views/layouts/_signin_signout.html.erb +++ b/app/views/layouts/_signin_signout.html.erb @@ -1,6 +1,6 @@ + + \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 483511b..b569d9c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,84 +1,91 @@ - - <%= _('%{application_name}') % { :application_name => Rails.configuration.branding[:application][:name] } %> + + <%= _('%{application_name}') % { :application_name => Rails.configuration.branding[:application][:name] } %> <%= favicon_link_tag "favicon.ico" %> - - - + + + - <%= stylesheet_link_tag "application" %> - <%= javascript_include_tag "application"%> - - <%= javascript_include_tag "jquery.tablesorter"%> - + // Set the JS locale equal to the one Rails has + I18n.defaultLocale = "<%= I18n.default_locale %>"; + I18n.locale = "<%= I18n.locale %>"; + - + - <%= yield(:head) %> - <%= csrf_meta_tags %> + <%= yield(:head) %> + <%= csrf_meta_tags %> - - - - - - - + + + + + + + - - <%= render "layouts/header" %> - + + <%= render "layouts/header" %> + -
    - <% if notice %> -

    <%= raw notice %>

    - <% end %> - <% if alert %> -

    <%= raw alert %>

    - <% end %> - -
    - <%= yield %> -
    -
    - - - <%= render "layouts/footer" %> - - +
    + <% if notice || flash[:notice] %> +
    + + <%= raw notice %> +
    + <% end %> + <% if alert || flash[:alert] || flash[:error] %> +
    + + <%= raw alert %> +
    + <% end %> + +
    + <%= yield %> +
    +
    + + + <%= render "layouts/footer" %> + + diff --git a/app/views/orgs/_shibboleth_ds_list.html.erb b/app/views/orgs/_shibboleth_ds_list.html.erb new file mode 100644 index 0000000..d937b09 --- /dev/null +++ b/app/views/orgs/_shibboleth_ds_list.html.erb @@ -0,0 +1,14 @@ +<% letter = '' %> + +
    diff --git a/app/views/orgs/admin_edit.html.erb b/app/views/orgs/admin_edit.html.erb index adbc664..4530930 100644 --- a/app/views/orgs/admin_edit.html.erb +++ b/app/views/orgs/admin_edit.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript 'admin.js' %>

    @@ -8,6 +7,7 @@
    +
    <%= form_for(@org, url: admin_update_org_path(@org), html: { multipart: true, id: "edit_org_details", method: :put}) do |f| %> @@ -75,7 +75,7 @@ <%= link_to _('Cancel'), :back, class: 'btn btn-primary' %>
    <% end %> - +
    diff --git a/app/views/orgs/admin_show.html.erb b/app/views/orgs/admin_show.html.erb index a3da37b..7e8d278 100644 --- a/app/views/orgs/admin_show.html.erb +++ b/app/views/orgs/admin_show.html.erb @@ -1,5 +1,3 @@ -<%= stylesheet_link_tag "admin" %> -

    <%= _('Organisation details') %>

    @@ -9,7 +7,7 @@
    - +
    <%= _('Name') %>
    <% if @org.name.present? then %> @@ -82,5 +80,6 @@ <%= link_to _('Edit'), admin_edit_org_path(current_user.org), class: 'btn btn-primary'%>
    + \ No newline at end of file diff --git a/app/views/orgs/shibboleth_ds.html.erb b/app/views/orgs/shibboleth_ds.html.erb new file mode 100644 index 0000000..08d6005 --- /dev/null +++ b/app/views/orgs/shibboleth_ds.html.erb @@ -0,0 +1,90 @@ +<% javascript 'orgs/shibboleth_ds.js'%> + +

    Find your institution to sign in

    + +
    + <%= form_for 'shibboleth_ds', url: shibboleth_ds_path, html: {class: "shibboleth-ds-form roadmap-form"} do |f| %> +
    +
    + + + <% if @orgs.count <= 10 %> + + + <% else %> + <%= render partial: "shared/accessible_combobox", + locals: {name: 'org_name', + id: 'org_name', + default_selection: nil, + models: @orgs, + attribute: 'name', + classes: 'fixed-width-large'} %> + <% end %> + + <%= render partial: 'shared/accessible_submit_button', + locals: {id: 'submit-button', + val: _('Go'), + disabled_initially: true, + classes: 'small-input-button', + tooltip: _('Select an institution.'), + classes: 'inline left-indent'} %> + + <% if @orgs.count > 10 %> +

    + - <%= _('or') %> - +
    + <%= _('See the full list of participating institutions') %> +

    + <% end %> + +
    +
    + <% end %> + + + +
    + +

    + <%= _('Institution not a %{application_name} partner?') % {application_name: Rails.configuration.branding[:application][:name]} %> <%= _('Create an account with any email address')%> +

    +
    diff --git a/app/views/phases/admin_add.html.erb b/app/views/phases/admin_add.html.erb index 72a9a66..91cd45e 100644 --- a/app/views/phases/admin_add.html.erb +++ b/app/views/phases/admin_add.html.erb @@ -1,5 +1,4 @@ <%- model_class = Phase -%> -<%= stylesheet_link_tag "admin" %> <% javascript "admin.js" %>

    diff --git a/app/views/phases/admin_preview.html.erb b/app/views/phases/admin_preview.html.erb index 59ce2d0..95b1b6a 100644 --- a/app/views/phases/admin_preview.html.erb +++ b/app/views/phases/admin_preview.html.erb @@ -1,5 +1,4 @@ <%- model_class = Phase -%> -<%= stylesheet_link_tag "admin" %>

    <%= @template.title %> diff --git a/app/views/phases/admin_show.html.erb b/app/views/phases/admin_show.html.erb index 321ac60..9ccde64 100644 --- a/app/views/phases/admin_show.html.erb +++ b/app/views/phases/admin_show.html.erb @@ -1,5 +1,4 @@ <%- model_class = Phase -%> -<%= stylesheet_link_tag "admin" %> <% javascript 'admin.js' %> <%= tinymce %> diff --git a/app/views/plans/_available_templates.html.erb b/app/views/plans/_available_templates.html.erb index 59eb6fa..5d9f710 100644 --- a/app/views/plans/_available_templates.html.erb +++ b/app/views/plans/_available_templates.html.erb @@ -1,16 +1,20 @@ <%= _('Which DMP template would you like to use?') %> - +
    + - + +
    -
    - <%= _('We found multiple DMP templates corresponding to your funder.') %> +
    +
    +

    <%= _('We found multiple DMP templates corresponding to your funder.') %>

    +
    \ No newline at end of file diff --git a/app/views/shared/_login_form.html.erb b/app/views/shared/_login_form.html.erb index 5bf1449..8101bbd 100644 --- a/app/views/shared/_login_form.html.erb +++ b/app/views/shared/_login_form.html.erb @@ -1,36 +1,63 @@ -<%= form_for(resource, :as => "user", :url => user_session_path) do |f| %> -
      -
    • - <%= devise_error_messages! %> -
    • -
    • - <%= f.email_field :email, placeholder: (_('Email address') + ' *') , :as => :email%> -
    • -
    • - <%= f.password_field :password, placeholder: (_('Password') + ' *'), :as => :password %> -
    • - -
    • - <%= f.check_box :remember_me %><%= f.label :remember_me, :class => "remember_div" do %> - <%= _('Remember me') %> - <%end%> -
    • -
    • - <%= f.submit _('Sign in'), :class => "btn btn-primary" %> -
    • +

      <%= _('Sign in') %>

      + +<%= form_for resource, as: 'user', url: user_session_path, html: {class: "login-form roadmap-form"} do |f| %> +
      <% if Rails.application.config.shibboleth_enabled %> - <% if request.fullpath != "/users/sign_up?nosplash=true" && session[:shibboleth_data].nil? then%> -
    • - -
    • - <%else%> +

      <%= _('Sign in with') %>

      + +
      + + <% if request.fullpath != "/users/sign_up?nosplash=true" && session[:shibboleth_data].nil? then%> + <% target = (Rails.application.config.shibboleth_use_filtered_discovery_service ? shibboleth_ds_path : user_shibboleth_omniauth_authorize_path) %> + + <%else%> <%= f.hidden_field :shibboleth_id, :value => session[:shibboleth_data][:uid] %> - <%end%> + <%end%> + +
      + +

      - <%= _('or') %> -

      <% end %> -
    -<% end %> + + + +
    + + + + +
    + <%= f.label :remember_me, _('Remember email'), class: "remember_div checkbox-label" %> + <%= f.check_box :remember_me %> +
    +
    + + +
    + + + + +
    + + +
    +
    + +
    + <%= render partial: 'shared/accessible_submit_button', + locals: {id: 'sign-in-button', + val: _('Sign In'), + disabled_initially: true, + classes: 'small-input-button', + tooltip: _('Enter your email and password.')} %> + + +
    + +<% end %> \ No newline at end of file diff --git a/app/views/shared/_register_form.html.erb b/app/views/shared/_register_form.html.erb index ab2f719..d517bac 100644 --- a/app/views/shared/_register_form.html.erb +++ b/app/views/shared/_register_form.html.erb @@ -1,65 +1,65 @@ -<% javascript "shared/register_form.js" %> +

    <%= _('Create account') %>

    -<%= form_for(resource, :as => "user", :url => registration_path("user"), :htmlb => {:autocomplete =>"off"}, :html => {class: "roadmap-form"}) do |f| %> -
      -
    • - <%= devise_error_messages! %> -
    • -
    • - <%= f.email_field :email, placeholder: (_('Email') + ' *'), :as => :email, :class => 'text_field has-tooltip reg-input', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Please enter your email') %> - -
    • - <% if extended then %> - <% unless session[:shibboleth_data].nil? %> - <%= f.hidden_field :shibboleth_id, :value => session[:shibboleth_data][:uid] %> - <% end %> -
    • - <%= f.text_field :firstname, placeholder: _('First name'), :as => :string, :class => 'text_field' %> -
    • -
    • - <%= f.text_field :surname, placeholder: _('Last name'), :as => :string, :class => 'text_field' %> -
    • - <% end %> - <% if resource.user_identifiers.count > 0 %> - <% scheme = resource.user_identifiers.identifier_scheme.name %> - <%= f.hidden_field "user_identifiers[#{scheme}]", value: resource.user_identifiers.first.identifier%> - <% end %> -
    • - <%= collection_select(:user, :org_id, Org.where("parent_id IS NULL").order("sort_name ASC, name ASC"), :id, :name, {include_blank: _('Organisation')}, { :class => 'typeahead org_sign_up' }) %> -
    • - - <% other_organisations = Array.new %> - <% Org.where("parent_id IS ? AND is_other = ?", nil, true).each do |org| %> - <% other_organisations << org.id %> - <% end %> - -
    • - <%= f.password_field :password, placeholder: (_('Password') + ' *'), :autocomplete => "off", :as => :password, :class => 'text_field has-tooltip reg-input', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Your password must contain at least 8 characters.') %> - -
    • -
    • - <%= f.password_field :password_confirmation, placeholder: (_('Password confirmation') + ' *'), :as => :password, :class => 'text_field has-tooltip reg-input', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Your password must contain at least 8 characters.') %> - -
    • -
    • - <%= f.check_box :accept_terms %> <%= f.label :accept_terms, :class => "remember_div" do %> - <%= raw _(' I accept the terms and conditions *') %> - <%end%> -
    • -
    • - <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'sign_up_submit', - val: _('Create account'), - disabled_initially: true, - tooltip: _('You can not continue until you have filled in all of the required information.')} %> -
    • -
    +<%= form_for resource, as: 'user', url: registration_path("user"), html: {autocomplete: "off", class: "roadmap-form register-form"} do |f| %> +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + +
    + + + +
    + + +
    + + + +
    + + +
    + + + + +
    + + +
    +
    + + +
    + + +
    + +
    + <%= render partial: 'shared/accessible_submit_button', + locals: {id: 'register-button', + val: _('Create account'), + disabled_initially: true, + classes: 'small-input-button', + tooltip: _('Enter all of the information above')} %> +
    +
    <% end %> diff --git a/app/views/static_pages/about_us.html.erb b/app/views/static_pages/about_us.html.erb index 3b32241..cb49e42 100644 --- a/app/views/static_pages/about_us.html.erb +++ b/app/views/static_pages/about_us.html.erb @@ -1,67 +1,63 @@ - - -

    - <%= _('About %{application_name}') % { :application_name => Rails.configuration.branding[:application][:name] } %> + <%= _('About %{application_name}') % { :application_name => Rails.configuration.branding[:application][:name] } %>

    - -
    -
    -
    - <%= raw _("

    Funding bodies increasingly require their grant-holders to produce Data Management Plans(DMP), both during the bid-preparation stage and after funding has been secured. %{application_name} has been produced by the %{organisation_name} to help research teams respond to this requirement, and any expectations that their institution or others may apply.

    The %{organisation_abbreviation} worked closely with research funders and universities to produce a tool that assists researchers to produce an effective data management plan (DMP) to cater for the whole lifecycle of a project, from bid-preparation stage through to completion.


    How the tool works

    There are a number of templates within the tool that represent the requirements of different funders and institutions. Users are asked three questions at the outset so we can determine the appropriate template to display (e.g. the ESRC template when applying for an ESRC grant). Guidance is provided to help you interpret and answer the questions. This guidance is provided by researcher funders, universities and disciplines.


    Getting Started

    If you have an account please sign in and start creating or editing your DMP.

    If you do not have a %{application_name} account, click on 'Create account' on the homepage.

    Please visit the 'Help' page for guidance.


    Additional Information

    We are constantly improving the user interface and functionality of %{application_name}. If you would like to contribute with feedback and suggestions, please contact us by emailing dmponline@dcc.ac.uk. You can also report bugs and request new features directly on GitHub

    ") % { - :organisation_name => Rails.configuration.branding[:organisation][:name], - :organisation_abbreviation => Rails.configuration.branding[:organisation][:abbreviation], - :application_name => Rails.configuration.branding[:application][:name], - :application_url => Rails.configuration.branding[:application][:url] } %> -
    -
    - <%= raw _('

    %{application_name} stories from the %{organisation_abbreviation} website


    ') % { - :organisation_abbreviation => Rails.configuration.branding[:organisation][:abbreviation], - :application_name => Rails.configuration.branding[:application][:name] } %> - <% news_left = @dcc_news_feed.entries.count %> - <% @dcc_news_feed.entries.each do |entry| %> -

    <%= entry.title.sanitize %>

    - <% summary = entry.summary.sanitize %> - <% summary = summary.gsub(/\A])+>/, "") %> - <% summary = summary.gsub(/\A])+>/, "") %> - <% name = summary[/\A[\s\w]*([^,])/].strip %> - <% summary = summary.gsub(/\A.*div>/, "") %> - <% summary = summary.gsub(/

    .*\z/, "") %> -

    <%= raw summary.strip %>

    - <% author = entry.author.blank? ? 'anonymous' : entry.author %> -

    <%= _('By ') %> <%= author %> <%= _(' on ') %> <%= entry.published.strftime("%d/%m/%Y") %>

    -

    <%= _('Read more on the ') %><%= link_to 'DCC Website', entry.url %>

    -
    - - <%if news_left.to_i > 1 then %> -
    - <%else%> -
    - <%end%> - <% news_left = news_left - 1 %> - <% end %> -
    -
    -
    diff --git a/app/views/static_pages/help.html.erb b/app/views/static_pages/help.html.erb index a2ad20a..c982d16 100644 --- a/app/views/static_pages/help.html.erb +++ b/app/views/static_pages/help.html.erb @@ -1,34 +1,24 @@ - -

    <%= _('Help')%>

    - -
    -
    -
    +
    + + +
    +
    <%= raw _("

    When you login to %{application_name} you will be directed to the 'My plans' page. From here you can edit, share, export or delete any of your plans. You will also see plans that have been shared with you by others.

    Create a plan

    To create a plan, click the 'Create plan' button from the 'My plans' page or the top menu. Select options from the drop-down menus and tickboxes to determine what questions and guidance you should be presented with. Confirm your selection by clicking 'Yes, create plan'

    Write your plan

    The tabbed interface allows you to navigate through different functions when editing your plan.

    • - 'Plan details' includes basic administrative details, tells you what sets of questions and guidance your plan is based on and gives you an overview to the questions that you will be asked.
    • - The following tab(s) present the questions to answer. There may be more than one tab if your funder or university asks different sets of questions at different stages e.g. at grant application and post-award.
    • - The 'Share' tab allows you to invite others to read or contribute to your plan.
    • - The 'Export' tab allows you to download your plan in various formats. This may be useful if you need to submit your plan as part of a grant application.

    When viewing any of the question tabs, you will see the different sections of your plan displayed. Click into these in turn to answer the questions. You can format your responses using the text editing buttons.

    Guidance is displayed in the right-hand panel. Click the '+' symbol to view this.

    Remember to 'save' your responses before moving on.

    Share plans

    Insert the email address of any collaborators you would like to invite to read or edit your plan. Set the level of permissions you would like to grant them via the drop-down options and click to 'Add collaborator'

    Export plans

    From here you can download your plan in various formats. This may be useful if you need to submit your plan as part of a grant application. Choose what format you would like to view/download your plan in and click to export. When you login to %{application_name} you will be directed to the 'My plans' page. From here you can edit, share, export or delete any of your plans. You will also see plans that have been shared with you by others.

    ") % { :application_name => Rails.configuration.branding[:application][:name] } %>
    -
    +
    <%= raw _("

    Useful resources on Data Management Planning

    Example Data Management Plans

    • Technical plan submitted to the AHRC [PDF, 7 pages]
      A DMP submitted by a researcher from the University of Bristol, also including comments from the reviewers
    • Two social science DMPs [PDF, 7 pages]
      Example plans from researchers at the University of Leeds, shared as part of the Leeds RoaDMaP training materials
    • Health sciences DMP [PDF, 11 pages]
      Example DMP produced by the DATUM for Health RDM training project
    • Psychology DMP [PDF, 11 pages]
      A very detailed, fictional psychology DMP produced by the DMTpsych RDM training project, based on a seminal psychology experiment
    • UCSD Example Data Management Plans [webpage]
      Over 20 example plans submitted to the National Science Foundation (NSF) in the United States by academics at UC San Diego
    • Colorado School of Mines examples [webpage]
      A variety of US example DMPs from Mines and elsewhere
    • NSF data management plans [webpage]
      5 DMPs submitted to the NSF, shared by the DataOne initiative
    • Biology and chemistry DMPs [webpage]
      Three example DMPs from the USA shared by NECDMC, an instructional tool for teaching RDM to undergraduates, graduate students, and researchers in the health sciences, sciences and engineering.

    Useful guides on Research Data Management in general

    • Managing and Sharing Data: best practice for researchers [PDF, 36 pages]
      A guide by the UK Data Service covering a range of topics including data formats, documentaion, ethics, copyright and data sharing.
    • How to Cite Datasets and Link to Publications [PDF, 12 pages]
      A guide by the Digital Curation Centre giving practical guidelines on how to cite data and the different tools and infrastructure that can be used to support data citation.
    • How to License Research Data [PDF, 16 pages]
      A guide by the Digital Curation Centre that outlines different types of licenses, the pros and cons of each and how they can be applied.
    • How to Appraise and Select Research Data for Curation [PDF, 8 pages]
      A guide by ANDS and the Digital Curation Centre on how to select which data to keep for long-term preservation, sharing and reuse. The guide puts forward several criteria to aid selection decisions.
    • Research Data MANTRA [online resource]
      An online training course designed for researchers or others planning to manage digital data as part of the research process. The course includes a number of software practicals on using SPSS, R, ArcGIS and NVivo.
    ") % { :application_name => Rails.configuration.branding[:application][:name] } %>
    diff --git a/app/views/static_pages/roadmap.html.erb b/app/views/static_pages/roadmap.html.erb index 8ee60c7..c670f71 100644 --- a/app/views/static_pages/roadmap.html.erb +++ b/app/views/static_pages/roadmap.html.erb @@ -1,48 +1,38 @@ - -

    - <%= _('Future plans')%> + <%= _('Future plans')%>

    - -
    -
    -
    - <%= raw _("

    The %{organisation_abbreviation} are now collaborating to develop a joint codebase for Data Management Planning called DMP Roadmap. Both of our tools will be delivered using this in the future. We've agreed what features need to be included and are planning a few sprints to deliver these. The initial release will include all of the main priorities we already had flagged, including:

    • - APIs to create plans, extract guidance and generate statistics from %{application_name}
    • - Multi-lingual support so foreign language versions can be presented
    • - Locales to provide a refined set of content for particular countries or other contexts
    • - A lifecycle to indicate the status of DMPs and allow institutional access to plans
    • - Support for reviewing Data Management Plans

    %{application_name} has an active and growing user base, and we are grateful to the members who suggest ideas for new and improved features. If you would like to help shape our future plans, please join the user group. More information on how you can engage with us is available under the 'Get involved' tab.


    Current release

    The current version of %{application_name} is %{application_version}.

    The code is available on GitHub


    ") % { - :organisation_email => Rails.configuration.branding[:organisation][:email], - :organisation_abbreviation => Rails.configuration.branding[:organisation][:abbreviation], - :application_name => Rails.configuration.branding[:application][:name], - :application_url => Rails.configuration.branding[:application][:url], - :application_version => Rails.configuration.branding[:application][:version], - :application_release_notes_url => Rails.configuration.branding[:application][:release_notes_url] } %> -
    -
    - <%= raw _("

    %{application_name} is developed and maintained by the UK %{organisation_name}. We’re a small team, and are happy to collaborate with others. There are various ways you can get involved:

    Join the user group

    We run a listserv for the %{application_name} user group that you can request to join. We also host periodic meetings to consult on our plans. Being part of the user group gives you the opportunity to be informed about future developments and to provide feedback to help shape our plans.

    Our user group sessions are usually focused around a certain topic (e.g. fleshing out use cases for an API) so invites are sent based on your areas of expertise. It is helpful for us to know your role and interests to invite relevant people to each session. Please introduce yourself on the list and share your ideas.

    Notes from previous user group sessions are provided below:

    Please let us know your interests and share your ideas for future developments via the mailing list so the community as a whole can feedback on them.


    Customise %{application_name}

    %{application_name} can be customised by institutions and disciplines. You can add templates for users in your organisation and tailored guidance that explains local support and services. Example answers can also be offered to help users understand what to write in a Data Management Plan. To do this you’ll need to request admin access, so please email us on dmponline@dcc.ac.uk.

    Futher guidance on customising %{application_name} is available on the %{application_name} website.


    Contribute to the code

    %{application_name} is a Ruby on Rails application. The source code is made available under an MIT License. This permits others to reuse the code freely, but obligates you to share the source code for any extensions in the same way. Please inform us if you install an instance of %{application_name} and offer your contributions back to the community.

    If you install an instance of %{application_name} we require that you credit the %{organisation_abbreviation} as originators of the tool. We recommend that the acknowledgement takes the form of the %{application_name} logo with a link back to the %{organisation_abbreviation}-hosted version of the tool.

    We are willing to work with external developers to add new features to the tool. We are also open to delivering new features on a chargeable basis. If there are extensions you would like to see prioritised and have resource to support additional developer effort, please contact us on dmponline@dcc.ac.uk to negotiate terms.

    The code is available on GitHub

    Support our work

    We are impressed by the uptake of %{application_name} both in the UK and internationally and are really keen to hear how you are using the tool and promoting it in your context. We are aware that others have run training courses, developed guidance materials and advocated use of the tool. Please notify us of this as it helps to show impact.

    We are currently investigating options for revenue generation. This will help us serve the increased demand more effectively and safeguard the long-term sustainability of %{application_name}. Plans will be released for consultation soon but we also welcome your suggestions on how best to support our work.

    ") % { - organisation_name: Rails.configuration.branding[:organisation][:name], - organisation_email: Rails.configuration.branding[:organisation][:email], - organisation_url: Rails.configuration.branding[:organisation][:url], - organisation_abbreviation: Rails.configuration.branding[:organisation][:abbreviation], - application_name: Rails.configuration.branding[:application][:name], - application_url: Rails.configuration.branding[:application][:url], - application_user_group_subscription_url: Rails.configuration.branding[:application][:user_group_subscription_url] } %> -
    -
    +
    + + +
    +
    + <%= raw _("

    The %{organisation_abbreviation} are now collaborating to develop a joint codebase for Data Management Planning called DMP Roadmap. Both of our tools will be delivered using this in the future. We've agreed what features need to be included and are planning a few sprints to deliver these. The initial release will include all of the main priorities we already had flagged, including:

    • - APIs to create plans, extract guidance and generate statistics from %{application_name}
    • - Multi-lingual support so foreign language versions can be presented
    • - Locales to provide a refined set of content for particular countries or other contexts
    • - A lifecycle to indicate the status of DMPs and allow institutional access to plans
    • - Support for reviewing Data Management Plans

    %{application_name} has an active and growing user base, and we are grateful to the members who suggest ideas for new and improved features. If you would like to help shape our future plans, please join the user group. More information on how you can engage with us is available under the 'Get involved' tab.


    Current release

    The current version of %{application_name} is %{application_version}.

    The code is available on GitHub


    ") % { + :organisation_email => Rails.configuration.branding[:organisation][:email], + :organisation_abbreviation => Rails.configuration.branding[:organisation][:abbreviation], + :application_name => Rails.configuration.branding[:application][:name], + :application_url => Rails.configuration.branding[:application][:url], + :application_version => Rails.configuration.branding[:application][:version], + :application_release_notes_url => Rails.configuration.branding[:application][:release_notes_url] } %> +
    +
    + <%= raw _("

    %{application_name} is developed and maintained by the UK %{organisation_name}. We’re a small team, and are happy to collaborate with others. There are various ways you can get involved:

    Join the user group

    We run a listserv for the %{application_name} user group that you can request to join. We also host periodic meetings to consult on our plans. Being part of the user group gives you the opportunity to be informed about future developments and to provide feedback to help shape our plans.

    Our user group sessions are usually focused around a certain topic (e.g. fleshing out use cases for an API) so invites are sent based on your areas of expertise. It is helpful for us to know your role and interests to invite relevant people to each session. Please introduce yourself on the list and share your ideas.

    Notes from previous user group sessions are provided below:

    Please let us know your interests and share your ideas for future developments via the mailing list so the community as a whole can feedback on them.


    Customise %{application_name}

    %{application_name} can be customised by institutions and disciplines. You can add templates for users in your organisation and tailored guidance that explains local support and services. Example answers can also be offered to help users understand what to write in a Data Management Plan. To do this you’ll need to request admin access, so please email us on dmponline@dcc.ac.uk.

    Futher guidance on customising %{application_name} is available on the %{application_name} website.


    Contribute to the code

    %{application_name} is a Ruby on Rails application. The source code is made available under an MIT License. This permits others to reuse the code freely, but obligates you to share the source code for any extensions in the same way. Please inform us if you install an instance of %{application_name} and offer your contributions back to the community.

    If you install an instance of %{application_name} we require that you credit the %{organisation_abbreviation} as originators of the tool. We recommend that the acknowledgement takes the form of the %{application_name} logo with a link back to the %{organisation_abbreviation}-hosted version of the tool.

    We are willing to work with external developers to add new features to the tool. We are also open to delivering new features on a chargeable basis. If there are extensions you would like to see prioritised and have resource to support additional developer effort, please contact us on dmponline@dcc.ac.uk to negotiate terms.

    The code is available on GitHub

    Support our work

    We are impressed by the uptake of %{application_name} both in the UK and internationally and are really keen to hear how you are using the tool and promoting it in your context. We are aware that others have run training courses, developed guidance materials and advocated use of the tool. Please notify us of this as it helps to show impact.

    We are currently investigating options for revenue generation. This will help us serve the increased demand more effectively and safeguard the long-term sustainability of %{application_name}. Plans will be released for consultation soon but we also welcome your suggestions on how best to support our work.

    ") % { + organisation_name: Rails.configuration.branding[:organisation][:name], + organisation_email: Rails.configuration.branding[:organisation][:email], + organisation_url: Rails.configuration.branding[:organisation][:url], + organisation_abbreviation: Rails.configuration.branding[:organisation][:abbreviation], + application_name: Rails.configuration.branding[:application][:name], + application_url: Rails.configuration.branding[:application][:url], + application_user_group_subscription_url: Rails.configuration.branding[:application][:user_group_subscription_url] } %> +
    +
    \ No newline at end of file diff --git a/app/views/templates/admin_index.html.erb b/app/views/templates/admin_index.html.erb index 2acb25a..49dfcb3 100644 --- a/app/views/templates/admin_index.html.erb +++ b/app/views/templates/admin_index.html.erb @@ -1,5 +1,3 @@ -<%= stylesheet_link_tag "admin" %> -

    <%= _('Templates') %>

    diff --git a/app/views/templates/admin_new.html.erb b/app/views/templates/admin_new.html.erb index e180b4a..74223aa 100644 --- a/app/views/templates/admin_new.html.erb +++ b/app/views/templates/admin_new.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript "admin.js" %>

    diff --git a/app/views/templates/admin_template.html.erb b/app/views/templates/admin_template.html.erb index 77b956a..e8217ad 100644 --- a/app/views/templates/admin_template.html.erb +++ b/app/views/templates/admin_template.html.erb @@ -1,4 +1,3 @@ -<%= stylesheet_link_tag "admin" %> <% javascript 'admin.js' %>

    diff --git a/app/views/templates/admin_template_history.html.erb b/app/views/templates/admin_template_history.html.erb index 95897a8..c845d66 100644 --- a/app/views/templates/admin_template_history.html.erb +++ b/app/views/templates/admin_template_history.html.erb @@ -1,5 +1,3 @@ -<%= stylesheet_link_tag "admin" %> -

    <%= @template.title %>

    diff --git a/app/views/users/_notification_preferences.html.erb b/app/views/users/_notification_preferences.html.erb new file mode 100644 index 0000000..7dae888 --- /dev/null +++ b/app/views/users/_notification_preferences.html.erb @@ -0,0 +1,49 @@ +

    <%= link_to 'Select all', '#', id: 'select_all' %> | +<%= link_to 'Deselect all', '#', id: 'deselect_all' %>

    + +
    +

    All Users

    +
    + <%= check_box_tag 'prefs[users][permission_granted]', true, @user.prefs[:users][:permission_granted] %> + <%= label_tag 'prefs[users][permission_granted]', 'New permissions granted to me', :class => 'checkbox-label' %> +
    +
    + <%= check_box_tag 'prefs[users][new_comment]', true, @user.prefs[:users][:new_comment] %> + <%= label_tag 'prefs[users][new_comment]', 'A new comment has been added to my DMP', :class => 'checkbox-label' %> +
    +
    + +

    DMP owners and co-owners

    +
    + <%= check_box_tag 'prefs[owners_and_coowners][visibility_changed]', true, @user.prefs[:owners_and_coowners][:visibility_changed] %> + <%= label_tag 'prefs[owners_and_coowners][visibility_changed]', "My DMP's visibility has changed", :class => 'checkbox-label' %> +
    +
    + <%= check_box_tag 'prefs[owners_and_coowners][user_added]', true, @user.prefs[:owners_and_coowners][:user_added] %> + <%= label_tag 'prefs[owners_and_coowners][user_added]', 'I have been made a co-owner of a DMP', :class => 'checkbox-label' %> +
    + +
    +

    DMP administrators

    +
    + <%= check_box_tag 'prefs[admins][template_published]', true, @user.prefs[:admins][:template_published] %> + <%= label_tag 'prefs[admins][template_published]', 'An organisational template is published', :class => 'checkbox-label' %> +
    +
    + <%= check_box_tag 'prefs[admins][template_unpublished]', true, @user.prefs[:admins][:template_unpublished] %> + <%= label_tag 'prefs[admins][template_unpublished]', 'An organisational template is unpublished', :class => 'checkbox-label' %> +
    +
    + + + \ No newline at end of file diff --git a/app/views/users/admin_grant_permissions.html.erb b/app/views/users/admin_grant_permissions.html.erb index e15b781..9a7b4fd 100644 --- a/app/views/users/admin_grant_permissions.html.erb +++ b/app/views/users/admin_grant_permissions.html.erb @@ -1,5 +1,3 @@ -<%= stylesheet_link_tag "admin" %> -

    <%= _('Edit User Privileges') %>

    diff --git a/app/views/users/admin_index.html.erb b/app/views/users/admin_index.html.erb index 5a8a19c..0f81351 100644 --- a/app/views/users/admin_index.html.erb +++ b/app/views/users/admin_index.html.erb @@ -1,5 +1,3 @@ -<%= stylesheet_link_tag "admin" %> -

    <%= _('List of users') %>

    diff --git a/config/application.rb b/config/application.rb index 5693427..9b3ab8e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -67,22 +67,34 @@ config.assets.paths << Rails.root.join("lib", "assets", "videos") config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif *ico) config.assets.precompile += %w(*mp4 *webm *ogg *ogv *swf) - +# config.assets.precompile += %w(*.js *.scss *.css) + config.assets.precompile += %w(plans.js) config.assets.precompile += %w(projects.js) config.assets.precompile += %w(jquery.placeholder.js) config.assets.precompile += %w(jquery.tablesorter.js) - config.assets.precompile += %w(jquery-accessible-autocomplet-list-aria.js) config.assets.precompile += %w(export_configure.js) config.assets.precompile += %w(toolbar.js) config.assets.precompile += %w(admin.js) config.assets.precompile += %w(admin.css) - - config.assets.precompile += %w(roadmap-form.scss) - config.assets.precompile += %w(plans/new_plan.js) - config.assets.precompile += %w(contacts/new_contact.js) - config.assets.precompile += %w(shared/register_form.js) - config.assets.precompile += %w(answers/status.js) + + config.assets.precompile += %w(roadmap.css + roadmap-tabs.css + roadmap-form.css + roadmap-hacks.css) + + config.assets.precompile += %w(answers/status.js + devise/passwords/new.js + devise/registrations/edit.js + contacts/new_contact.js + home/index.js + orgs/shibboleth_ds.js + plans/edit.js + plans/index.js + plans/new.js + shared/login_form.js + shared/register_form.js + static_pages/utils.js) config.autoload_paths += %W(#{config.root}/lib) config.action_controller.include_all_helpers = true @@ -93,10 +105,18 @@ # Enable shibboleth as an alternative authentication method # Requires server configuration and omniauth shibboleth provider configuration # See config/initializers/devise.rb - config.shibboleth_enabled = true + config.shibboleth_enabled = false # Relative path to Shibboleth SSO Logout - config.shibboleth_logout_url = '/Shibboleth.sso/Logout?return=' + config.shibboleth_login = '/Shibboleth.sso/Login' + config.shibboleth_logout_url = '/Shibboleth.sso/Logout' + + # If this value is set to true your users will be presented with a list of orgs that have a + # shibboleth identifier in the orgs_identifiers table. If it is set to false (default), the user + # will be driven out to your federation's discovery service + # + # A super admin will also be able to associate orgs with their shibboleth entityIds if this is set to true + config.shibboleth_use_filtered_discovery_service = false # Active Record will no longer suppress errors raised in after_rollback or after_commit # in the next version. Devise appears to be using those callbacks. @@ -105,5 +125,12 @@ # Load Branded terminology (e.g. organization name, application name, etc.) config.branding = config_for(:branding).deep_symbolize_keys + + # The default visibility setting for new plans + # organisationally_visible - Any member of the user's org can view, export and duplicate the plan + # publicly_visibile - (NOT advisable because plans will show up in Public DMPs page by default) + # is_test - (NOT advisable because test plans are excluded from statistics) + # privately_visible - Only the owner and people they invite can access the plan + config.default_plan_visibility = 'organisationally_visible' end end diff --git a/config/routes.rb b/config/routes.rb index 2f4c08c..c2e9f61 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,26 +31,30 @@ end devise_for :users, controllers: { - registrations: "registrations", - passwords: 'passwords', - sessions: 'sessions', + registrations: "registrations", + passwords: 'passwords', + sessions: 'sessions', omniauth_callbacks: 'users/omniauth_callbacks'} do - + get "/users/sign_out", :to => "devise/sessions#destroy" end - + # WAYFless access point - use query param idp #get 'auth/shibboleth' => 'users/omniauth_shibboleth_request#redirect', :as => 'user_omniauth_shibboleth' #get 'auth/shibboleth/assoc' => 'users/omniauth_shibboleth_request#associate', :as => 'user_shibboleth_assoc' #post '/auth/:provider/callback' => 'sessions#oauth_create' - + # fix for activeadmin signout bug devise_scope :user do delete '/users/sign_out' => 'devise/sessions#destroy' end delete '/users/identifiers/:id', to: 'user_identifiers#destroy', as: 'destroy_user_identifier' - + + get '/orgs/shibboleth', to: 'orgs#shibboleth_ds', as: 'shibboleth_ds' + get '/orgs/shibboleth/:org_name', to: 'orgs#shibboleth_ds_passthru' + post '/orgs/shibboleth', to: 'orgs#shibboleth_ds_passthru' + #ActiveAdmin.routes(self) #organisation admin area @@ -77,10 +81,10 @@ get "public_plans" => 'static_pages#public_plans' get "public_export/:id" => 'static_pages#public_export', as: 'public_export' get "existing_users" => 'existing_users#index' - + #post 'contact_form' => 'contacts', as: 'localized_contact_creation' #get 'contact_form' => 'contacts#new', as: 'localized_contact_form' - + resources :orgs, :path => 'org/admin', only: [] do member do get 'children' @@ -201,8 +205,10 @@ get 'section_answers' get 'share' get 'show_export' + post 'duplicate' get 'export' post 'invite' + post 'visibility', constraints: {format: [:json]} end collection do diff --git a/db/migrate/20170516184429_add_recovery_email_to_users.rb b/db/migrate/20170516184429_add_recovery_email_to_users.rb new file mode 100644 index 0000000..09034ac --- /dev/null +++ b/db/migrate/20170516184429_add_recovery_email_to_users.rb @@ -0,0 +1,5 @@ +class AddRecoveryEmailToUsers < ActiveRecord::Migration + def change + add_column :users, :recovery_email, :string + end +end diff --git a/db/migrate/20170606215136_add_preferences_to_users.rb b/db/migrate/20170606215136_add_preferences_to_users.rb new file mode 100644 index 0000000..f570a76 --- /dev/null +++ b/db/migrate/20170606215136_add_preferences_to_users.rb @@ -0,0 +1,10 @@ +class AddPreferencesToUsers < ActiveRecord::Migration + + def self.up + add_column :users, :prefs, :binary + end + + def self.down + remove_column :users, :prefs + end +end diff --git a/db/migrate/20170607154433_create_org_identifiers.rb b/db/migrate/20170607154433_create_org_identifiers.rb new file mode 100644 index 0000000..7ec175d --- /dev/null +++ b/db/migrate/20170607154433_create_org_identifiers.rb @@ -0,0 +1,12 @@ +class CreateOrgIdentifiers < ActiveRecord::Migration + def change + create_table :org_identifiers do |t| + t.string :identifier + t.string :attrs + t.timestamps + end + + add_reference :org_identifiers, :org, foreign_key: true + add_reference :org_identifiers, :identifier_scheme, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index fd97cce..a531b90 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,71 +11,75 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170428083711) do - - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" +ActiveRecord::Schema.define(version: 20170607154433) do create_table "annotations", force: :cascade do |t| - t.integer "question_id" - t.integer "org_id" - t.text "text" - t.integer "type", default: 0, null: false + t.integer "question_id", limit: 4 + t.integer "org_id", limit: 4 + t.text "text", limit: 65535 + t.integer "type", limit: 4, default: 0, null: false t.datetime "created_at" t.datetime "updated_at" end + add_index "annotations", ["org_id"], name: "fk_rails_aca7521f72", using: :btree + add_index "annotations", ["question_id"], name: "fk_rails_0e08e753b6", using: :btree + create_table "answers", force: :cascade do |t| - t.text "text" - t.integer "plan_id" - t.integer "user_id" - t.integer "question_id" + t.text "text", limit: 65535 + t.integer "plan_id", limit: 4 + t.integer "user_id", limit: 4 + t.integer "question_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "lock_version", default: 0 + t.integer "lock_version", limit: 4, default: 0 end + add_index "answers", ["plan_id"], name: "fk_rails_84a6005a3e", using: :btree + add_index "answers", ["question_id"], name: "fk_rails_3d5ed4418f", using: :btree + add_index "answers", ["user_id"], name: "fk_rails_584be190c2", using: :btree + create_table "answers_question_options", id: false, force: :cascade do |t| - t.integer "answer_id", null: false - t.integer "question_option_id", null: false + t.integer "answer_id", limit: 4, null: false + t.integer "question_option_id", limit: 4, null: false end add_index "answers_question_options", ["answer_id", "question_option_id"], name: "answer_question_option_index", using: :btree add_index "answers_question_options", ["question_option_id", "answer_id"], name: "question_option_answer_index", using: :btree create_table "exported_plans", force: :cascade do |t| - t.integer "plan_id" - t.integer "user_id" - t.string "format" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "phase_id" + t.integer "plan_id", limit: 4 + t.integer "user_id", limit: 4 + t.string "format", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "phase_id", limit: 4 end create_table "file_types", force: :cascade do |t| - t.string "name" - t.string "icon_name" - t.integer "icon_size" - t.string "icon_location" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "name", limit: 255 + t.string "icon_name", limit: 255 + t.integer "icon_size", limit: 4 + t.string "icon_location", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "file_uploads", force: :cascade do |t| - t.string "name" - t.string "title" - t.text "description" - t.integer "size" + t.string "name", limit: 255 + t.string "title", limit: 255 + t.text "description", limit: 65535 + t.integer "size", limit: 4 t.boolean "published" - t.string "location" - t.integer "file_type_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "location", limit: 255 + t.integer "file_type_id", limit: 4 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "friendly_id_slugs", force: :cascade do |t| - t.string "slug", null: false - t.integer "sluggable_id", null: false + t.string "slug", limit: 255, null: false + t.integer "sluggable_id", limit: 4, null: false t.string "sluggable_type", limit: 40 t.datetime "created_at" end @@ -85,298 +89,343 @@ add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree create_table "guidance_groups", force: :cascade do |t| - t.string "name" - t.integer "org_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "name", limit: 255 + t.integer "org_id", limit: 4 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.boolean "optional_subset" t.boolean "published" end + add_index "guidance_groups", ["org_id"], name: "fk_rails_819c1dbbc7", using: :btree + create_table "guidances", force: :cascade do |t| - t.text "text" - t.integer "guidance_group_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "question_id" + t.text "text", limit: 65535 + t.integer "guidance_group_id", limit: 4 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "question_id", limit: 4 t.boolean "published" end + add_index "guidances", ["guidance_group_id"], name: "fk_rails_20d29da787", using: :btree + create_table "identifier_schemes", force: :cascade do |t| - t.string "name" - t.string "description" + t.string "name", limit: 255 + t.string "description", limit: 255 t.boolean "active" t.datetime "created_at" t.datetime "updated_at" - t.string "logo_url" - t.string "user_landing_url" + t.string "logo_url", limit: 255 + t.string "user_landing_url", limit: 255 end create_table "languages", force: :cascade do |t| - t.string "abbreviation" - t.string "description" - t.string "name" + t.string "abbreviation", limit: 255 + t.string "description", limit: 255 + t.string "name", limit: 255 t.boolean "default_language" end create_table "notes", force: :cascade do |t| - t.integer "user_id" - t.text "text" + t.integer "user_id", limit: 4 + t.text "text", limit: 65535 t.boolean "archived" - t.integer "answer_id" - t.integer "archived_by" + t.integer "answer_id", limit: 4 + t.integer "archived_by", limit: 4 t.datetime "created_at" t.datetime "updated_at" end + add_index "notes", ["answer_id"], name: "fk_rails_907f8d48bf", using: :btree + add_index "notes", ["user_id"], name: "fk_rails_7f2323ad43", using: :btree + + create_table "org_identifiers", force: :cascade do |t| + t.string "identifier", limit: 255 + t.string "attrs", limit: 255 + t.datetime "created_at" + t.datetime "updated_at" + t.integer "org_id", limit: 4 + t.integer "identifier_scheme_id", limit: 4 + end + + add_index "org_identifiers", ["identifier_scheme_id"], name: "fk_rails_189ad2e573", using: :btree + add_index "org_identifiers", ["org_id"], name: "fk_rails_36323c0674", using: :btree + create_table "org_token_permissions", force: :cascade do |t| - t.integer "org_id" - t.integer "token_permission_type_id" + t.integer "org_id", limit: 4 + t.integer "token_permission_type_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" end + add_index "org_token_permissions", ["org_id"], name: "fk_rails_e1db1b22c5", using: :btree + add_index "org_token_permissions", ["token_permission_type_id"], name: "fk_rails_2aa265f538", using: :btree + create_table "orgs", force: :cascade do |t| - t.string "name" - t.string "abbreviation" - t.string "target_url" - t.string "wayfless_entity" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "parent_id" + t.string "name", limit: 255 + t.string "abbreviation", limit: 255 + t.string "target_url", limit: 255 + t.string "wayfless_entity", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "parent_id", limit: 4 t.boolean "is_other" - t.string "sort_name" - t.text "banner_text" - t.string "logo_file_name" - t.integer "region_id" - t.integer "language_id" - t.string "logo_uid" - t.string "logo_name" - t.string "contact_email" - t.integer "org_type", default: 0, null: false + t.string "sort_name", limit: 255 + t.text "banner_text", limit: 65535 + t.string "logo_file_name", limit: 255 + t.integer "region_id", limit: 4 + t.integer "language_id", limit: 4 + t.string "logo_uid", limit: 255 + t.string "logo_name", limit: 255 + t.string "contact_email", limit: 255 + t.integer "org_type", limit: 4, default: 0, null: false end + add_index "orgs", ["language_id"], name: "fk_rails_5640112cab", using: :btree + add_index "orgs", ["region_id"], name: "fk_rails_5a6adf6bab", using: :btree + create_table "perms", force: :cascade do |t| - t.string "name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "name", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_index "perms", ["name"], name: "index_perms_on_name", using: :btree add_index "perms", ["name"], name: "index_roles_on_name_and_resource_type_and_resource_id", using: :btree create_table "phases", force: :cascade do |t| - t.string "title" - t.text "description" - t.integer "number" - t.integer "template_id" + t.string "title", limit: 255 + t.text "description", limit: 65535 + t.integer "number", limit: 4 + t.integer "template_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.string "slug" + t.string "slug", limit: 255 t.boolean "modifiable" end add_index "phases", ["template_id"], name: "index_phases_on_template_id", using: :btree create_table "plans", force: :cascade do |t| - t.string "title" - t.integer "template_id" + t.string "title", limit: 255 + t.integer "template_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.string "slug" - t.string "grant_number" - t.string "identifier" - t.text "description" - t.string "principal_investigator" - t.string "principal_investigator_identifier" - t.string "data_contact" - t.string "funder_name" - t.integer "visibility", default: 0, null: false + t.string "slug", limit: 255 + t.string "grant_number", limit: 255 + t.string "identifier", limit: 255 + t.text "description", limit: 65535 + t.string "principal_investigator", limit: 255 + t.string "principal_investigator_identifier", limit: 255 + t.string "data_contact", limit: 255 + t.string "funder_name", limit: 255 + t.integer "visibility", limit: 4, default: 0, null: false end add_index "plans", ["template_id"], name: "index_plans_on_template_id", using: :btree create_table "plans_guidance_groups", force: :cascade do |t| - t.integer "guidance_group_id" - t.integer "plan_id" + t.integer "guidance_group_id", limit: 4 + t.integer "plan_id", limit: 4 end + add_index "plans_guidance_groups", ["guidance_group_id"], name: "fk_rails_ec1c5524d7", using: :btree + add_index "plans_guidance_groups", ["plan_id"], name: "fk_rails_13d0671430", using: :btree + create_table "question_formats", force: :cascade do |t| - t.string "title" - t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "option_based", default: false - t.integer "formattype", default: 0 + t.string "title", limit: 255 + t.text "description", limit: 65535 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "option_based", default: false + t.integer "formattype", limit: 4, default: 0 end create_table "question_options", force: :cascade do |t| - t.integer "question_id" - t.string "text" - t.integer "number" + t.integer "question_id", limit: 4 + t.string "text", limit: 255 + t.integer "number", limit: 4 t.boolean "is_default" t.datetime "created_at" t.datetime "updated_at" end + add_index "question_options", ["question_id"], name: "fk_rails_b9c5f61cf9", using: :btree + create_table "questions", force: :cascade do |t| - t.text "text" - t.text "default_value" - t.integer "number" - t.integer "section_id" + t.text "text", limit: 65535 + t.text "default_value", limit: 65535 + t.integer "number", limit: 4 + t.integer "section_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "question_format_id" - t.boolean "option_comment_display", default: true + t.integer "question_format_id", limit: 4 + t.boolean "option_comment_display", default: true t.boolean "modifiable" end + add_index "questions", ["question_format_id"], name: "fk_rails_4fbc38c8c7", using: :btree add_index "questions", ["section_id"], name: "index_questions_on_section_id", using: :btree create_table "questions_themes", id: false, force: :cascade do |t| - t.integer "question_id", null: false - t.integer "theme_id", null: false + t.integer "question_id", limit: 4, null: false + t.integer "theme_id", limit: 4, null: false end add_index "questions_themes", ["question_id", "theme_id"], name: "question_theme_index", using: :btree add_index "questions_themes", ["theme_id", "question_id"], name: "theme_question_index", using: :btree create_table "regions", force: :cascade do |t| - t.string "abbreviation" - t.string "description" - t.string "name" - t.integer "super_region_id" + t.string "abbreviation", limit: 255 + t.string "description", limit: 255 + t.string "name", limit: 255 + t.integer "super_region_id", limit: 4 end create_table "roles", force: :cascade do |t| - t.integer "user_id" - t.integer "plan_id" + t.integer "user_id", limit: 4 + t.integer "plan_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "access", default: 0, null: false + t.integer "access", limit: 4, default: 0, null: false end + add_index "roles", ["plan_id"], name: "fk_rails_a1ce6c2772", using: :btree + add_index "roles", ["user_id"], name: "fk_rails_ab35d699f0", using: :btree + create_table "sections", force: :cascade do |t| - t.string "title" - t.text "description" - t.integer "number" + t.string "title", limit: 255 + t.text "description", limit: 65535 + t.integer "number", limit: 4 t.datetime "created_at" t.datetime "updated_at" t.boolean "published" - t.integer "phase_id" + t.integer "phase_id", limit: 4 t.boolean "modifiable" end add_index "sections", ["phase_id"], name: "index_sections_on_phase_id", using: :btree create_table "settings", force: :cascade do |t| - t.string "var", null: false - t.text "value" - t.integer "target_id", null: false - t.string "target_type", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "var", limit: 255, null: false + t.text "value", limit: 65535 + t.integer "target_id", limit: 4, null: false + t.string "target_type", limit: 255, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_index "settings", ["target_type", "target_id", "var"], name: "index_settings_on_target_type_and_target_id_and_var", unique: true, using: :btree create_table "splash_logs", force: :cascade do |t| - t.string "destination" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "destination", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "templates", force: :cascade do |t| - t.string "title" - t.text "description" + t.string "title", limit: 255 + t.text "description", limit: 65535 t.boolean "published" - t.integer "org_id" - t.string "locale" + t.integer "org_id", limit: 4 + t.string "locale", limit: 255 t.boolean "is_default" t.datetime "created_at" t.datetime "updated_at" - t.integer "version" - t.integer "visibility" - t.integer "customization_of" - t.integer "dmptemplate_id" + t.integer "version", limit: 4 + t.integer "visibility", limit: 4 + t.integer "customization_of", limit: 4 + t.integer "dmptemplate_id", limit: 4 t.boolean "migrated" - t.boolean "dirty", default: false + t.boolean "dirty", default: false end add_index "templates", ["org_id", "dmptemplate_id"], name: "template_organisation_dmptemplate_index", using: :btree add_index "templates", ["org_id"], name: "index_templates_on_org_id", using: :btree create_table "themes", force: :cascade do |t| - t.string "title" - t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "locale" + t.string "title", limit: 255 + t.text "description", limit: 65535 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "locale", limit: 255 end create_table "themes_in_guidance", id: false, force: :cascade do |t| - t.integer "theme_id" - t.integer "guidance_id" + t.integer "theme_id", limit: 4 + t.integer "guidance_id", limit: 4 end + add_index "themes_in_guidance", ["guidance_id"], name: "fk_rails_a5ab9402df", using: :btree + add_index "themes_in_guidance", ["theme_id"], name: "fk_rails_7d708f6f1e", using: :btree + create_table "token_permission_types", force: :cascade do |t| - t.string "token_type" - t.text "text_description" + t.string "token_type", limit: 255 + t.text "text_description", limit: 65535 t.datetime "created_at" t.datetime "updated_at" end create_table "user_identifiers", force: :cascade do |t| - t.string "identifier" + t.string "identifier", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.integer "user_id" - t.integer "identifier_scheme_id" + t.integer "user_id", limit: 4 + t.integer "identifier_scheme_id", limit: 4 end + add_index "user_identifiers", ["identifier_scheme_id"], name: "fk_rails_fe95df7db0", using: :btree + add_index "user_identifiers", ["user_id"], name: "fk_rails_65c9a98cdb", using: :btree + create_table "users", force: :cascade do |t| - t.string "firstname" - t.string "surname" - t.string "email", default: "", null: false - t.string "orcid_id" - t.string "shibboleth_id" + t.string "firstname", limit: 255 + t.string "surname", limit: 255 + t.string "email", limit: 255, default: "", null: false + t.string "orcid_id", limit: 255 + t.string "shibboleth_id", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "encrypted_password", default: "" - t.string "reset_password_token" + t.string "encrypted_password", limit: 255, default: "" + t.string "reset_password_token", limit: 255 t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", limit: 4, default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.string "confirmation_token" + t.string "current_sign_in_ip", limit: 255 + t.string "last_sign_in_ip", limit: 255 + t.string "confirmation_token", limit: 255 t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "invitation_token" + t.string "invitation_token", limit: 255 t.datetime "invitation_created_at" t.datetime "invitation_sent_at" t.datetime "invitation_accepted_at" - t.string "other_organisation" + t.string "other_organisation", limit: 255 t.boolean "accept_terms" - t.integer "org_id" - t.string "api_token" - t.integer "invited_by_id" - t.string "invited_by_type" - t.integer "language_id" + t.integer "org_id", limit: 4 + t.string "api_token", limit: 255 + t.integer "invited_by_id", limit: 4 + t.string "invited_by_type", limit: 255 + t.integer "language_id", limit: 4 + t.string "recovery_email", limit: 255 + t.binary "prefs", limit: 65535 end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["invitation_token"], name: "index_users_on_invitation_token", unique: true, using: :btree + add_index "users", ["language_id"], name: "fk_rails_45f4f12508", using: :btree + add_index "users", ["org_id"], name: "fk_rails_e73753bccb", using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree create_table "users_perms", id: false, force: :cascade do |t| - t.integer "user_id" - t.integer "perm_id" + t.integer "user_id", limit: 4 + t.integer "perm_id", limit: 4 end + add_index "users_perms", ["perm_id"], name: "fk_rails_457217c31c", using: :btree add_index "users_perms", ["user_id", "perm_id"], name: "index_users_perms_on_user_id_and_perm_id", using: :btree add_foreign_key "annotations", "orgs" @@ -390,6 +439,8 @@ add_foreign_key "guidances", "guidance_groups" add_foreign_key "notes", "answers" add_foreign_key "notes", "users" + add_foreign_key "org_identifiers", "identifier_schemes" + add_foreign_key "org_identifiers", "orgs" add_foreign_key "org_token_permissions", "orgs" add_foreign_key "org_token_permissions", "token_permission_types" add_foreign_key "orgs", "languages" @@ -415,4 +466,4 @@ add_foreign_key "users", "orgs" add_foreign_key "users_perms", "perms" add_foreign_key "users_perms", "users" -end +end \ No newline at end of file diff --git a/lib/assets/images/dcc_logo.png b/lib/assets/images/dcc_logo.png index 593a7dd..961e4bb 100644 --- a/lib/assets/images/dcc_logo.png +++ b/lib/assets/images/dcc_logo.png Binary files differ diff --git a/lib/assets/images/logo.jpg b/lib/assets/images/logo.jpg deleted file mode 100644 index b45a9a3..0000000 --- a/lib/assets/images/logo.jpg +++ /dev/null Binary files differ diff --git a/lib/assets/images/logo.png b/lib/assets/images/logo.png new file mode 100644 index 0000000..a5b9cd8 --- /dev/null +++ b/lib/assets/images/logo.png Binary files differ diff --git a/lib/assets/images/uc3_logo.jpg b/lib/assets/images/uc3_logo.jpg index 46ac993..f29d8ec 100644 --- a/lib/assets/images/uc3_logo.jpg +++ b/lib/assets/images/uc3_logo.jpg Binary files differ diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js index 3f45565..e33ba11 100644 --- a/lib/assets/javascripts/application.js +++ b/lib/assets/javascripts/application.js @@ -22,10 +22,13 @@ //= require_tree ./locale //= require gettext/all //= require jquery-accessible-autocomplet-list-aria.js - +//= require roadmap-utils.js +//= require roadmap-form.js +//= require roadmap-tabs.js +//= require shared/login_form.js +//= require shared/register_form.js $( document ).ready(function() { - $(function(){ $('.dropdown-toggle').dropdown() }); diff --git a/lib/assets/javascripts/contacts/new_contact.js b/lib/assets/javascripts/contacts/new_contact.js index 1b692e2..3127eb2 100644 --- a/lib/assets/javascripts/contacts/new_contact.js +++ b/lib/assets/javascripts/contacts/new_contact.js @@ -3,12 +3,23 @@ }; $(document).ready(function(){ + // Run the input validations when the focus changes + $("#contact_us_contact_email").blur(function(){ + toggleInputError(this, validateEmail($(this).val().trim())); + }); + $("input[type='text'], input[type='email'], textarea").change(function(e){ var enable = ($("#contact_us_contact_name").val().trim().length > 0 && - $("#contact_us_contact_email").val().trim().length > 0 && + validateEmail($("#contact_us_contact_email").val().trim()) != '' && $("#contact_us_contact_subject").val().trim().length > 0 && $("#contact_us_contact_message").val().trim().length > 0); + // Check the recaptcha status + if($("#recaptcha-anchor")){ + if($("#recaptcha-anchor").prop('aria-checked') != 'true'){ + enable = false; + } + } $("#create_contact_submit").attr('aria-disabled', !enable); }); }); \ No newline at end of file diff --git a/lib/assets/javascripts/devise/passwords/new.js b/lib/assets/javascripts/devise/passwords/new.js new file mode 100644 index 0000000..18816c3 --- /dev/null +++ b/lib/assets/javascripts/devise/passwords/new.js @@ -0,0 +1,10 @@ +$(document).ready(function(){ + $("#user_email").blur(function(){ + $("#password-reset-button").attr('aria-disabled', validateEmail($(this).val()) != ''); + }); + + // Run the input validations when the focus changes + $("#user_email").blur(function(){ + toggleInputError(this, validateEmail($(this).val().trim())); + }); +}); \ No newline at end of file diff --git a/lib/assets/javascripts/devise/registrations/edit.js b/lib/assets/javascripts/devise/registrations/edit.js new file mode 100644 index 0000000..1f5c57e --- /dev/null +++ b/lib/assets/javascripts/devise/registrations/edit.js @@ -0,0 +1,57 @@ +$(document).ready(function(){ + // See if we should enable the submit button when a required input changes + $("input").change(function(){ + toggleSubmit(); + }); + + // Run the input validations when the focus changes + $("#user_email, #user_recovery_email").blur(function(){ + let msg = validateEmail($(this).val().trim()); + // If the standard email validation was successful validate that they do not match + toggleInputError(this, (msg != '' ? msg : validateEmailsDoNotMatch())); + }); + $("#user_current_password").blur(function(){ + toggleInputError(this, validatePassword($(this).val().trim())); + }); + $("#user_new_password, #user_password_confirmation").blur(function(){ + let msg = validatePassword($(this).val().trim()); + // If the standard password validation was successful validate that they match + toggleInputError(this, (msg != '' ? msg : validatePasswordsMatch())); + }); + + // Toggle the password field so that its visible/masked + $("#passwords_show").click(function(){ + let typ = $("#user_current_password").attr('type'); + $("#user_current_password").attr('type', (typ === 'password' ? 'text' : 'password')); + $("#user_new_password").attr('type', (typ === 'password' ? 'text' : 'password')); + $("#user_password_confirmation").attr('type', (typ === 'password' ? 'text' : 'password')); + }); + + // Make sure the show password checkbox is unchecked on load + $("#passwords_show").attr("checked", false); + + toggleSubmit(); + + function validateEmailsDoNotMatch(){ + let email = $("form.register-form #user_email").val().trim(); + let recovery = $("form.register-form #user_recovery_email").val().trim(); + return (email === recovery ? (email != '' ? __('Emails must be different') : '') : ''); + } + + function validatePasswordsMatch(){ + let pwd = $("#user_new_password").val().trim(); + let conf = $("#user_password_confirmation").val().trim(); + return (pwd != conf ? (pwd != '' ? __('Passwords must match') : '') : ''); + } + + // Display the submit button only if there is a valid email and password + function toggleSubmit(){ + let disabled = ($("#user_firstname").val().trim().length <= 0 || + $("#user_surname").val().trim().length <= 0 || + validateEmail($("#user_email").val()) != '' || + validateEmail($("#user_recovery_email").val()) != '' || + $("#user_new_password").val() != $("#user_password_confirmation").val()); + $("#update").attr('aria-disabled', disabled); + } +}); + diff --git a/lib/assets/javascripts/home/index.js b/lib/assets/javascripts/home/index.js new file mode 100644 index 0000000..96ec922 --- /dev/null +++ b/lib/assets/javascripts/home/index.js @@ -0,0 +1,32 @@ +$(document).ready(function(){ + $(".tab-panels div.tab-panel:not(.active)").hide(); + + $(".tabs li a").click(function(e){ + e.preventDefault(); + + // Make the clicked tab the active tab + $(this).parent().addClass('active'); + $(this).attr('aria-selected', 'true'); + $.each($(this).parent().siblings(), function(i, sib){ + $(sib).removeClass('active'); + $(sib).find('> a').attr('aria-selected', 'false'); + }); + + // Make the corresponding panel visible and the others hidden + let panel = $($(this).attr("href")); + panel.show().attr("aria-hidden", 'false'); + $.each($(panel).siblings(), function(i, p){ + $(p).hide().attr("aria-hidden", 'true'); + }); + }); + + // If the hidden valid-form field is set to true then enable the submit button + $("#valid-form").change(function(){ + $(this).siblings(".form-submit").attr('aria-disabled', $(this).val() != "true"); + }); + + $("#shibboleth-login").click(function(e){ + e.preventDefault(); + window.location.href = $(this).attr('href'); + }) +}); \ No newline at end of file diff --git a/lib/assets/javascripts/orgs/shibboleth_ds.js b/lib/assets/javascripts/orgs/shibboleth_ds.js new file mode 100644 index 0000000..2537ab6 --- /dev/null +++ b/lib/assets/javascripts/orgs/shibboleth_ds.js @@ -0,0 +1,21 @@ +$(document).ready(function(){ + // If the hidden valid-form field is set to true then enable the submit button + $("#org_name, #org_id").change(function(){ + if($(this).prop('tagName') === 'select'){ + $(this).siblings(".form-submit").attr('aria-disabled', $(this).children(':selected').attr('id') === ""); + }else{ + $(this).siblings(".form-submit").attr('aria-disabled', $("#org_id").val() === ""); + } + }); + + $("#show_list").click(function(e){ + e.preventDefault(); + if($("#full_list").css("display") == "none"){ + $("#full_list").attr('aria-hidden', 'false').show(); + $(this).html(__('Hide list')); + }else{ + $("#full_list").attr('aria-hidden', 'true').hide(); + $(this).html(__('See the full list of partner institutions')); + } + }); +}); \ No newline at end of file diff --git a/lib/assets/javascripts/plans/edit.js b/lib/assets/javascripts/plans/edit.js new file mode 100644 index 0000000..492bc3a --- /dev/null +++ b/lib/assets/javascripts/plans/edit.js @@ -0,0 +1,24 @@ +$(document).ready(function(){ + + $("#is_test").on('click, change', function(e){ + toggleVisibility(); + // If the test box is checked then update the visibility to 'is_test' + if($("#is_test").is(':checked')){ + $('#plan_visibility').val('is_test'); + }else{ + $('#plan_visibility').val(''); + } + }); + + $("input[name='visibility']").on('change', function(e){ + $('#plan_visibility').val($("input[name='visibility']:checked").val()); + }); + + toggleVisibility(); + + function toggleVisibility(){ + let test = $("#is_test").is(':checked'); + // If the test checkbox is true then disable the visibility dropdown + $("input[name='visibility']").attr('aria-disabled', test).attr('disabled', test); + } +}); \ No newline at end of file diff --git a/lib/assets/javascripts/plans/index.js b/lib/assets/javascripts/plans/index.js new file mode 100644 index 0000000..df4b4d6 --- /dev/null +++ b/lib/assets/javascripts/plans/index.js @@ -0,0 +1,19 @@ +$(document).ready(function(){ + $("input[type='checkbox']").on('click, change', function(e){ + let self = this; + let id = $(this).attr("id").replace("is_test-", ""); + let params = {plan: {visibility: $(this).is(':checked') ? 'is_test' : 'privately_visible'}}; + + // Update the visbility to test or private + $.post("/plans/" + id + "/visibility", params, function(data){ + if(data['code'] === 1){ + // If the save was successful make sure the Visibility text gets updated to 'Private' + $(self).parent().siblings("#visibility-" + id).html(__('Private')); + }else{ + // Display an error message + $("#main-page-alert").show().html(data['msg']); + e.preventDefault(); + } + }); + }); +}); \ No newline at end of file diff --git a/lib/assets/javascripts/plans/new.js b/lib/assets/javascripts/plans/new.js new file mode 100644 index 0000000..7bf6e19 --- /dev/null +++ b/lib/assets/javascripts/plans/new.js @@ -0,0 +1,65 @@ +$(document).ready(function(){ + $("#available-templates").hide(); + + // retrieve the template options and toggle the submit button on page reload + handleComboboxChange(); + handleCheckboxClick("org", $("#plan_no_org").prop("checked")); + handleCheckboxClick("funder", $("#plan_no_funder").prop("checked")); + + // When the hidden org and funder id fields change toogle the submit button + $("#plan_org_id, #plan_funder_id").change(function(){ + handleComboboxChange(); + }); + + // Make sure the checkbox is unchecked if we're entering text + $(".js-combobox").keyup(function(){ + var whichOne = $(this).prop('id').split('_')[1]; + $("#plan_no_" + whichOne).prop("checked", false); + }); + + // If the user clicks the no Org/Funder checkbox disable the dropdown + // and hide clear button + $("#plan_no_org, #plan_no_funder").click(function(){ + var whichOne = $(this).prop('id').split('_')[2]; + handleCheckboxClick(whichOne, this.checked); + }); + + $("#plan_template_id").change(function(){ + $("#create_plan_submit").attr('aria-disabled', ($(this).val().trim().length <= 0)); + }); +}); + +// Only display the submit button if the user has made each decision +// ------------------------------------------------------------- +function handleComboboxChange(){ + // If the (no_org checkbox is checked OR an org was selected) AND + // (no_funder checkbox is checked OR a funder was selected) AND + // (the template selector is not visible OR a template has been selected) + var retrieve = ($("#plan_no_org").prop("checked") || + $("#plan_org_id").val().trim().length > 0) && + ($("#plan_no_funder").prop("checked") || + $("#plan_funder_id").val().trim().length > 0); + + if(retrieve){ + if($("#plan_template_id").val().trim().length <= 0){ + $("form").submit(); + } + + }else{ + $("#available-templates").fadeOut(); + $("#plan_template_id").val(""); + } +} + +// Clear the combobox and disable it if the box was checked +// ------------------------------------------------------------- +function handleCheckboxClick(name, checked){ + $("#plan_" + name + "_name").prop("disabled", checked); + $("#plan_template_id").val("").change(); + + if(checked){ + $("#plan_" + name + "_name").val(""); + $("#plan_" + name + "_id").val("").change(); + $("#plan_" + name + "_name").siblings(".combobox-clear-button").hide(); + } +} diff --git a/lib/assets/javascripts/plans/new_plan.js b/lib/assets/javascripts/plans/new_plan.js deleted file mode 100644 index 7bf6e19..0000000 --- a/lib/assets/javascripts/plans/new_plan.js +++ /dev/null @@ -1,65 +0,0 @@ -$(document).ready(function(){ - $("#available-templates").hide(); - - // retrieve the template options and toggle the submit button on page reload - handleComboboxChange(); - handleCheckboxClick("org", $("#plan_no_org").prop("checked")); - handleCheckboxClick("funder", $("#plan_no_funder").prop("checked")); - - // When the hidden org and funder id fields change toogle the submit button - $("#plan_org_id, #plan_funder_id").change(function(){ - handleComboboxChange(); - }); - - // Make sure the checkbox is unchecked if we're entering text - $(".js-combobox").keyup(function(){ - var whichOne = $(this).prop('id').split('_')[1]; - $("#plan_no_" + whichOne).prop("checked", false); - }); - - // If the user clicks the no Org/Funder checkbox disable the dropdown - // and hide clear button - $("#plan_no_org, #plan_no_funder").click(function(){ - var whichOne = $(this).prop('id').split('_')[2]; - handleCheckboxClick(whichOne, this.checked); - }); - - $("#plan_template_id").change(function(){ - $("#create_plan_submit").attr('aria-disabled', ($(this).val().trim().length <= 0)); - }); -}); - -// Only display the submit button if the user has made each decision -// ------------------------------------------------------------- -function handleComboboxChange(){ - // If the (no_org checkbox is checked OR an org was selected) AND - // (no_funder checkbox is checked OR a funder was selected) AND - // (the template selector is not visible OR a template has been selected) - var retrieve = ($("#plan_no_org").prop("checked") || - $("#plan_org_id").val().trim().length > 0) && - ($("#plan_no_funder").prop("checked") || - $("#plan_funder_id").val().trim().length > 0); - - if(retrieve){ - if($("#plan_template_id").val().trim().length <= 0){ - $("form").submit(); - } - - }else{ - $("#available-templates").fadeOut(); - $("#plan_template_id").val(""); - } -} - -// Clear the combobox and disable it if the box was checked -// ------------------------------------------------------------- -function handleCheckboxClick(name, checked){ - $("#plan_" + name + "_name").prop("disabled", checked); - $("#plan_template_id").val("").change(); - - if(checked){ - $("#plan_" + name + "_name").val(""); - $("#plan_" + name + "_id").val("").change(); - $("#plan_" + name + "_name").siblings(".combobox-clear-button").hide(); - } -} diff --git a/lib/assets/javascripts/registrations/sign_in_sign_up.js b/lib/assets/javascripts/registrations/sign_in_sign_up.js new file mode 100644 index 0000000..9e28fb2 --- /dev/null +++ b/lib/assets/javascripts/registrations/sign_in_sign_up.js @@ -0,0 +1,57 @@ +$(document).ready(function(){ + + $(".tab-panels div.tab-panel:not(.active)").hide(); + + $(".tabs li a").click(function(e){ + e.preventDefault(); + + // Make the clicked tab the active tab + $(this).parent().addClass('active'); + $(this).attr('aria-selected', 'true'); + $.each($(this).parent().siblings(), function(i, sib){ + $(sib).removeClass('active'); + $(sib).find('> a').attr('aria-selected', 'false'); + }); + + // Make the corresponding panel visible and the others hidden + let panel = $($(this).attr("href")); + panel.show().attr("aria-hidden", 'false'); + $.each($(panel).siblings(), function(i, p){ + $(p).hide().attr("aria-hidden", 'true'); + }); + }); + + // If the hidden valid-form field is set to true then enable the submit button + $("#valid-form").change(function(){ + $(this).siblings(".form-submit").attr('aria-disabled', $(this).val() != "true"); + }); + + $("#sign-in-form input[class*='required']").change(function(){ + toggleSignInSubmit(); + }); +}); + +function toggleError(input, valid){ + if(valid){ + $(input).siblings('.' + $(input).attr('id') + '-error').hide(); + $(input).removeClass('red-border'); + }else{ + $(input).siblings('.' + $(input).attr('id') + '-error').show(); + $(input).addClass('red-border'); + } +} + +function toggleSignInSubmit(){ + let disabled = (validateEmail($("#user_email").val()) != '' || validatePassword($("#user_password").val()) != ''); + $("#sign-in-button").attr('aria-disabled', disabled); +} + +function toggleRegisterSubmit(){ + let disabled = ($("#user_firstname").val().trim().length <= 0 || + $("#user_surname").val().trim().length <= 0 || + $("#user_email").val().trim().length <= 0 || + $("#user_recovery_email").val().trim().length <= 0 || + $("#user_password").val().trim().length <= 0 || + $("#user_email").val() === $("#user_recovery_email").val()); + $("#register-button").attr('aria-disabled', disabled); +} \ No newline at end of file diff --git a/lib/assets/javascripts/roadmap-form.js b/lib/assets/javascripts/roadmap-form.js new file mode 100644 index 0000000..feff1b7 --- /dev/null +++ b/lib/assets/javascripts/roadmap-form.js @@ -0,0 +1,34 @@ +// --------------------------------------------------------------------------- +function toggleInputError(input, errorMessage){ + let err = $(input).siblings("span.error-tooltip"); + if(err.length <= 0){ + err = $(input).siblings("span.error-tooltip-right"); + } + + if(err.length > 0 && (errorMessage === '' || $(input).val().trim().length <= 0)){ + err.html('').attr('role', ''); + $(input).removeClass('red-border'); + }else{ + err.html(__('Error: ') + errorMessage).attr('role', 'tooltip'); + $(input).addClass('red-border'); + } +} + +// --------------------------------------------------------------------------- +function validatePassword(sPassword) { + if(sPassword.trim().length >= 8 && sPassword.trim().length <= 128){ + return ''; + }else{ + return __('Passwords must have at least 8 characters'); + } +} + +// --------------------------------------------------------------------------- +function validateEmail(sEmail) { + var filter = /^[a-zA-Z0-9]+[a-zA-Z0-9_.-]+[a-zA-Z0-9_-]+@[a-zA-Z0-9]+[a-zA-Z0-9.-]+.[a-z]{2,4}$/; + if(filter.test(sEmail)){ + return ''; + }else{ + return __('Invalid Email'); + } +} \ No newline at end of file diff --git a/lib/assets/javascripts/roadmap-tabs.js b/lib/assets/javascripts/roadmap-tabs.js new file mode 100644 index 0000000..82f630f --- /dev/null +++ b/lib/assets/javascripts/roadmap-tabs.js @@ -0,0 +1,19 @@ +$(document).ready(function(){ + $(".tab-panels div.tab-panel:not(.active)").hide(); + + $("li[role='tab'] a").click(function(e){ + e.preventDefault(); + + // Unselect the other tabs + $("li[role='tab']").removeClass('active').children('a').attr('aria-selected', 'false'); + // Select the current tab + $(this).attr('aria-selected', 'true').parent().addClass('active'); + + // Display the corresponding panel + let panel = $($(this).attr("href")); + panel.show().attr("aria-hidden", 'false'); + $.each($(panel).siblings(), function(i, p){ + $(p).hide().attr("aria-hidden", 'true'); + }); + }); +}); \ No newline at end of file diff --git a/lib/assets/javascripts/roadmap-utils.js b/lib/assets/javascripts/roadmap-utils.js new file mode 100644 index 0000000..70da96a --- /dev/null +++ b/lib/assets/javascripts/roadmap-utils.js @@ -0,0 +1,8 @@ +$(document).ready(function(){ + // Allow for tabs to be selected if a user presses enter while on a tab + $("li[role='tab']").keydown(function(ev) { + if (ev.which ==13) { + $(this).click() + } + }); +}); diff --git a/lib/assets/javascripts/select2.min.js b/lib/assets/javascripts/select2.min.js index c668840..43f0a65 100644 --- a/lib/assets/javascripts/select2.min.js +++ b/lib/assets/javascripts/select2.min.js @@ -1,3 +1,3 @@ -/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;hc;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('
      ');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('
    • '),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c0?b.first().trigger("mouseenter"):a.first().trigger("mouseenter"),this.ensureHighlightVisible()},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")})})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("unselect",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html(''),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("focus",function(b){a.isOpen()||c.$selection.focus()}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('
        '),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('
      • ×
      • ');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e0||0===c.length)){var d=a('×');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change"); -if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){d.status&&"0"===d.status||e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&null!=a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("focus",function(){c.isOpen()&&e.$search.focus()}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('
      • '),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a(""),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.topf.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-n.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.position="relative",a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this.$element.on("focus.select2",function(a){b.trigger("focus",a)}),this._syncA=c.bind(this._syncAttributes,this),this._syncS=c.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._syncA),a.each(c,b._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",b._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",b._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",b._syncS,!1))},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype._syncSubtree=function(a,b){var c=!1,d=this;if(!a||!a.target||"OPTION"===a.target.nodeName||"OPTGROUP"===a.target.nodeName){if(b)if(b.addedNodes&&b.addedNodes.length>0)for(var e=0;e0&&(c=!0);else c=!0;c&&this.dataAdapter.current(function(a){d.trigger("selection:update",{data:a})})}},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null; +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;hc;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('
          ');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('
        • '),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c0?b.first().trigger("mouseenter"):a.first().trigger("mouseenter"),this.ensureHighlightVisible()},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")})})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("unselect",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html(''),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("focus",function(b){a.isOpen()||c.$selection.focus()}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('
            '),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('
          • ×
          • ');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e0||0===c.length)){var d=a('×');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change"); +if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){d.status&&"0"===d.status||e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&null!=a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("focus",function(){c.isOpen()&&e.$search.focus()}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('
          • '),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a(""),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.topf.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-n.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.position="relative",a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this.$element.on("focus.select2",function(a){b.trigger("focus",a)}),this._syncA=c.bind(this._syncAttributes,this),this._syncS=c.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._syncA),a.each(c,b._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",b._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",b._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",b._syncS,!1))},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype._syncSubtree=function(a,b){var c=!1,d=this;if(!a||!a.target||"OPTION"===a.target.nodeName||"OPTGROUP"===a.target.nodeName){if(b)if(b.addedNodes&&b.addedNodes.length>0)for(var e=0;e0&&(c=!0);else c=!0;c&&this.dataAdapter.current(function(a){d.trigger("selection:update",{data:a})})}},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null; },e.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery-mousewheel",["jquery"],function(a){return a}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d,f=Array.prototype.slice.call(arguments,1);return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2."),d=c[b].apply(c,f)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c}); \ No newline at end of file diff --git a/lib/assets/javascripts/shared/login_form.js b/lib/assets/javascripts/shared/login_form.js new file mode 100644 index 0000000..0fe6822 --- /dev/null +++ b/lib/assets/javascripts/shared/login_form.js @@ -0,0 +1,44 @@ +$(document).ready(function(){ + if($("form.login-form").length > 0){ + // If the hidden valid-form field is set to true then enable the submit button + $("form.login-form #valid-form").change(function(){ + $(this).siblings(".form-submit").attr('aria-disabled', $(this).val() != "true"); + }); + + // See if we should enable the submit button when a required input changes + $("form.login-form input[class*='required']").on('change keyup', function(){ + toggleLogInSubmit(); + }); + + // Run the input validations when the focus changes + $("form.login-form #user_email").on('blur', function(){ + toggleInputError(this, validateEmail($(this).val().trim())); + }); + $("form.login-form #user_password").on('blur', function(){ + toggleInputError(this, validatePassword($(this).val().trim())); + }); + + // Toggle the password field so that its visible/masked + $("form.login-form #password_show").click(function(){ + let typ = $("form.login-form #user_password").attr('type'); + $("form.login-form #user_password").attr('type', (typ === 'password' ? 'text' : 'password')); + }); + + // Run the validations in case the page was refreshed + toggleInputError($("form.login-form #user_email"), + validateEmail($("form.login-form #user_email").val().trim())); + toggleInputError($("form.login-form #user_password"), + validatePassword($("form.login-form #user_password").val().trim())); + + // Make sure the show password checkbox is unchecked on load + $("form.login-form #password_show").attr("checked", false); + + // Display the submit button only if there is a valid email and password + function toggleLogInSubmit(){ + let disabled = (validateEmail($("form.login-form #user_email").val()) != '' || + validatePassword($("form.login-form #user_password").val()) != ''); + $("form.login-form #sign-in-button").attr('aria-disabled', disabled); + } + } +}); + diff --git a/lib/assets/javascripts/shared/register_form.js b/lib/assets/javascripts/shared/register_form.js index 82b077d..a33441b 100644 --- a/lib/assets/javascripts/shared/register_form.js +++ b/lib/assets/javascripts/shared/register_form.js @@ -1,61 +1,56 @@ -$(document).ready(function() { - var email_regex = /[^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,}$/; - var valid_email = false; - var valid_password = false; - var valid_password_confirmation = false; - var valid_accept_terms = false; +$(document).ready(function(){ + if($("form.register-form").length > 0){ + // If the hidden valid-form field is set to true then enable the submit button + $("form.register-form #valid-form").change(function(){ + $(this).siblings(".form-submit").attr('aria-disabled', $(this).val() != "true"); + }); - $("#user_email.text_field.reg-input").change(function(){ - if (email_regex.test($(this).val())) { - $(this).next().hide(); - valid_email = true; - } - else { - $(this).next().show(); - valid_email = false; - } - set_aria_submit(); + // See if we should enable the submit button when a required input changes + $("form.register-form input[class*='required']").on('change keyup', function(){ + toggleRegisterSubmit(); }); - $("#user_password.text_field.reg-input").change(function() { - if($(this).val().length >= 8) { - $(this).next().hide(); - valid_password = true; - } - else { - $(this).next().show(); - valid_password = false; - } - // If password_confirmation is non empty already, force to check its validity - if($("#user_password_confirmation.text_field.reg-input").val().length > 0){ - console.log('force to check validity of confirmation'); - $("#user_password_confirmation.text_field.reg-input").change(); - } - set_aria_submit(); + + // Run the input validations when the focus changes + $("form.register-form #user_email, form.register-form #user_recovery_email").on('blur change', function(){ + let msg = validateEmail($(this).val().trim()); + // If the standard email validation was successful validate that they do not match + toggleInputError(this, (msg != '' ? msg : validateEmailsDoNotMatch())); }); - $("#user_password_confirmation.text_field.reg-input").change(function() { - if ($(this).val() === $("#user_password.text_field.reg-input").val()) { - $(this).next().hide(); - valid_password_confirmation = true; - } - else { - $(this).next().show(); - valid_password_confirmation = false; - } - set_aria_submit(); + $("form.register-form #user_password").on('blur change', function(){ + toggleInputError(this, validatePassword($(this).val().trim())); }); - $("#user_accept_terms").change(function(){ - valid_accept_terms = $(this).prop('checked'); - set_aria_submit(); + + // Toggle the password field so that its visible/masked + $("form.register-form #password_show").click(function(){ + let typ = $("form.register-form #user_password").attr('type'); + $("form.register-form #user_password").attr('type', (typ === 'password' ? 'text' : 'password')); }); - function set_aria_submit(){ - if(valid_email - && valid_password - && valid_password_confirmation - && valid_accept_terms){ - $("#sign_up_submit").attr('aria-disabled', false); - } - else { - $("#sign_up_submit").attr('aria-disabled', true); - } + + // Run the validations in case the page was refreshed + toggleInputError($("form.register-form #user_email"), + validateEmail($("form.register-form #user_email").val().trim())); + toggleInputError($("form.register-form #user_recovery_email"), + validateEmail($("form.register-form #user_recovery_email").val().trim())); + toggleInputError($("form.register-form #user_password"), + validatePassword($("form.register-form #user_password").val().trim())); + + // Make sure the show password checkbox is unchecked on load + $("form.register-form #password_show").attr("checked", false); + + function validateEmailsDoNotMatch(){ + let email = $("form.register-form #user_email").val().trim(); + let recovery = $("form.register-form #user_recovery_email").val().trim(); + return (email === recovery ? (email != '' ? __('Emails must be different') : '') : ''); } -}); \ No newline at end of file + + function toggleRegisterSubmit(){ + let disabled = ($("form.register-form #user_firstname").val().trim().length <= 0 || + $("form.register-form #user_surname").val().trim().length <= 0 || + validateEmail($("form.register-form #user_email").val()) != '' || + validateEmail($("form.register-form #user_recovery_email").val()) != '' || + !$("form.register-form #user_accept_terms").prop('checked') || + $("form.register-form #user_email").val() === $("form.register-form #user_recovery_email").val()); + $("form.register-form #register-button").attr('aria-disabled', disabled); + } + } +}); diff --git a/lib/assets/stylesheets/application.css b/lib/assets/stylesheets/application.css index becfdeb..36e7537 100644 --- a/lib/assets/stylesheets/application.css +++ b/lib/assets/stylesheets/application.css @@ -14,5 +14,8 @@ *= require bootstrap.css *= require select2.css *= require bootstrap_and_overrides.css.less + *= require roadmap-hacks.scss + *= require roadmap.scss + *= require roadmap-tabs.scss *= require roadmap-form.scss */ \ No newline at end of file diff --git a/lib/assets/stylesheets/roadmap-form.scss b/lib/assets/stylesheets/roadmap-form.scss index 5a860ae..9f717a7 100644 --- a/lib/assets/stylesheets/roadmap-form.scss +++ b/lib/assets/stylesheets/roadmap-form.scss @@ -1,49 +1,22 @@ -@import "font-awesome"; +@import "roadmap"; -$font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; -$header-font: "GillSansLight"; - -$white: #FFF; -$dark-grey: #333; - -$primary-color: #F49700; -$primary-admin-color: #0057A7; -$disabled-button-color: #CCC; -$cancel-button-color: #827D7E; -$reverse-text: #FFF; - -/* See `.combobox-clear-button` for an example of this mixin in use */ -@mixin icon($icon) { - @extend .fa; - @extend .fa-#{$icon}:before; -} - -.arrow-left { - display: inline-block; - width: 0; - height: 0; - border-top: 12px solid transparent; - border-bottom: 12px solid transparent; - border-right: 12px solid $dark-grey; -} - -.main_header { - margin-bottom: 20px; -} - -.content-box { - -} +/* ================================================ */ +/* FORM Styling */ +/* The top of this file is generic form styles and */ +/* the bottom contains page specific changes to the generic */ +/* ================================================ */ /* Roadmap Form Styling */ /* ------------------------------------------------ */ form.roadmap-form { + text-align: left; fieldset.padded { padding: 10px 10px 25px 10px; } /* Fieldset with labels over inputs */ + /* -------------------------------- */ fieldset.standard { padding: 5px; background-color: $white; @@ -53,38 +26,39 @@ -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; - - label, - input[type="checkbox"], - .combobox-container, + + label, + input[type="checkbox"], + .combobox-container, .left-indent { margin-left: 15px; } - + input[type="text"], select { margin-bottom: 15px; } - + input[type="checkbox"] { vertical-align: top; } - + .checkbox-label { display: inline-block; margin-left: 5px; } } - + /* Fieldset with labels to the left of inputs */ + /* ------------------------------------------ */ fieldset.side-by-side { .mce-tinymce { display: inline-block; } - + div { label, - input[type="checkbox"], - .combobox-container, + input[type="checkbox"], + .combobox-container, .identifier-scheme { display: inline-block; } @@ -92,7 +66,7 @@ .identifier-scheme-indent { margin-left: -5px; } - + label { width: 25%; text-align: right; @@ -100,18 +74,22 @@ vertical-align: middle; } - input.form-submit, + label.align-top { + vertical-align: top; + } + input.form-submit, button.form-cancel { font-size: 10pt; + text-decoration:none; } } - + .button-spacer { display: inline-block; width: 21%; } } - + /* Generic Fieldset Settings */ fieldset legend { font-family: $header-font; @@ -119,17 +97,29 @@ font-size: 26px; font-weight: normal; text-decoration: none; - + margin-bottom: 10px; + padding-left: 5px; + float: left; /* positions the legend within the fieldset box */ border-bottom: none; } - + + .form-input { + clear: both; + } + div.inline { display: inline-block; } .left-indent { margin-left: 15px; } + .right-indent { + margin-right: 5px; + } + .input-full-width { + width: 95%; + } .input-extra-large { width: 70%; } @@ -139,6 +129,9 @@ .input-medium { width: 30%; } + select.input-medium { + width: 32%; + } .input-small { width: 10%; } @@ -146,13 +139,14 @@ .fixed-width-large { width: 550px; } - + /* Accessible Form Buttons */ input.form-submit { background-color: $primary-color; color: $reverse-text; - font-size: 14px; padding: 4px 12px; + font-size: 14px; + margin-top: 15px; -webkit-border-radius: 5px; -moz-border-radius: 5px; @@ -171,13 +165,25 @@ button.form-cancel { background-color: $cancel-button-color; color: $reverse-text; - font-size: 14px; padding: 4px 12px; - + margin-top: 15px; + font-size: 14px; + -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } + + /* Tooltip */ + .arrow-left { + display: inline-block; + width: 0; + height: 0; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-right: 6px solid $dark-grey; + } + /* .submit-tooltip { display: none; margin-left: 5px; @@ -190,11 +196,64 @@ color: $white; background-color: $dark-grey; padding: 5px 5px 8px 5px; - - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; + border-radius: 2px; } + }*/ + input.small-input-button { + width: 150px; + } + + div.form-input { + position: relative; + } + + /* Input validation errors */ + .red-border { + border: 1px solid $error-color; + box-shadow: 0 0 6px $error-background; + } + + .error-tooltip, .error-tooltip-right, .submit-tooltip { + display: none; + width: 45%; + background: $error-background; + border-radius: 3px; + color: $error-color; + padding: 4px 6px; + } + + .error-tooltip[role='tooltip'], .submit-tooltip[role='tooltip'] { + top: 55px; + left: 0; + display: inline; + position: absolute; + z-index: 9; + } + .error-tooltip[role='tooltip']:before, .submit-tooltip[role='tooltip']:before { + display: inline; + position: absolute; + top: -5px; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid $error-background; + } + + .error-tooltip-right[role="tooltip"] { + top: 0; + left: 500px; + display: inline; + } + .error-tooltip-right[role='tooltip']:before { + display: inline; + position: absolute; + left: -5px; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid $error-background; } } @@ -213,15 +272,15 @@ .combobox-container { position: relative; - max-width: 585px; - + width: 95%; + min-width: 325px; font-family: $font-family; } .combobox-suggestions { position: absolute; left: 0; - width: 562px; + width: 98%; margin-top: -10px; background: #fff; z-index: 99; @@ -256,4 +315,207 @@ /* http://geektnt.com/how-to-remove-x-from-search-input-field-on-chrome-and-ie.html */ .js-combobox[type=text]::-ms-clear { display: none; width: 0; height: 0; } -.js-combobox[type=text]::-ms-reveal { display: none; width: 0; height: 0; } \ No newline at end of file +.js-combobox[type=text]::-ms-reveal { display: none; width: 0; height: 0; } + + + +/* ------------------------------------------------ */ +/* ------------------------------------------------ */ +/* Page specific form styling */ +/* ------------------------------------------------ */ +/* ------------------------------------------------ */ + +/* Home Page */ +/* ------------------------------------------------ */ +.main_page_content .content-two-column { + min-height: 400px; +} + + +/* Sign in/Create account forms */ +/* ------------------------------------------------ */ +#sign-in-panel, #create-account-panel { + width: 375px; +} +.omniauth-options { + display: block; + width: 100%; + + .omniauth-login { + width: 93%; + padding: 4px 12px; + background-color: $primary-color; + color: $white; + margin: 0 auto 10px auto; + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + } +} +#new_user { + margin-bottom: 10px; +} + +#new_user fieldset { + width: 90%; + padding-bottom: 15px; + margin-bottom: 5px; + + input[type="password"], + #user_password, + input[type="email"] { + width: 90%; + } + + .small-header { + margin-top: 0; + } + + .checkbox-right { + width: 97%; + position: relative; + + #user_remember_me, input[type='checkbox'], .checkbox-label { + float: right; + } + } +} +#edit_user fieldset { + .checkbox-right { + width: 55%; + position: relative; + + #user_remember_me, input[type='checkbox'], .checkbox-label { + float: right; + } + } +} + +#new_user.register-form { + .inline { + width: 46%; + + #user_firstname, + #user_surname { + width: 92%; + } + } +} +div#forgot-password-link { + vertical-align: bottom; + margin-bottom: 5px; + + a:hover { + color: $primary-color !important; /* Remove the important once the bootstrap_and_overrides css is gone */ + } +} +#new_user #password-reset-form { + width: 50%; +} + +#shibboleth-ds { + width: 50%; +} + +/* Shibboleth Custom Discovery Service form */ +/* ------------------------------------------------ */ +form.shibboleth-ds-form { + width: 55%; + + select { + width: 85%; + } + + input.form-submit { + margin-top: 0; + margin-bottom: 15px; + } +} + +div.three-column, div.two-column, div.one-column { + display: inline-block; + vertical-align: top; + + ul li { + margin-top: 5px; + margin-bottom: 5px; + } + + ul li.separator { + background-color: $medium-grey; + color: $white; + font-weight: bold; + padding: 4px 12px; + } +} +div.three-column { + width: 31%; +} +div.two-column { + width: 47%; +} +div.one-column { + width: 95%; +} + +/* View plans */ +/* ------------------------------------------------ */ +div.main_page_content p #create-test { + vertical-align: middle; +} + +/* Create plan */ +/* ------------------------------------------------ */ +#create_plan { + fieldset { + padding-bottom: 15px; + } + + #plan_title { + margin-bottom: 5px; + width: 90%; + } + + .form-left-side { + position: relative; + float: left; + width: 40%; + } + .form-right-side { + position: relative; + float: left; + margin-left: 20px; + padding-top: 20px; + width: 45%; + min-width: 400px; + + label, input { + display: inline-block; + } + label { + width: 75%; + } + p { + vertical-align: top; + margin-top: -2px; + margin: -2px -5px 0 5px; + } + } +} + +/* Edit plan details */ +/* ------------------------------------------------ */ +.edit-plan-details form.roadmap-form fieldset.side-by-side div { + label.radio-label { + vertical-align: top; + } + + .inline-radios { + margin-left: -5px; + } + + input[type='radio'] { + margin: 0 10px 5px -5px; + } +} \ No newline at end of file diff --git a/lib/assets/stylesheets/roadmap-hacks.scss b/lib/assets/stylesheets/roadmap-hacks.scss new file mode 100644 index 0000000..cf450a8 --- /dev/null +++ b/lib/assets/stylesheets/roadmap-hacks.scss @@ -0,0 +1,255 @@ +@import "roadmap"; + +/* ============================================================================================ */ +/* HACKS TO NULL OUT OLD BOOTSTRAP, BOOTSTRAP_AND_OVERRIDES.CSS.LESS and ADMIN.CSS.LESS STYLING */ +/* ============================================================================================ */ + +.left_side_footer, .right_side_footer { + padding-top: 30px; +} + +#new_user input { + width: 350px; +} +#new_user input[type="checkbox"]{ + width: auto; +} +#new_user input[type="submit"] { + width: 150px; + margin-left: 15px; +} + +/* Override generic handling of input fields */ +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + position: relative; + background-color: $white; + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: $black; + border-radius: 4px; + vertical-align: middle; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border linear .2s, box-shadow linear .2s; +} +select { + height: 30px; +} +textarea { + height: auto; +} + +.modal-body { + overflow: hidden; + padding-bottom: 45px; + margin-bottom: 45px; +} + +html.dmponline { + background: none; +} + +/* Override the color scheme settings in bootstrap_and_overrides and the admin css files */ +/* --------------------------------------------------------------------------------------------- */ +a:hover, a:focus, +p a:hover, p a:focus, +li a:hover, li a:focus, +.screencast_home_page, +#create-test:hover, +.dmp_table_link:hover, +ul.tabs li a:hover, +.navbar, +ul.question_right_column_ul li.active a, ul.question_right_column_ul li.active a:active, +ul.question_right_column_ul li.active a:hover, ul.question_right_column_ul li.active a:focus, +.question-guidance .accordion-heading a, .question-guidance .accordion-heading a:hover { + color: $primary-color !important; +} + +.accordion-group, +.accordion_heading_text, +.plan-export-settings h4 { + background-color: $primary-color; +} + +.header_org_banner_text, +.header_org_banner_text a, .header_org_banner_text a:link, .header_org_banner_text a:visited, +.header_org_banner_text a:active, .header_org_banner_text a:focus, +.accordion_heading_text { + color: $white; +} + +#main-nav-tabs ul { + border-bottom: none; +} +.main_header { + position: relative; + height: auto; + width: 100%; +} +div.header_left, div.header_right { + position: relative; + float: none; + clear: none; + display: inline-block; + width: auto; +} +div.header_right { + margin-left: 20px; + height: auto; + width: 75%; + position: absolute; + bottom: 0; +} +div.header_org_banner_text_vertical_align { + margin-top: -20px; +} +div.signin_signout_header_link { + position: absolute; + top: 2px; + right: 10px; + + .signIn { + float: none; + position: relative; + top: auto; + right: auto; + + ul.navbar li, + ul.navbar li a, ul.navbar li a:link, ul.navbar li a:visited, ul.navbar li a:active, ul.navbar li a:focus { + color: $white !important; + } + } +} + +#main-nav-tabs { + width: auto; +} +#main-nav-tabs li a { + color: $white; + background-color: $primary-color; + border: 1px solid $white; +} +#main-nav-tabs li.active a, #main-nav-tabs li.active a:active, #main-nav-tabs li a:hover, lang-dropdown-link:hover{ + color: $primary-color !important; + background: $white; + border: 1px solid $white; +} + +#project-tabs { + margin-bottom: 10px; +} + +#project-tabs li a { + color: $white; + background-color: $primary-color; + border-bottom: 1px solid $primary-color; +} +#project-tabs li.active a, #project-tabs li.active a:active, #project-tabs li a:hover { + color: $primary-color !important; + background: $white; + border-left: 1px solid $primary-color; + border-top: 1px solid $primary-color; + border-right: 1px solid $primary-color; + border-bottom: none; +} + +div.accordion-group { + border-color: $primary-color; + + .section_desc { + background-color: $primary-color; + } +} +div.accordion-heading a.accordion-toggle { + color: $white; +} + +.label-info, +.badge-info, +ul.question_right_column_ul li a { + background-color: $dark-grey; +} + +.comment-area, .question-guidance, +.question-guidance .guidance-accordion-body, +ul.question_right_column_ul li.active a, ul.question_right_column_ul li.active a:active, +ul.question_right_column_ul li.active a:hover, ul.question_right_column_ul li.active a:focus { + border: 1px solid $primary-color; +} + +.caret, .caret-orange { + border-top: 4px solid $primary-color !important; + border-bottom-color: $primary-color !important; +} + +hr.orange_break_line { + border-top: 1px solid $primary-color; +} + +.question-divider, +ul.question_right_column_ul li a, +#plan-guidance-accordion > .accordion-group > .accordion-heading { + border-bottom: 1px solid $primary-color; +} + +p.alert, +ul#signIn_dropdown li a.signIn_dropdown_link:hover, ul#signIn_dropdown li a.signIn_dropdown_link:focus { + color: $black; + background-color: $light-grey !important; + border: none; +} + +body.dmponline { + .btn-primary, .btn-primary:focus, .btn-primary:active, .btn-primary:visited, .btn-primary:link, + .dmp_toolbar, + .dmp_table thead, + .accordion-heading, + .progress .bar, + .section-status, + .move_2_right input.btn-primary { + background: $primary-color !important; + } + + .accordion-heading { + color: $white; + border: 1px solid $primary-color; + } + + table.dmp_table tbody td, { + border-bottom: 1px solid $light-grey; + } + + div.dmp_details_body, + div.phase_details_body, + div.roadmap-info-box, + .tab-panel, + #create_plan fieldset, #contact-us { + border: 1px solid $primary-color; + border-radius: 5px; + } + table.dmp_details_table tr td.first { + border-right: 1px solid $primary-color; + } + div.phase_details_body { + padding: 0 6px 25px 6px; + } +} diff --git a/lib/assets/stylesheets/roadmap-tabs.scss b/lib/assets/stylesheets/roadmap-tabs.scss new file mode 100644 index 0000000..99cbab2 --- /dev/null +++ b/lib/assets/stylesheets/roadmap-tabs.scss @@ -0,0 +1,63 @@ +@import "roadmap"; + +/* Roadmap Tab Styling */ +/* ------------------------------------------------ */ +.tabs { + list-style: none; + margin: 0; + + li { + display: inline; + + a { + position: relative; + float: left; + display: block; + padding: 10px 35px; + margin-left: -1px; + left: 1px; + color: $white; + background-color: $primary-color; + border-bottom: 2px solid $primary-color; + text-decoration: none; + font-weight: bold; + } + } + li a:hover, + li.active a, + li.active a:hover { + color: $primary-color; + background-color: $white; + border: 1px solid $primary-color; + border-bottom: 2px solid $white; + z-index: 1; + } +} +.tabs:after { + visibility: hidden; + display: block; + font-size: 0; + content: ""; + clear: both; + height: 0; +} +.tab-panels { + position: relative; + min-height: 350px; +} +.tabbed-area div.tab-panel { + padding: 10px 10px; + min-height: 300px; + position: absolute; + top: -1px; + left: 0; + width: inherit; + border: 1px solid $primary-color; + border-top-left-radius: 0; + border-top-right-radius: 5px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} +.tab-panel div.active { + z-index: 1; +} \ No newline at end of file diff --git a/lib/assets/stylesheets/roadmap.scss b/lib/assets/stylesheets/roadmap.scss new file mode 100644 index 0000000..daad890 --- /dev/null +++ b/lib/assets/stylesheets/roadmap.scss @@ -0,0 +1,168 @@ +@import "font-awesome"; + +$font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; +$header-font: "GillSansLight"; + +$black: #000; +$white: #FFF; +$error-color: #FFF; +$error-background: #827D7E; +$dark-grey: #333; +$medium-grey: #827D7E; +$light-grey: #CCC; + +$primary-color: #F17D04; +$primary-admin-color: #F17D04; +$disabled-button-color: #CCC; +$cancel-button-color: #F17D04; +$reverse-text: #FFF; + +$header-gradient-light: #F59503; +$header-gradient-dark: #ED6406; +$header-height: 160px; + +/* See `.combobox-clear-button` for an example of this mixin in use */ +@mixin icon($icon) { + @extend .fa; + @extend .fa-#{$icon}:before; +} + +html.dmponline { + background-color: $white; +} + +h1, h2, h3, h4, h5, +a, a:hover, a:visited, a:active, a:link { + color: $primary-color; +} + +/* This class will ensure that the item is hidden for sighted users and that it does not take up space */ +/* For example:

            <%= _('Create account') %>

            */ +.aria-only { + visibility: hidden; + height: 0; + width: 0; +} + +.primary-color { + color: $primary-color; +} + +/* Used to override standard h2,h3,h4,etc. sizing */ +.small-header { + font-size: 20px; +} + +.main_header { + margin-bottom: 20px; + background: linear-gradient($header-gradient-dark, $header-gradient-light); + + div.header_left { + width: auto; + + .home_logo { + margin: 0 10px 0 0; + } + } +} + +.content { + width: 100%; + background-color: $white; + border-radius: 3px; + margin-top: 25px; + padding: 10px 20px; +} + +.content-two-column { + width: 100%; + + .column-left { + display: inline-block; + width: 55%; + } + .column-right { + display: inline-block; + width: 40%; + height: auto; + margin-left: 35px; + vertical-align: top; + } +} + +.inline { + display: inline-block; +} +.centered { + text-align: center; +} + +.left-indent { + margin-left: 15px; +} + +div.roadmap-info-box { + position: absolute; + float: right; + top: 180px; + right: 12%; + background-color: $white; + padding: 8px 20px; + border-radius: 5px; + vertical-align: middle; + + span { + padding-left: 30px; + } + .fa { + position: absolute; + top: 10px; + background: transparent; + color: $black; + font-size: 16pt; + margin-right: 15px; + } +} + +table.dmp_table{ + width: 100%; + border-collapse: separate; + border-spacing: 2px; + border-bottom: 0px; + margin-bottom: 20px; + table-layout: fixed; + + thead th, tbody td { + padding: 6px 15px 6px 6px; + overflow: hidden; + } + + .col-tiny { + width: 5%; + } + .col-small { + width: 10%; + } + .col-medium { + width: 20%; + } + th.col-large { + width: 35%; + } +} + + +.checkbox-label { + display: inline-block; + font-size: 1em; + margin: 0; + padding: 2px; +} + +.checkbox-input { + border: none; + vertical-align: middle; + height: 17px; + margin: 0 4px 0 0; + padding: 0; +} \ No newline at end of file diff --git a/lib/assets/stylesheets/select2.css b/lib/assets/stylesheets/select2.css index d2278f9..76de04d 100644 --- a/lib/assets/stylesheets/select2.css +++ b/lib/assets/stylesheets/select2.css @@ -1 +1 @@ -.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb} +.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb} diff --git a/test/functional/plans_controller_test.rb b/test/functional/plans_controller_test.rb index 3811003..75708f2 100644 --- a/test/functional/plans_controller_test.rb +++ b/test/functional/plans_controller_test.rb @@ -1,9 +1,9 @@ require 'test_helper' class PlansControllerTest < ActionDispatch::IntegrationTest - + include Devise::Test::IntegrationHelpers - + # TODO: Cleanup these routes! There are duplicates and ones no longer in use! # # CURRENT RESULTS OF `rake routes` @@ -24,7 +24,7 @@ # invite_plan POST /plans/:id/invite plans#invite # possible_templates_plans GET /plans/possible_templates plans#possible_templates # possible_guidance_plans GET /plans/possible_guidance plans#possible_guidance - + # plans GET /plans plans#index # POST /plans plans#create # new_plan GET /plans/new plans#new @@ -33,12 +33,12 @@ # PATCH /plans/:id plans#update # PUT /plans/:id plans#update # DELETE /plans/:id plans#destroy - + setup do @org = Org.first scaffold_plan @user = @plan.owner - + # This should NOT be unnecessary! Owner should have full access role = Role.where(user: @user, plan: @plan).first role.access = 15 @@ -51,23 +51,23 @@ # Should redirect user to the root path if they are not logged in! get plans_path assert_unauthorized_redirect_to_root_path - + sign_in @user - + get plans_path assert_response :success assert assigns(:plans) end - + # GET /plans/new (new_plan_path) # ---------------------------------------------------------- test 'load the new plan page' do # Should redirect user to the root path if they are not logged in! get new_plan_path assert_unauthorized_redirect_to_root_path - + sign_in @user - + get new_plan_path assert_response :success assert assigns(:plan) @@ -83,22 +83,25 @@ # Should redirect user to the root path if they are not logged in! post plans_path(format: :js), params assert_unauthorized_redirect_to_root_path - + sign_in @user - + post plans_path(format: :js), params assert flash[:notice].include?(_('Plan was successfully created.')) assert_response :success assert assigns(:plan) assert_equal "Testing Create", Plan.last.title, "expected the record to have been created" - end + + # assert that the default visibility is used when none is specified + assert_equal Rails.application.config.default_plan_visibility, Plan.last.visibility, "Expected the plan to have been assigned the default visibility" + end # GET /plan/:id (plan_path) # ---------------------------------------------------------- test 'show the plan page' do # Should redirect user to the root path if they are not logged in! try_no_user_and_unauthorized(plan_path(@plan)) - + sign_in @user get plan_path(@plan) assert_response :success @@ -114,23 +117,23 @@ # Should redirect user to the root path if they are not logged in! put plan_path(@plan), {plan: params} assert_unauthorized_redirect_to_root_path - + # User who is does not have access to the plan sign_in User.first put plan_path(@plan), {plan: params} assert_equal _('You are not authorized to perform this action.'), flash[:notice] assert_response :redirect assert_redirected_to plans_url - + sign_in @user - + put plan_path(@plan), {plan: params} assert_equal _('Plan was successfully updated.'), flash[:notice] assert_response :redirect assert_redirected_to plan_url(@plan) assert assigns(:plan) assert_equal 'Testing UPDATE', @plan.reload.title, "expected the record to have been updated" - + # TODO: Reactivate this once the validations on the model are in place! # Invalid object # put plan_path(@plan), {plan: {title: nil}} @@ -138,7 +141,31 @@ # assert_response :success # assert assigns(:plan) end - + + # POST/plan/:id + # ---------------------------------------------------------- + test 'duplicate a plan' do + # Should redirect user to the root path if they are not logged in! + post duplicate_plan_path(@plan, format: :js) + assert_unauthorized_redirect_to_root_path + + # User who is does not have access to the plan + sign_in User.first + put plan_path(@plan, format: :js) + assert_equal _('You are not authorized to perform this action.'), flash[:notice] + assert_response :redirect + assert_redirected_to plans_url + + sign_in @user + post duplicate_plan_path(@plan, format: :js) + @duplicate_plan = Plan.last + assert_equal _('Plan was successfully duplicated.'), flash[:notice] + assert_response :success + assert assigns(:plan) + assert_equal 'Copy of Test Plan', @duplicate_plan.title, "Copy of" + end + + # DELETE /plan/:id (plan_path) # ---------------------------------------------------------- test "delete the plan" do @@ -146,125 +173,125 @@ # Should redirect user to the root path if they are not logged in! delete plan_path(@plan) assert_unauthorized_redirect_to_root_path - + # User who is does not have access to the plan sign_in User.first delete plan_path(@plan) assert_equal _('You are not authorized to perform this action.'), flash[:notice] assert_response :redirect assert_redirected_to plans_url - + sign_in @user delete plan_path(@plan) assert_equal _('Plan was successfully deleted.'), flash[:notice] assert_response :redirect assert assigns(:plan) assert_redirected_to plans_path - assert_raise ActiveRecord::RecordNotFound do + assert_raise ActiveRecord::RecordNotFound do Plan.find(id).nil? end end - + # PUT /plans/:id/update_guidance_choices (update_guidance_choices_plan_path) # ---------------------------------------------------------- test "update the selected guidance" do params = {guidance_group_ids: [GuidanceGroup.first.id, GuidanceGroup.last.id]} - + # Make sure the guidance is attached to the template first so that its a valid selection! q = @template.phases.first.sections.first.questions.first q.themes << GuidanceGroup.first.guidances.first.themes.first q.themes << GuidanceGroup.last.guidances.first.themes.first q.save - + put update_guidance_choices_plan_path(@plan, format: :json), params assert_unauthorized_redirect_to_root_path - + # User who does not have access to the plan sign_in User.first put update_guidance_choices_plan_path(@plan, format: :json), params assert_equal _('You are not authorized to perform this action.'), flash[:notice] assert_response :redirect assert_redirected_to plans_url - + sign_in @user put update_guidance_choices_plan_path(@plan, format: :json), params assert_response :redirect assert_redirected_to plan_path(@plan) - + @plan.reload ggs = @plan.guidance_groups.ids assert ggs.include?(GuidanceGroup.first.id), "expected the plan to have the first GuidanceGroup selected" assert ggs.include?(GuidanceGroup.last.id), "expected the plan to have the last GuidanceGroup selected" end - + # GET /plans/:id/share (share_plan_path) # ---------------------------------------------------------- test "get the share plan page" do # Should redirect user to the root path if they are not logged in! try_no_user_and_unauthorized(share_plan_path(@plan)) - sign_in @user + sign_in @user get share_plan_path(@plan) assert_response :success assert assigns(:plan) end - + # GET /plans/:id/status(format: :json) (status_plan_path) # ---------------------------------------------------------- test "get the plan status" do # Should redirect user to the root path if they are not logged in! try_no_user_and_unauthorized(status_plan_path(@plan, format: :json)) - sign_in @user + sign_in @user get status_plan_path(@plan, format: :json) assert_response :success assert assigns(:plan) end - + # GET /plans/:id/answer(format: :json) (answer_plan_path) # ---------------------------------------------------------- test "get the answer to the specified question for the plan" do # Should redirect user to the root path if they are not logged in! try_no_user_and_unauthorized(answer_plan_path(@plan, format: :json)) - sign_in @user + sign_in @user get answer_plan_path(@plan, format: :json) assert_response :success assert assigns(:plan) end - + # GET /plans/:id/export (export_plan_path) # ---------------------------------------------------------- test "export the plan" do # Should redirect user to the root path if they are not logged in! try_no_user_and_unauthorized(export_plan_path(@plan)) - sign_in @user + sign_in @user get export_plan_path(@plan) assert_response :success assert assigns(:plan) - + # TODO: We need some better tests here to check the different formats! end - + # GET /plans/:id/show_export (show_export_plan_path) # ---------------------------------------------------------- test "show the export the plan page" do # Should redirect user to the root path if they are not logged in! try_no_user_and_unauthorized(show_export_plan_path(@plan)) - sign_in @user + sign_in @user get show_export_plan_path(@plan) assert_response :success assert assigns(:plan) end - + private def try_no_user_and_unauthorized(target) # Should redirect user to the root path if they are not logged in! get target assert_unauthorized_redirect_to_root_path - + # User who is does not have access to the plan sign_in User.first get target @@ -272,5 +299,5 @@ assert_response :redirect assert_redirected_to plans_url end - + end diff --git a/test/functional/registrations_controller_test.rb b/test/functional/registrations_controller_test.rb index ef0079b..09b6a79 100644 --- a/test/functional/registrations_controller_test.rb +++ b/test/functional/registrations_controller_test.rb @@ -17,7 +17,7 @@ # ------------------------------------------------------------- test "user receives proper error messaging if they have not accepted terms" do - post user_registration_path, {user: {accept_terms: false}} + post user_registration_path, {user: {accept_terms: "off"}} assert_response :redirect follow_redirect! @@ -30,14 +30,12 @@ test "user receives proper error messaging if they have not provided a valid email and/or password" do [ {}, {email: 'foo.bar@test.org'}, # No Password or Confirmation - {password: 'test12345'}, # No Confirmation - {password_confirmation: 'test12345'}, # No Password + {password: 'test12345'}, # No Email {password: 'test12345', password_confirmation: 'test12345'}, # No Email - {email: 'foo.bar@test.org', password: 'test', password_confirmation: 'test'}, # Password is too short - {email: 'foo.bar@test.org', password: 'test12345', password_confirmation: 'test123'}, # Passwords do not match - {email: 'foo.bar$test.org', password: 'test12345', password_confirmation: 'test12345'} # invalid email + {email: 'foo.bar@test.org', password: 'test'}, # Password is too short + {email: 'foo.bar$test.org', password: 'test12345'} # invalid email ].each do |params| - post user_registration_path, {user: {accept_terms: 1}.merge(params)} + post user_registration_path, {user: {accept_terms: "on"}.merge(params)} assert_response :redirect follow_redirect! @@ -49,10 +47,9 @@ # ------------------------------------------------------------- test "user is able to register and is auto-logged in and brought to profile page" do - form = {accept_terms: 1, + form = {accept_terms: "on", email: 'foo.bar@test.org', - password: 'Test12345', - password_confirmation: 'Test12345'} + password: 'Test12345'} cntr = 1 # Test the bare minimum requirements and then all options diff --git a/test/integration/authentication_test.rb b/test/integration/authentication_test.rb index c9d4c26..c647877 100644 --- a/test/integration/authentication_test.rb +++ b/test/integration/authentication_test.rb @@ -39,7 +39,7 @@ get root_path # Make sure that the user is sent to the page that lists their plans - assert_select '.welcome-message h2', _('Welcome.') + assert_select 'h1', _('Welcome.') end # ---------------------------------------------------------- @@ -58,7 +58,7 @@ # Make sure that the user is sent to the page that lists their plans assert_response :success - assert_select '.welcome-message h2', _('Welcome.') + assert_select 'h1', _('Welcome.') end end diff --git a/test/test_helper.rb b/test/test_helper.rb index ec8cda5..a366019 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -117,7 +117,7 @@ follow_redirects assert_response :success - assert_select '.welcome-message h2', _('Welcome.') + assert_select 'h1', _('Welcome.') end # ---------------------------------------------------------------------- diff --git a/test/unit/plan_test.rb b/test/unit/plan_test.rb index 08c03cc..b3ad047 100644 --- a/test/unit/plan_test.rb +++ b/test/unit/plan_test.rb @@ -1,7 +1,7 @@ require 'test_helper' class PlanTest < ActiveSupport::TestCase - + setup do @org = Org.first @template = Template.first @@ -10,70 +10,70 @@ @administrator = User.create!(email: 'administrator@example.com', password: 'password123') @editor = User.create!(email: 'editor@example.com', password: 'password123') @reader = User.create!(email: 'reader@example.com', password: 'password123') - + @plan = Plan.create(title: 'Test Plan', template: @template, grant_number: 'Plan12345', - identifier: '000912', description: 'This is a test plan', + identifier: '000912', description: 'This is a test plan', principal_investigator: 'John Doe', principal_investigator_identifier: 'ABC', data_contact: 'john.doe@example.com', visibility: 1) - + @plan.assign_creator(@creator.id) @plan.save! @plan.reload end - + # --------------------------------------------------- test "required fields are required" do - # TODO: uncomment the validation on Plan and then retest this. The validations appear to be breaking the + # TODO: uncomment the validation on Plan and then retest this. The validations appear to be breaking the # current plan save process in the controller, so determine why and fix. =begin assert_not Plan.new.valid? assert_not Plan.new(title: 'Testing').valid?, "expected the template field to be required" - + # Make sure that the Settings gem is defaulting the title for us assert Plan.new(template: @template).valid?, "expected the title field to have been set by default by the Settings gem" - + # Ensure the bare minimum and complete versions are valid a = Plan.new(title: 'Testing', template: @template) assert a.valid?, "expected the 'title', 'template' and at least one 'user' fields to be enough to create an Plan! - #{a.errors.map{|f, m| f.to_s + ' ' + m}.join(', ')}" =end end - + # --------------------------------------------------- test "dmptemplate returns the template" do assert_equal @plan.template, @plan.dmptemplate end - + # --------------------------------------------------- test "correctly creates a new answer" do q = @template.phases.first.sections.last.questions.last q.answers = [] q.save! - + answer = @plan.answer(q.id) assert_equal nil, answer.id, "expected a new Answer" assert_equal q.default_value, answer.text, "expected the new Answer to use the Default Answer for the Question" end - + # --------------------------------------------------- test "correctly retrieves the answer for the question" do q = @template.phases.first.sections.last.questions.last answer = @plan.answer(q.id) answer.text = "testing" answer.save! - + answr = @plan.answer(q.id) assert_not answr.id.nil?, "expected the latest Answer" assert_equal "testing", answr.text, "expected the Answer returned to have the correct text" - + # Check an option based question q = QuestionFormat.find_by(option_based: true).questions.first p = Plan.create(template: q.section.phase.template, title: 'Testing an option based answer') o = QuestionOption.find_by(question: q) d = QuestionOption.create(question: q, text: 'default', number: 99, is_default: true) a = Answer.create(plan: p, question: q, user: @creator, question_options: [o]) - + answr = p.answer(q.id, false) - + assert_not answr.id.nil?, "expected the Option Based Answer" assert_equal [o], answr.question_options, "expected the Answer returned to have the correct options selected" @@ -82,7 +82,7 @@ answr = p.answer(q.id) assert_equal [d], answr.question_options, "expected the Answer returned to have the correct options selected" end - + # --------------------------------------------------- test "retrieves the selected guidance groups" do # Create a new theme and attach it to our template's question and a guidance group @@ -93,21 +93,21 @@ g.save q.themes << t q.save - + # Create a new guidance group and guidance that is attached to a theme but NOT used by our template t = Theme.create!(title: 'Test B') gg = GuidanceGroup.create!(name: 'Tester', org: @creator.org) g = Guidance.create!(text: 'Testing guidance', guidance_group: gg, themes: [t]) - + pggs = @plan.get_guidance_group_options assert pggs.include?(GuidanceGroup.first) assert_not pggs.include?(gg) end - + # --------------------------------------------------- test "retrieves the selected guidance for a specific question" do q = @template.phases.first.sections.first.questions.first - + ['By Template', 'By Org', 'Selected'].each do |txt| t = Theme.create!(title: "Theme test for - #{txt}") gg = GuidanceGroup.create!(name: "GuidanceGroup test for - #{txt}", org: @creator.org) @@ -116,59 +116,59 @@ q.themes << t q.save end - + @template.org.guidance_groups << GuidanceGroup.find_by(name: "GuidanceGroup test for - By Template") @template.org.save @plan.owner.org.guidance_groups << GuidanceGroup.find_by(name: "GuidanceGroup test for - By Org") @plan.owner.org.save @plan.guidance_groups << GuidanceGroup.find_by(name: "GuidanceGroup test for - Selected") @plan.save - @plan.reload + @plan.reload gs = @plan.guidance_for_question(q) - + # Template org's themed guidance hash = gs.select{|h| h[:guidance] == Guidance.find_by(text: "Guidance test for - By Template")}.first assert_not hash.nil?, "expected to find the guidance by template" assert hash[:theme].include?("Theme test for - By Template"), "expected to find the theme by template" - + # User org's themed guidance hash = gs.select{|h| h[:guidance] == Guidance.find_by(text: "Guidance test for - By Org")}.first assert_not hash.nil?, "expected to find the guidance by org" assert hash[:theme].include?("Theme test for - By Org"), "expected to find the theme by org" - + # Selected guidance group's guidance hash = gs.select{|h| h[:guidance] == Guidance.find_by(text: "Guidance test for - Selected")}.first assert_not hash.nil?, "expected to find the guidance by selected" assert hash[:theme].include?("Theme test for - Selected"), "expected to find the theme by selected" end - + # --------------------------------------------------- test "adds the guidance to a guidance array" do # TODO: Skipping because the add_guidance_to_array method doesn't seem to be called from anywhere end - + # --------------------------------------------------- test "checks whether the specified user can edit the plan" do @plan.assign_administrator(@administrator) @plan.assign_editor(@editor) @plan.assign_reader(@reader) - + # TODO: It seems like editable_by? should return true if the user is the creator or we've called assign_editor # or assign_administrator. seems to be an issue with the assign_user private method on the Plan model #assert @plan.editable_by?(@creator), "expected the creator to NOT be able to edit the plan" #assert @plan.editable_by?(@editor), "expected the editor to be able to edit the plan" #assert @plan.editable_by?(@administrator), "expected the administrator to NOT be able to edit the plan" - + assert_not @plan.editable_by?(@reader), "expected the reader to NOT be able to edit the plan" end - + # --------------------------------------------------- test "checks whether the specified user can read the plan" do @plan.assign_administrator(@administrator) @plan.assign_editor(@editor) @plan.assign_reader(@reader) - + # TODO: It seems like readable_by? should return true if the user is the creator or we've called assign_editor # or assign_administrator or assign_reader. seems to be an issue with the assign_user method on Plan #assert @plan.readable_by?(@creator), "expected the creator to NOT be able to read the plan" @@ -176,89 +176,90 @@ #assert @plan.readable_by?(@administrator), "expected the administrator to be able to read the plan" #assert @plan.readable_by?(@reader), "expected the reader to be able to read the plan" end - + # --------------------------------------------------- test "checks whether the specified user can administer the plan" do @plan.assign_administrator(@administrator) @plan.assign_editor(@editor) @plan.assign_reader(@reader) - + # TODO: It seems like creator should be able to administer their own plan or we have called assign_administrator # seems to be an issue with the assign_user private method on the Plan model #assert @plan.administerable_by?(@creator), "expected the creator to NOT be able to administer the plan" #assert @plan.administerable_by?(@administrator), "expected the administrator to be able to administer the plan" - + assert_not @plan.administerable_by?(@editor), "expected the editor to NOT be able to administer the plan" assert_not @plan.administerable_by?(@reader), "expected the reader to NOT be able to administer the plan" end - + # --------------------------------------------------- test "checks that status returns the correct information" do q = 0 @template.phases.first.sections.map{|s| q += s.questions.count } - + @plan.answers << Answer.new(user: User.last, text: "testing status", question: @template.phases.first.sections.first.questions.first) - + hash = @plan.status - + # Expecting the hash to look something like this: # ----------------------------------------------- - #{"num_questions"=>13, - # "num_answers"=>0, + #{"num_questions"=>13, + # "num_answers"=>0, # "sections"=>{ - # 1=>{"questions"=>[1, 2], "num_questions"=>2, "num_answers"=>0}, - # 2=>{"questions"=>[3], "num_questions"=>1, "num_answers"=>0}}, + # 1=>{"questions"=>[1, 2], "num_questions"=>2, "num_answers"=>0}, + # 2=>{"questions"=>[3], "num_questions"=>1, "num_answers"=>0}}, # "questions"=>{ # 1=>{"answer_id"=>nil, "answer_created_at"=>nil, "answer_text"=>nil, - # "answer_option_ids"=>nil, "answered_by"=>nil}, + # "answer_option_ids"=>nil, "answered_by"=>nil}, # 2=>{"answer_id"=>nil, "answer_created_at"=>nil, "answer_text"=>nil, - # "answer_option_ids"=>nil, "answered_by"=>nil}, + # "answer_option_ids"=>nil, "answered_by"=>nil}, # 3=>{"answer_id"=>nil, "answer_created_at"=>nil, "answer_text"=>nil, - # "answer_option_ids"=>nil, "answered_by"=>nil}}, + # "answer_option_ids"=>nil, "answered_by"=>nil}}, # "space_used"=>30} - + assert_equal q, hash["num_questions"], "expected the number of questions to match" assert_equal @plan.answers.count, hash["num_answers"], "expected the number of answers to match" - + @template.phases.first.sections.each do |s| assert_not hash["sections"][s.id].nil?, "expected section #{s.id} to be in sections portion" s.questions.each do |q| assert hash["sections"][s.id]["questions"].include?(q.id), "expected section #{s.id}, question #{q.id} to be in section portion" - + assert_not hash["questions"][q.id].nil?, "expected question #{q.id} to appear in the questions portion" end end end - + # --------------------------------------------------- test "checks that user is a properly assigned as a creator" do usr = User.first @plan.assign_creator(usr) - + # TODO: It seems like the creator should be allowed to administer, red and edit their plan #assert @plan.administerable_by?(usr), "expected the creator to be able to administer" #assert @plan.editable_by?(usr), "expected the creator to be able to edit" #assert @plan.readable_by?(usr), "expected the creator to be able to read" + assert @plan.owned_by?(usr), "expected the creator to be able to own a plan" end - + # --------------------------------------------------- test "checks that user is a properly assigned as a editor" do usr = User.first @plan.assign_editor(usr) - + assert_not @plan.administerable_by?(usr), "expected the editor to NOT be able to administer" - + # TODO: It seems like an editor should be able to read and edit #assert @plan.editable_by?(usr), "expected the editor to be able to edit" #assert @plan.readable_by?(usr), "expected the editor to be able to read" end - + # --------------------------------------------------- test "checks that user is a properly assigned as a reader" do usr = User.first @plan.assign_reader(usr) - + assert_not @plan.administerable_by?(usr), "expected the reader to NOT be able to administer" assert_not @plan.editable_by?(usr), "expected the reader to NOT be able to edit" @@ -266,23 +267,23 @@ # seems to be an issue with the assign_user private method on the Plan model #assert @plan.readable_by?(usr), "expected the reader to be able to read" end - + # --------------------------------------------------- test "checks that user is a properly assigned as a adminstrator" do usr = User.first @plan.assign_administrator(usr) - + # TODO: It seems like assigning someone as an administrator should give them permission to also read and edit #assert @plan.administerable_by?(usr), "expected the adminstrator to be able to administer" #assert @plan.editable_by?(usr), "expected the adminstrator to be able to edit" #assert @plan.readable_by?(usr), "expected the adminstrator to be able to read" end - + # --------------------------------------------------- test "name returns the title" do assert_equal @plan.title, @plan.name end - + # --------------------------------------------------- test "owner returns the creator" do @plan.assign_creator(@creator) @@ -290,21 +291,21 @@ # TODO: Investigate whether or not this should pass. It seems logical that the creator should be the owner by # default but perhaps there is a use-case for someone creating plans for another user #assert_equal @creator, @plan.owner, "expected the owner to match the creator" - + plan = Plan.create!(template: Template.last, title: 'Testing no creator') assert plan.owner.nil?, "expected a new plan with no creator assigned to return nil" end - + # --------------------------------------------------- test "checks that last edited matches the latest_update" do now = Time.new @template.phases.last.updated_at = now assert_equal now.to_date, @plan.last_edited end - + # --------------------------------------------------- test "can CRUD Plan" do - obj = Plan.create(title: 'Testing CRUD', template: Template.where.not(id: @template.id).first, + obj = Plan.create(title: 'Testing CRUD', template: Template.where.not(id: @template.id).first, roles: [Role.new(user: User.last, creator: true)], description: "should change") assert_not obj.id.nil?, "was expecting to be able to create a new Plan! - #{obj.errors.map{|f, m| f.to_s + ' ' + m}.join(', ')}" @@ -312,10 +313,10 @@ obj.save! obj.reload assert_equal 'changed', obj.description, "Was expecting to be able to update the title of the Plan!" - + assert obj.destroy!, "Was unable to delete the Plan!" end - + # --------------------------------------------------- test "can manage has_many relationship with Answers" do a = Answer.new(user: User.last, plan: @plan, question: @plan.questions.first, text: 'Test!') @@ -327,17 +328,17 @@ role = Role.new(user: User.first, editor: true) verify_has_many_relationship(@plan, role, @plan.roles.count) end - + # --------------------------------------------------- test "can manage has_many relationship with ExportedPlan" do ep = ExportedPlan.create(format: ExportedPlan::VALID_FORMATS.last) verify_has_many_relationship(@plan, ep, @plan.exported_plans.count) end - + # --------------------------------------------------- test "can manage belongs_to relationship with Template" do plan = Plan.new(title: 'Tester') verify_belongs_to_relationship(plan, Template.first) end - + end