diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb
index 7d48ddf..e66bd66 100644
--- a/app/controllers/answers_controller.rb
+++ b/app/controllers/answers_controller.rb
@@ -36,9 +36,24 @@
@question = @answer.question
@section = @plan.get_section(@question.section_id)
- respond_to do |format|
- format.js {}
- end
+ render json: {
+ "question" => {
+ "id" => @question.id,
+ "answer_lock_version" => @answer.lock_version,
+ "locking" => @stale_answer ?
+ render_to_string(partial: 'answers/locking', locals: { question: @question, answer: @stale_answer, user: @answer.user }, formats: [:html]) :
+ nil,
+ "answer_status" => render_to_string(partial: 'answers/status', locals: { answer: @answer}, formats: [:html])
+ },
+ "section" => {
+ "id" => @section.id,
+ "progress" => render_to_string(partial: '/sections/progress', locals: { section: @section, plan: @plan }, formats: [:html])
+ },
+ "plan" => {
+ "id" => @plan.id,
+ "progress" => render_to_string(:partial => 'plans/progress', locals: { plan: @plan }, formats: [:html])
+ }
+ }.to_json
end # End update
private
diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb
index 93b9508..6031374 100644
--- a/app/controllers/plans_controller.rb
+++ b/app/controllers/plans_controller.rb
@@ -217,12 +217,12 @@
end
end
- def show_export
+ def download
@plan = Plan.find(params[:id])
authorize @plan
@phase_options = @plan.phases.order(:number).pluck(:title,:id)
@export_settings = @plan.settings(:export)
- render 'show_export'
+ render 'download'
end
@@ -277,7 +277,7 @@
end
rescue ActiveRecord::RecordInvalid => e
@phase_options = @plan.phases.order(:number).pluck(:title,:id)
- redirect_to show_export_plan_path(@plan), alert: _('%{format} is not a valid exporting format. Available formats to export are %{available_formats}.') %
+ redirect_to download_plan_path(@plan), alert: _('%{format} is not a valid exporting format. Available formats to export are %{available_formats}.') %
{format: params[:format], available_formats: ExportedPlan::VALID_FORMATS.to_s}
end
end
diff --git a/app/controllers/settings/plans_controller.rb b/app/controllers/settings/plans_controller.rb
index 07b2789..3117367 100644
--- a/app/controllers/settings/plans_controller.rb
+++ b/app/controllers/settings/plans_controller.rb
@@ -37,7 +37,7 @@
end
respond_to do |format|
@phase_options = @plan.phases.order(:number).pluck(:title,:id)
- format.html { redirect_to(show_export_plan_path(@plan.id)) }
+ format.html { redirect_to(download_plan_path(@plan.id)) }
# format.json { render json: settings_json }
end
end
diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb
index d320fc2..b15c6e4 100644
--- a/app/policies/plan_policy.rb
+++ b/app/policies/plan_policy.rb
@@ -21,7 +21,7 @@
@plan.readable_by?(@user.id) && Role.find_by(user_id: @user.id, plan_id: @plan.id).active
end
- def show_export?
+ def download?
@plan.readable_by?(@user.id) && Role.find_by(user_id: @user.id, plan_id: @plan.id).active
end
diff --git a/app/views/answers/_new_edit.html.erb b/app/views/answers/_new_edit.html.erb
index 5706833..0aa8384 100644
--- a/app/views/answers/_new_edit.html.erb
+++ b/app/views/answers/_new_edit.html.erb
@@ -1,86 +1,22 @@
-<% q_format = question.question_format %>
-<%= form_for answer, url: {controller: :answers, action: :update}, html: {method: :put, class: "roadmap-form answer", 'data-autosave': question.id}, remote: true do |f| %>
-
+ <%= label_tag(:phase_id, _("Select phase to download")) %>
+ <%= select_tag(:phase_id, options_for_select(@phase_options, @phase_options[0])) %>
+
+ <% else %>
+ <%= hidden_field_tag(:phase_id, @phase_options[0][1]) %>
+ <% end %>
+
+
+ <%= label_tag 'export[project_details]', raw("#{check_box_tag 'export[project_details]', true, false} #{_('project details coversheet')}") %>
+
+
+ <%= label_tag 'export[question_headings]', raw("#{check_box_tag 'export[question_headings]', true, true} #{_('question text and section headings')}") %>
+
+
+ <%= label_tag 'export[unanswered_questions]', raw("#{check_box_tag 'export[unanswered_questions]', true, true} #{_('unanswered questions')}") %>
+
+ <% if @plan.template.customization_of.present? %>
+
+ <%= label_tag 'export[custom_sections]', raw("#{check_box_tag 'export[custom_sections]', true, false} #{_('supplementary section(s) not requested by funding organisation')}") %>
+
+ <% end %>
+
+
+
+ <%= label_tag "export[formatting][font_face]", _('Face'), class: 'control-label' %>
+ <%= select_tag "export[formatting][font_face]",
+ options_for_select(Settings::Template::VALID_FONT_FACES,
+ @export_settings.formatting[:font_face]),
+ class: 'form-control',
+ "data-default": @plan.template.settings(:export).formatting[:font_face] %>
+
+
+ <%= label_tag "export[formatting][font_size]", _('Size') + " (pt)", class: 'control-label' %>
+ <%= select_tag "export[formatting][font_size]",
+ options_for_select(Settings::Template::VALID_FONT_SIZE_RANGE.to_a, @export_settings.formatting[:font_size]),
+ class: 'form-control',
+ "data-default": @plan.template.settings(:export).formatting[:font_size] %>
+
+
+
+ <%= label_tag "export[formatting][margin][top]", _('Top'),
+ class: 'control-label' %>
+ <%= select_tag "export[formatting][margin][top]",
+ options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a,
+ @export_settings.formatting[:margin][:top]),
+ class: 'form-control',
+ "data-default": @plan.template.settings(:export).formatting[:margin][:top] %>
+
+
+ <%= label_tag "export[formatting][margin][bottom]", _('Bottom'),
+ class: 'control-label' %>
+ <%= select_tag "export[formatting][margin][bottom]",
+ options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a,
+ @export_settings.formatting[:margin][:bottom]),
+ class: 'form-control',
+ "data-default": @plan.template.settings(:export).formatting[:margin][:bottom] %>
+
+
+ <%= label_tag "export[formatting][margin][left]", _('Left'),
+ class: 'control-label' %>
+ <%= select_tag "export[formatting][margin][left]",
+ options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a,
+ @export_settings.formatting[:margin][:left]),
+ class: 'form-control',
+ "data-default": @plan.template.settings(:export).formatting[:margin][:left] %>
+
+
+ <%= label_tag "export[formatting][margin][right]", _('Right'),
+ class: 'control-label' %>
+ <%= select_tag "export[formatting][margin][right]",
+ options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a,
+ @export_settings.formatting[:margin][:right]),
+ class: 'form-control',
+ "data-default": @plan.template.settings(:export).formatting[:margin][:rigth] %>
+
+
+
+ <%= button_tag(_('Download Plan'), class: "btn btn-primary", type: "submit") %>
+<% end %>
diff --git a/app/views/plans/_progress.html.erb b/app/views/plans/_progress.html.erb
index db2542a..e95f0b9 100644
--- a/app/views/plans/_progress.html.erb
+++ b/app/views/plans/_progress.html.erb
@@ -1,9 +1,11 @@
<%
nanswers = plan.num_answered_questions()
nquestions = plan.num_questions()
+ value=(nanswers.to_f/nquestions*100).round(2)
%>
-<% answered = %(#{nanswers}/#{nquestions})%>
-
-
-
<%= answered -%> <%= _(' answered')%>
+
;">
+ <%= "#{value} % #{_('answered')}" %>
diff --git a/app/views/plans/download.html.erb b/app/views/plans/download.html.erb
new file mode 100644
index 0000000..f971efe
--- /dev/null
+++ b/app/views/plans/download.html.erb
@@ -0,0 +1,12 @@
+
+
+
+
<%= @plan.title %>
+
+
+
+
+
+ <%= render partial: 'download_form', layout: '/shared/plan_navigation', locals: { plan: @plan } %>
+
+
diff --git a/app/views/plans/index.html.erb b/app/views/plans/index.html.erb
index fd90df3..9567967 100644
--- a/app/views/plans/index.html.erb
+++ b/app/views/plans/index.html.erb
@@ -76,14 +76,14 @@
<%= link_to _('Share'), share_plan_path(plan) %>
<% end %>
-
<%= link_to _('Download'), show_export_plan_path(plan) %>
+
<%= link_to _('Download'), download_plan_path(plan) %>
<%= link_to _('Make a copy'), duplicate_plan_path(plan),
method: :post, remote: true %>
<% else %>
<%= link_to _('View'), plan_path(plan) %>
-
<%= link_to _('Download'), show_export_plan_path(plan) %>
+
<%= link_to _('Download'), download_plan_path(plan) %>
<% end %>
<% role = plan.roles.where(user_id: current_user.id).first %>
<% conf = (role.creator? && plan.publicly_visible?) ? _("Are you sure you wish to remove this public plan? This will remove it from the Public DMPs page but any collaborators will still be able to access it.") : _("Are you sure you wish to remove this plan? Any collaborators will still be able to access it.") %>
diff --git a/app/views/plans/show_export.html.erb b/app/views/plans/show_export.html.erb
deleted file mode 100644
index 3a2253a..0000000
--- a/app/views/plans/show_export.html.erb
+++ /dev/null
@@ -1,124 +0,0 @@
-<%- model_class = Plan -%>
-
-
-
-
-
<%= @plan.title %>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<%= _("Download Settings") %>
-
- <%= form_tag( export_plan_path(@plan), method: :get, html: {class: "roadmap-form"}) do |f| %>
- <% if @phase_options.length > 1 %>
-
- <%= label_tag(:phase_id, _("Select phase to download")) %>
- <%= select_tag(:phase_id, options_for_select(@phase_options, @phase_options[0])) %>
-
- <% else %>
- <%= hidden_field_tag(:phase_id, @phase_options[0][1]) %>
- <% end %>
- <%= _("Optional plan components") %>
-
- <%= check_box_tag 'export[project_details]', true, false %>
- <%= label_tag 'export[project_details]', _('project details coversheet'), class: 'checkbox-label' %>
-
-
- <%= check_box_tag 'export[question_headings]', true, true, class: 'question-headings'%>
- <%= label_tag 'export[question_headings]', _('question text and section headings'), class: 'checkbox-label' %>
-
-
- <%= check_box_tag 'export[unanswered_questions]', true, true, class: 'unanswered-questions' %>
- <%= label_tag 'export[unanswered_questions]', _('unanswered questions'), class: 'checkbox-label' %>
-
- <% if @plan.template.customization_of.present? %>
-
- <%= check_box_tag 'export[custom_sections]', true, false %>
- <%= label_tag 'export[custom_sections]', _('supplementary section(s) not requested by funding organisation'), class: 'checkbox-label' %>
-
- <% end %>
-
-
-
-
- <%= label_tag(:format, _('Format')) %>
- <%= select_tag(:format, options_for_select(ExportedPlan::VALID_FORMATS, :pdf), class: 'export-format-selection') %>
-
-
-
-
-
-
-
- <% end %>
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/views/questions/_new_edit_question_option_based.html.erb b/app/views/questions/_new_edit_question_option_based.html.erb
new file mode 100644
index 0000000..7a5a0c1
--- /dev/null
+++ b/app/views/questions/_new_edit_question_option_based.html.erb
@@ -0,0 +1,48 @@
+<% options = question.question_options.by_number %>
+<% if question.question_format.checkbox? %>
+ <%= f.label(:question_option_ids, raw(question.text), class: 'control-label') %>
+ <% options.each do |op| %>
+
+
+ <%= f.check_box(:question_option_ids, { multiple: true, checked: answer.has_question_option(op.id), disabled: readonly }, op.id, nil) %>
+ <%= raw op.text %>
+
+
+ <% end %>
+<% elsif question.question_format.radiobuttons? %>
+ <%= f.label(:question_option_ids, raw(question.text), class: 'control-label') %>
+ <% options.each do |op| %>
+
+
+ <%= f.radio_button :question_option_ids, op.id, { checked: answer.has_question_option(op.id), id: "answer_option_ids_#{op.id}", disabled: readonly } %>
+ <%= raw op.text %>
+
+
+ <% end %>
+<% elsif question.question_format.dropdown? || question.question_format.multiselectbox? %>
+ <%
+ options_html = ""
+ options.each do |op|
+ options_html += answer.has_question_option(op.id) ?
+ "
#{op.text} " :
+ "
#{op.text} "
+ end
+ %>
+ <%= f.label(:question_option_ids, raw(question.text), class: 'control-label') %>
+ <%= select_tag('answer[question_option_ids]',
+ raw(options_html),
+ { multiple: question.question_format.multiselectbox?,
+ include_blank: question.question_format.dropdown?,
+ disabled: readonly,
+ class: 'form-control' }) %>
+<% end %>
+
diff --git a/app/views/questions/_new_edit_question_textarea.html.erb b/app/views/questions/_new_edit_question_textarea.html.erb
new file mode 100644
index 0000000..c390574
--- /dev/null
+++ b/app/views/questions/_new_edit_question_textarea.html.erb
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/views/questions/_new_edit_question_textfield.html.erb b/app/views/questions/_new_edit_question_textfield.html.erb
new file mode 100644
index 0000000..b1dbd95
--- /dev/null
+++ b/app/views/questions/_new_edit_question_textfield.html.erb
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/views/sections/_progress.html.erb b/app/views/sections/_progress.html.erb
index 02c8d69..7eae314 100644
--- a/app/views/sections/_progress.html.erb
+++ b/app/views/sections/_progress.html.erb
@@ -3,6 +3,6 @@
<% num_section_answers = section.num_answered_questions(plan.id) %>
- <%= num_section_answers %> / <%= num_section_questions %>
+ (<%= num_section_answers %> / <%= num_section_questions %>)
\ No newline at end of file
diff --git a/app/views/shared/_plan_navigation.html.erb b/app/views/shared/_plan_navigation.html.erb
index 1c65b33..a6ffbd4 100644
--- a/app/views/shared/_plan_navigation.html.erb
+++ b/app/views/shared/_plan_navigation.html.erb
@@ -12,8 +12,8 @@
<%= _('Share') %>
-
- <%= _('Download') %>
+
+ <%= _('Download') %>
diff --git a/config/routes.rb b/config/routes.rb
index 343704b..218ec8f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -212,7 +212,7 @@
get 'warning'
get 'section_answers'
get 'share'
- get 'show_export'
+ get 'download'
post 'duplicate'
get 'export'
post 'invite'
diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js
index 58a9708..0a2fc35 100644
--- a/lib/assets/javascripts/application.js
+++ b/lib/assets/javascripts/application.js
@@ -1,7 +1,9 @@
+import './views/answers/status';
import './views/contacts/new';
import './views/devise/invitations/edit';
import './views/devise/passwords/edit';
import './views/devise/passwords/new';
import './views/phases/edit';
+import './views/plans/download';
import './views/plans/edit_details';
import './views/plans/share';
diff --git a/lib/assets/javascripts/views/answers/status.js b/lib/assets/javascripts/views/answers/status.js
index ae9b12d..55e3aa0 100644
--- a/lib/assets/javascripts/views/answers/status.js
+++ b/lib/assets/javascripts/views/answers/status.js
@@ -1,92 +1,130 @@
-$(document).ready(function(){
- /*--------------
- START Autosaving
- ----------------*/
- // debounced object holds a set of debounced functions, one for each form present in the page. Note,
- // each debounced function stored at funcs is created on demand, i.e. once the user changes any element of a form
- var debounced = (function(){
- var funcs = {};
- return {
- has: function(id){
- return funcs[id] !== undefined;
- },
- get: function(id){
+import {
+ isObject,
+ isNumber,
+ isString } from '../../utils/isType';
+import { Tinymce } from '../../utils/tinymce';
+import debounce from '../../utils/debounce';
- return funcs[id];
- },
- set: function(id, func){
- funcs[id] = dmproadmap.utils.debounce(func);
- }
- }
- })();
- // This function triggers a form submit, if and only if the answer has not been optimistically locked
- var autoSaving=function(){
- if($(this).closest('.question-form').find('.answer-locking').children().length === 0){
- $(this).closest('form.answer').submit();
- }
- };
- var listenersForEditor=function(editor){
- editor.on('change', function(){
- var notAnswered = $('#'+editor.id).closest('.question-form').find('.not-answered');
- notAnswered.hide();
- });
- editor.on('blur', function(){
- var id = $('#'+editor.id).closest('form.answer').attr('data-autosave');
- $('#'+editor.id).val(editor.getContent()); //Updates target element of tinyMCE.editor with its content
- if(!debounced.has(id)){
- debounced.set(id, autoSaving);
- }
- debounced.get(id).apply($('#'+editor.id),[id]);
- });
- editor.on('focus', function(){
- var id = $('#'+editor.id).closest('form.answer').attr('data-autosave');
- if(debounced.has(id)){
- debounced.get(id).cancel(); //Cancels the execution of its debounced function either because user transitioned from question with options
- // to the comments or because textarea lost focus and gained again before the delay being met
- }
- });
+$(() => {
+ /*
+ * Shows the closest saving-message HTML element within a question-form
+ * @param { Strin } selector - A valid CSS selector to look for
+ * @return { jQuery }
+ */
+ const showSavingMessage = selector => $(selector).closest('.question-form').find('.saving-message').show();
+ /*
+ * Hides the closest not-answered HTML element within a question-form
+ * @param { String } selector - A valid CSS selector to look for
+ * @return { jQuery }
+ */
+ const hideNotAnswered = selector => $(selector).closest('.question-form').find('.not-answered').hide();
+ /*
+ * Retrieves the question id for the closest form-answer
+ * @param { String } selector - A valid CSS selector to look for
+ * @return { String } representing the question id for a given answer, otherwise undefined
+ */
+ const questionId = selector => $(selector).closest('.form-answer').attr('data-autosave');
+ /*
+ * A map of debounced functions, one for each input, textarea or select change at any
+ * form with class form-answer. The key represents a question id and the value holds
+ * the debounced function for a given input, textarea or select. Note, this map is
+ * populated on demand, i.e. the first time a change is made at a given input, textarea
+ * or select within the form, a new key-value should be created. Succesive times, the
+ * debounced function should be retrieved instead.
+ */
+ const debounceMap = {};
+ const autoSaving = (selector) => {
+ if ($(selector).closest('.question-form').find('.answer-locking').html().length === 0) {
+ $(selector).closest('.form-answer').trigger('submit');
}
- /*--------------
- END Autosaving
- ----------------*/
- // Listener for submit event triggered
- $('.question-form').on('submit', 'form.answer', function(){
- var id = $(this).attr('data-autosave');
- if(debounced.has(id)){
- debounced.get(id).cancel(); //Cancels the execution of its debounced function, if not already, since submit() could have been trigerred through Save button
- }
- var container = $(this).closest('.question-form');
- var saving = container.find('.saving-message');
- saving.show();
+ };
+ // Initialises tinymce for any target element with class tinymce_answer
+ Tinymce.init({ selector: '.tinymce_answer' });
+ // Listeners for change, blur and focus at any target element with class tinymce_answer
+ Tinymce.findEditorsByClassName('tinymce_answer').forEach((editor) => {
+ editor.on('Change', () => {
+ hideNotAnswered(`#${editor.id}`);
});
- // Listener for changes at any element value from question-form
- $('.question-form').on('change', 'form.answer fieldset input, form.answer fieldset select', function(){
- var notAnswered = $(this).closest('.question-form').find('.not-answered');
- notAnswered.hide();
+ editor.on('Blur', () => {
+ const id = questionId(`#${editor.id}`);
+ $(`#${editor.id}`).val(editor.getContent()); // Updates target element of editor with its content
+ if (!debounceMap[id]) {
+ debounceMap[id] = debounce(autoSaving);
+ }
+ debounceMap[id]($(`#${editor.id}`));
});
- // Listener for changes at any element value from question-form. This triggers the debounced function
- $('.question-form').on('change', 'form.answer fieldset input, form.answer fieldset select', function(){
- var id = $(this).closest('form.answer').attr('data-autosave');
- if(!debounced.has(id)){
- debounced.set(id, autoSaving);
- }
- debounced.get(id).apply($(this),[id]);
+ editor.on('Focus', () => {
+ const id = questionId(`#${editor.id}`);
+ if (debounceMap[id]) {
+ /* Cancels the delayed execution of autoSaving, either because user
+ * transitioned from an option_based question to the comment or
+ * because the target element triggered blur and focus before
+ * the delayed execution of autoSaving.
+ */
+ debounceMap[id].cancel();
+ }
});
- // Init function to add listeners for every tinyMCE editor whose target element class is tinymce_answer
- (function(){
- var editors = dmproadmap.utils.tinymce.findEditorsByClassName('tinymce_answer');
- editors.forEach(listenersForEditor);
- // Initialises timeago for each element abbr with class timeago
- $('abbr.timeago').timeago();
- })();
- (function(ctx){
- // function to add listeners for a tinyMCE editor with target element id passed
- ctx.reloadEditorListeners = ctx.reloadEditorListeners || (function(id){
- var editor = dmproadmap.utils.tinymce.findEditorById(id);
- if(editor){
- listenersForEditor(editor);
- $('abbr.timeago').timeago();
+ });
+ // Listener for input or select field
+ $('.question-form').on('change', 'form.answer fieldset input, form.answer fieldset select', (e) => {
+ hideNotAnswered(e.target);
+ const id = questionId(e.target);
+ if (!debounceMap[id]) {
+ debounceMap[id] = debounce(autoSaving);
+ }
+ debounceMap[id]($(e.target));
+ });
+ // Listener for submit button
+ $('.form-answer').on('submit', (e) => {
+ e.preventDefault();
+ const id = questionId(e.target);
+ if (debounceMap[id]) {
+ // Cancels the delated execution of autoSaving
+ // (e.g. user clicks the button before the delay is met)
+ debounceMap[id].cancel();
+ }
+ showSavingMessage(e.target);
+ const formElements = $(e.target).closest('.form-answer').serializeArray();
+ const answerId = formElements.find(el => el.name === 'answer[id]');
+ if (answerId) {
+ // TODO centralise AJAX calls
+ $.ajax({
+ method: 'PUT',
+ url: `/answers/${answerId}`,
+ data: formElements,
+ }).done((data) => {
+ // Validation for the data object received
+ if (isObject(data)) {
+ if (isObject(data.question)) { // Object related to question within data received
+ if (isNumber(data.question.id)) {
+ if (isString(data.question.answer_status)) {
+ $(`#answer-status-${data.question.id}`).html(data.question.answer_status); // TODO check partial render of this view on the server
+ $('abbr.timeago').timeago();
+ }
+ if (isString(data.question.locking)) {
+ $(`#answer-locking-${data.question.id}`).html(data.question.locking);
+ }
+ if (isNumber(data.question.answer_lock_version)) {
+ $(e.target).closest('.form-answer').find('#answer_lock_version').val(data.question.answer_lock_version);
+ }
}
- });
- })(define('dmproadmap.answers.status'));
-});
\ No newline at end of file
+ }
+ if (isObject(data.plan)) { // Object related to plan within data received
+ if (isString(data.plan.progress)) {
+ $('.progress').html(data.plan.progress);
+ }
+ }
+ if (isObject(data.section)) { // Object related to section within data received
+ if (isNumber(data.section.id)) {
+ if (isString(data.section.progress)) {
+ $(`.section-progress-${data.section.id}`).html(data.section.progress);
+ }
+ }
+ }
+ }
+ }, () => {
+ // TODO adequate error handling for network error
+ });
+ }
+ });
+});
diff --git a/lib/assets/javascripts/views/phases/edit.js b/lib/assets/javascripts/views/phases/edit.js
index ffab5ac..f771160 100644
--- a/lib/assets/javascripts/views/phases/edit.js
+++ b/lib/assets/javascripts/views/phases/edit.js
@@ -1,8 +1,16 @@
+import 'bootstrap-sass/assets/javascripts/bootstrap/collapse';
import expandCollapseAll from '../../utils/expandCollapseAll';
$(() => {
// Attach handlers for the expand/collapse all accordions
expandCollapseAll();
+ $('a[data-toggle="collapse"').click((e) => {
+ if ($(e.target).hasClass('fa-plus')) {
+ $(e.target).removeClass('fa-plus').addClass('fa-minus');
+ } else {
+ $(e.target).removeClass('fa-minus').addClass('fa-plus');
+ }
+ });
});
/*
$(document).ready(function(){
diff --git a/lib/assets/javascripts/views/plans/download.js b/lib/assets/javascripts/views/plans/download.js
new file mode 100644
index 0000000..2f5af98
--- /dev/null
+++ b/lib/assets/javascripts/views/plans/download.js
@@ -0,0 +1,10 @@
+$(() => {
+ // Hide the PDF Formatting section if 'pdf' is not the desired format
+ $('select#format').on('change', (e) => {
+ if ($(e.currentTarget).val() === 'pdf') {
+ $('#pdf-formatting').show();
+ } else {
+ $('#pdf-formatting').hide();
+ }
+ });
+});
diff --git a/lib/assets/javascripts/views/plans/export_configure.js b/lib/assets/javascripts/views/plans/export_configure.js
deleted file mode 100644
index 5e7bf31..0000000
--- a/lib/assets/javascripts/views/plans/export_configure.js
+++ /dev/null
@@ -1,131 +0,0 @@
-$(document).ready(function() {
-
- // Prevent the click handler from being registered multiple times.
- // This is due to the buggy way this is included.
- if (window['has_export_js'])
- return;
-
- window['has_export_js'] = true;
-
- $.expr.filters.indeterminate = function(element) {
- return $(element).prop('indeterminate');
- };
-
- $("select#format").change(function(){
- if ($(this).val() == 'pdf') {
- $("#pdf-format-options").show();
- $("#settings-toggle > small").show();
- }
- else {
- $("#pdf-format-options").hide();
- $("#settings-toggle > small").hide();
- }
- });
-
- $("input:checkbox, select:not(#format)").change(function(){
- $(".unsaved_changes_alert").show();
- });
-
- $("select:not(#format)").change(function(){
- $(".unsaved_changes_alert").show();
- });
-
- $('.check_select > legend').append(' ');
-
- $('.resetbutton').click(function(){
- $('input:checkbox').prop('checked',true);
- $("select:not(#format)").each(function(){
- $(this).val($(this).data("default"));
- });
- $(".unsaved_changes_alert").hide();
- $("#settings-toggle > small").text(__('(Using template PDF formatting values)'));
- });
-
- $('.savebutton').click(function(){
- var custom = false;
- $("select:not(#format)").each(function(){
- if ($(this).val() != $(this).data("default")) {
- custom = true;
- }
- });
- if (custom) {
- $("#settings-toggle > small").text(__('(Using custom PDF formatting values)'));
- }
- else {
- $("#settings-toggle > small").text(__('(Using template PDF formatting values)'));
- }
- $(".unsaved_changes_alert").hide();
- });
-
- $('.check_select').each(function() {
- var container = $(this),
- toggle = container.find('> legend > .toggle'),
- checks = container.find('> ol > li > input[type=checkbox], li > fieldset > legend > input[type=checkbox]');
-
-
- function checked(toggle) {
- var checks = toggle.prop('checks'),
- checked = checks.filter(':checked').length,
- indeterminate = checks.filter(':indeterminate').length;
-
- return {
- 'indeterminate' : ((checked > 0 && checked < checks.length) || indeterminate > 0),
- 'checked' : (checked == checks.length)
- };
- }
-
- function toggleParent(toggle) {
- var parent_toggle = toggle.prop('toggle');
-
- if (parent_toggle)
- parent_toggle.prop(checked(parent_toggle));
- }
-
- checks.prop('toggle', toggle);
- toggle.prop('checks', checks);
- toggle.prop('id', container.find('> legend > label').prop('for'));
- toggle.prop(checked(toggle));
- toggleParent(toggle);
-
- checks.change(function() {
- toggle.prop(checked(toggle));
- toggleParent(toggle);
- });
-
- toggle.change(function() {
- $(".unsaved_changes_alert").show();
- checks.prop({ 'checked': toggle.is(':checked'), 'indeterminate': false});
-
- checks.each(function() {
- var child_checks = $(this).prop('checks');
-
- if (child_checks)
- child_checks.prop({ 'checked': toggle.is(':checked'), 'indeterminate': toggle.is(':indeterminate') });
-
- });
- });
- });
- /*----------------
- Listener for select that displays the formatting options (e.g. csv, html, pdf, txt, etc.)
- ------------------*/
- $('.export-format-selection').click(function(e){
- e.preventDefault();
- if($(this).val() === 'pdf'){
- $('#pdf-format-options').show();
- }else{
- $('#pdf-format-options').hide();
- }
- });
-
- /*----------------
- Listener for select that disables the unanswered questions
- ------------------*/
- $('.question-headings').click(function(e){
- if($(this).is(':checked')){
- $('.unanswered-questions').removeAttr("disabled");
- }else{
- $('.unanswered-questions').prop("checked", false);
- $('.unanswered-questions').prop("disabled", true);
- }
- });
-});
\ No newline at end of file
diff --git a/lib/assets/javascripts/views/plans/share.js b/lib/assets/javascripts/views/plans/share.js
index 7b54f68..e85491e 100644
--- a/lib/assets/javascripts/views/plans/share.js
+++ b/lib/assets/javascripts/views/plans/share.js
@@ -1,6 +1,6 @@
import ariatiseForm from '../../utils/ariatiseForm';
-$().ready(() => {
+$(() => {
// Invite Collaborators form on the Share page
ariatiseForm({ selector: '#new_role' });
});
diff --git a/lib/assets/stylesheets/application.scss b/lib/assets/stylesheets/application.scss
index 420a724..1df72cd 100644
--- a/lib/assets/stylesheets/application.scss
+++ b/lib/assets/stylesheets/application.scss
@@ -19,3 +19,7 @@
@import "dmproadmap/tables";
@import "dmproadmap/forms";
*/
+
+[class^="bg-"] {
+ padding: 15px;
+}
diff --git a/lib/assets/webpack.config.js b/lib/assets/webpack.config.js
index 71f1ec7..6e20570 100644
--- a/lib/assets/webpack.config.js
+++ b/lib/assets/webpack.config.js
@@ -15,7 +15,7 @@
context: __dirname,
entry: {
- vendor: ['jquery'],
+ vendor: ['jquery', 'timeago/jquery.timeago'],
application: ['./javascripts/application.js', './stylesheets/application.scss'],
},
diff --git a/test/functional/answers_controller_test.rb b/test/functional/answers_controller_test.rb
index 6ed2db9..372ed47 100644
--- a/test/functional/answers_controller_test.rb
+++ b/test/functional/answers_controller_test.rb
@@ -69,11 +69,9 @@
private
def put_answer(answer, attributes, referrer)
- put answer_path(FastGettext.locale, answer, format: "js"), attributes, {'HTTP_REFERER': referrer}
+ put answer_path(FastGettext.locale, answer, format: "json"), attributes, {'HTTP_REFERER': referrer}
assert_response :success
- assert_equal "text/javascript", @response.content_type
-
-# assert_match(/[^\$]*\$\("#answer-locking-[0-9]+"\).html\(""\);[^\$]*\$\("#answer-form-[0-9]+"\)[^\.]*.html\(".+"\);[^\$]*\$\("#answer-status-[0-9]+"\)[^.]*.html\(".+"\);[^\$]*\$.[^$]*\$.[^\$]*\$\(".progress"\).html\(".+"\);[^\$]*\$\("#section-progress-[0-9]+"\)[^.]*.html\(".+"\);/, @response.body)
+ assert_equal "application/json", @response.content_type
end
end
diff --git a/test/functional/plans_controller_test.rb b/test/functional/plans_controller_test.rb
index 6de8db2..a17531e 100644
--- a/test/functional/plans_controller_test.rb
+++ b/test/functional/plans_controller_test.rb
@@ -290,14 +290,14 @@
# TODO: We need some better tests here to check the different formats!
end
- # GET /plans/:id/show_export (show_export_plan_path)
+ # GET /plans/:id/download (download_plan_path)
# ----------------------------------------------------------
- test "show the export the plan page" do
+ test "show the download 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))
+ try_no_user_and_unauthorized(download_plan_path(@plan))
sign_in @user
- get show_export_plan_path(@plan)
+ get download_plan_path(@plan)
assert_response :success
assert assigns(:plan)
end
diff --git a/test/integration/answer_locking_test.rb b/test/integration/answer_locking_test.rb
index 0c0bb0f..6dfe674 100644
--- a/test/integration/answer_locking_test.rb
+++ b/test/integration/answer_locking_test.rb
@@ -26,9 +26,9 @@
# Signin as UserA and insert the new answer
sign_in @plan.owner
- put answer_path(FastGettext.locale, userA, format: "js"), obj_to_params(userA.attributes)
+ put answer_path(FastGettext.locale, userA, format: "json"), obj_to_params(userA.attributes)
assert_response :success
- assert_equal "text/javascript", @response.content_type
+ assert_equal "application/json", @response.content_type
updated = Answer.find_by(plan: @plan, question: @question)
assert_equal "Initial answer - by UserA", updated.text
assert_equal @plan.owner.id, updated.user_id
@@ -40,9 +40,9 @@
# Signin as UserB and try to insert the new answer but fail
sign_in @collaborator
- put answer_path(FastGettext.locale, userB, format: "js"), obj_to_params(userB.attributes)
+ put answer_path(FastGettext.locale, userB, format: "json"), obj_to_params(userB.attributes)
assert_response :success
- assert_equal "text/javascript", @response.content_type
+ assert_equal "application/json", @response.content_type
updated = Answer.find_by(plan: @plan, question: @question)
assert_equal "Initial answer - by UserA", updated.text
assert_equal @plan.owner.id, updated.user_id
@@ -63,9 +63,9 @@
sign_in @plan.owner
userA['text'] += " - Updated by userA"
- put answer_path(FastGettext.locale, userA['id'], format: "js"), obj_to_params(userA)
+ put answer_path(FastGettext.locale, userA['id'], format: "json"), obj_to_params(userA)
assert_response :success
- assert_equal "text/javascript", @response.content_type
+ assert_equal "application/json", @response.content_type
updated = Answer.find_by(plan: @plan, question: @question)
assert_equal "Initial answer - by UserA - Updated by userA", updated.text
assert_equal @plan.owner.id, updated.user_id
@@ -79,9 +79,9 @@
sign_in @collaborator
userB['text'] += " - Updated by userB"
- put answer_path(FastGettext.locale, userB['id'], format: "js"), obj_to_params(userB)
+ put answer_path(FastGettext.locale, userB['id'], format: "json"), obj_to_params(userB)
assert_response :success
- assert_equal "text/javascript", @response.content_type
+ assert_equal "application/json", @response.content_type
updated = Answer.find_by(plan: @plan, question: @question)
assert_equal "Initial answer - by UserA - Updated by userA", updated.text
assert_equal @plan.owner.id, updated.user_id