- <%= form_for @plan, html: {method: :post, class: "roadmap-form padded bordered"}, remote: true do |f| %>
-
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 -%>
-
-
\ 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| %>
+
+ <% end %>
+<% elsif question.question_format.radiobuttons? %>
+ <%= f.label(:question_option_ids, raw(question.text), class: 'control-label') %>
+ <% options.each do |op| %>
+
+ <% end %>
+<% elsif question.question_format.dropdown? || question.question_format.multiselectbox? %>
+ <%
+ options_html = ""
+ options.each do |op|
+ options_html += answer.has_question_option(op.id) ?
+ "
"
+ 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) %>
\ No newline at end of file
diff --git a/app/views/shared/_accessible_combobox.html.erb b/app/views/shared/_accessible_combobox.html.erb
index 6b32337..f2ef99f 100644
--- a/app/views/shared/_accessible_combobox.html.erb
+++ b/app/views/shared/_accessible_combobox.html.erb
@@ -1,10 +1,13 @@
<% if !models.nil? %>
+ <% required = required ||= false %>
+ <% classes = classes ||= '' %>
+ <% error = error ||= _('Please select an item from the list.') %>
+
<% json = {} %>
<% models.map{|m| json[m[attribute]] = m.id} %>
- <% err_msg = error ||= _('Please select an item from the list.') %>
" name="<%= name.gsub("_#{attribute}]", "_id]") %>"
- value="<%= default_selection.id unless default_selection.nil? %>" />
+ value="<%= default_selection.id unless default_selection.nil? %>" aria-required="<%= required %>"
+ data-validation="js-combobox" data-validation-error="<%= error %>" />
-
<% end %>
\ 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 @@
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..9006f60 100644
--- a/lib/assets/javascripts/application.js
+++ b/lib/assets/javascripts/application.js
@@ -1,7 +1,10 @@
+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/new';
import './views/plans/share';
diff --git a/lib/assets/javascripts/constants.js b/lib/assets/javascripts/constants.js
index 1ac6267..25721c0 100644
--- a/lib/assets/javascripts/constants.js
+++ b/lib/assets/javascripts/constants.js
@@ -7,6 +7,7 @@
export const VALIDATION_MESSAGE_PASSWORD = 'The password must be between 8 and 128 characters.';
export const VALIDATION_MESSAGE_PASSWORDS_MATCH = 'The passwords must match.';
export const VALIDATION_MESSAGE_RADIO = 'Please choose one of the options.';
+export const VALIDATION_MESSAGE_SELECT = "Please select a value from the list.";
export const VALIDATION_MESSAGE_TEXT = 'This field is required.';
export const SHOW_PASSWORD_MESSAGE = 'Show password';
diff --git a/lib/assets/javascripts/spec/autoCompleteSpec.js b/lib/assets/javascripts/spec/autoCompleteSpec.js
new file mode 100644
index 0000000..08965ea
--- /dev/null
+++ b/lib/assets/javascripts/spec/autoCompleteSpec.js
@@ -0,0 +1,26 @@
+import initAutoComplete from '../utils/autoComplete';
+
+describe('autoComplete test suite', () => {
+ beforeAll(() => fixture.setBase('javascripts/spec/fixtures'));
+
+ beforeEach(() => {
+ $('body').html(fixture.load('autoComplete.html'));
+ initAutoComplete();
+ // Override the form submission, we are just going to validate the ariatisation of the form
+ $('form').submit((e) => { e.preventDefault(); });
+ });
+
+ afterEach(() => {
+ fixture.cleanup();
+ $('body').html('');
+ });
+
+ it('shows/hides the clear button correctly', () => {
+
+ });
+
+ it('Selects the correct id based on the item selected in the combobox', () => {
+
+ });
+
+});
diff --git a/lib/assets/javascripts/spec/expandCollapseAllSpec.js b/lib/assets/javascripts/spec/expandCollapseAllSpec.js
new file mode 100644
index 0000000..71c129a
--- /dev/null
+++ b/lib/assets/javascripts/spec/expandCollapseAllSpec.js
@@ -0,0 +1,48 @@
+import expandCollapseAll from '../utils/expandCollapseAll';
+
+describe('expandCollapseAll test suite', () => {
+ beforeAll(() => fixture.setBase('javascripts/spec/fixtures'));
+
+ beforeEach(() => {
+ this.form = fixture.load('accordion.html');
+ expandCollapseAll({ selector: '#accordion' });
+ });
+
+ afterEach(() => {
+ fixture.cleanup();
+ });
+
+ it('should be able to expand all sections when all are either expanded or collapsed', () => {
+ // Collapse all of the sections
+ // - click on 'collapse all' should have no effect
+ // - click on 'expand all' should expand all sections
+ $('#accordion div.panel-collapse').collapse('hide');
+ expect($('.in').length === 0);
+ $('a[data-toggle-direction="hide"]').click();
+ expect($('.in').length === 0);
+ $('a[data-toggle-direction="show"]').click();
+ expect($('.in').length === 3);
+
+ // Expand all of the sections
+ // - click on 'expand all' should have no effect
+ // - click on 'collapse all' should collapse all sections
+ $('#accordion div.panel-collapse').collapse('show');
+ expect($('.in').length === 3);
+ $('a[data-toggle-direction="show"]').click();
+ expect($('.in').length === 3);
+ $('a[data-toggle-direction="hide"]').click();
+ expect($('.in').length === 0);
+ });
+
+ it('should be able to expand all sections when some are open and some collapsed', () => {
+ // Expand 2 of the 3 sections - click 'collapse all' - verify that all are collapsed
+ $('#collapseA, #collapseC').collapse('show');
+ $('a[data-toggle-direction="hide"]').click();
+ expect($('.in').length === 0);
+
+ // Expand 2 of the 3 sections - click 'expand all' - verify that all are expanded
+ $('#collapseA, #collapseC').collapse('show');
+ $('a[data-toggle-direction="show"]').click();
+ expect($('.in').length === 3);
+ });
+});
diff --git a/lib/assets/javascripts/spec/fixtures/accordion.html b/lib/assets/javascripts/spec/fixtures/accordion.html
new file mode 100644
index 0000000..78cb5aa
--- /dev/null
+++ b/lib/assets/javascripts/spec/fixtures/accordion.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+ This is test section A.
+
+
+
+
+
+
+ This is test section B.
+
+
+
+
+
+
+ This is test section C.
+
+
+
+
\ No newline at end of file
diff --git a/lib/assets/javascripts/spec/fixtures/autoComplete.html b/lib/assets/javascripts/spec/fixtures/autoComplete.html
new file mode 100644
index 0000000..7c3ce2f
--- /dev/null
+++ b/lib/assets/javascripts/spec/fixtures/autoComplete.html
@@ -0,0 +1,35 @@
+
\ No newline at end of file
diff --git a/lib/assets/javascripts/utils/ariatiseForm.js b/lib/assets/javascripts/utils/ariatiseForm.js
index fdd417a..2db61b3 100644
--- a/lib/assets/javascripts/utils/ariatiseForm.js
+++ b/lib/assets/javascripts/utils/ariatiseForm.js
@@ -103,12 +103,14 @@
return validator.isValidPassword(value);
case 'radio':
return validator.isValidText(value);
+ case 'js-combobox':
+ return validator.isValidText(value);
default:
return false;
}
};
-const getValidationMessage = (type) => {
+const getDefaultValidationMessage = (type) => {
switch (type) {
case 'text':
return constants.VALIDATION_MESSAGE_TEXT;
@@ -120,11 +122,21 @@
return constants.VALIDATION_MESSAGE_PASSWORD;
case 'radio':
return constants.VALIDATION_MESSAGE_RADIO;
+ case 'js-combobox':
+ return constants.VALIDATION_MESSAGE_SELECT;
default:
return constants.VALIDATION_MESSAGE_DEFAULT;
}
};
+const getValidationMessage = (el) => {
+ if ($(el).attr('data-validation-error')) {
+ return $(el).attr('data-validation-error');
+ }
+ // Use the default validation error message if none was specified
+ return getDefaultValidationMessage(getValidationTypeForElement(el));
+};
+
const valid = (el) => {
$(el).parent().removeClass(validationStates.hasError);
$(el).attr(ariaInvalid(false));
@@ -142,9 +154,8 @@
// Add validation error message sections for each validatable input element
validatable.each((i, el) => {
- const type = getValidationTypeForElement(el);
$(el).attr(ariaDescribedBy(`help${i}`));
- $(el).after(blockHelp(`help${i}`, getValidationMessage(type)));
+ $(el).after(blockHelp(`help${i}`, getValidationMessage(el)));
});
// Bind validations to the form's submit button
@@ -160,7 +171,7 @@
valid(el);
} else {
anyInvalid = true;
- invalid(el, getValidationMessage(type));
+ invalid(el);
}
}
});
diff --git a/lib/assets/javascripts/utils/autoComplete.js b/lib/assets/javascripts/utils/autoComplete.js
new file mode 100644
index 0000000..3660dba
--- /dev/null
+++ b/lib/assets/javascripts/utils/autoComplete.js
@@ -0,0 +1,59 @@
+import 'jquery-accessible-autocomplete-list-aria/jquery-accessible-autocomplete-list-aria';
+import debounce from '../utils/debounce';
+
+/*
+ * Looks up the id for the text selected by the user in the jquery autocomplete combobox and
+ * then sets updates the hidden id field with the id value so that its available on form submit.
+ * The id-text mappings are stored as JSON in the corresponding hidden crosswalk field
+ * @param the combobox element
+ */
+const updateIdField = (el) => {
+ const crosswalk = $(`#${$(el).attr('id')}_crosswalk`);
+ const idField = $(el).attr('id').replace(/_name/, '_id');
+
+ if (crosswalk && idField) {
+ const json = JSON.parse(`${$(crosswalk).val().replace(/\\"/g, '"').replace(/\\'/g, '\'')}`);
+ const selection = json[$(el).val()];
+ $(idField).val(selection === 'undefined' ? '' : selection).change();
+ }
+};
+
+/*
+ * Shows/hides the combobox's clear button based on whether or not text is present
+ * @param the combobox id
+ */
+const toggleClearButton = (el) => {
+ const clearButton = $(el).parent().find('.combobox-clear-button');
+ if ($(el).val().trim().length <= 0) {
+ $(clearButton).addClass('hidden');
+ } else {
+ $(clearButton).removeClass('hidden');
+ }
+};
+
+/*
+ * Wires up the jquery autocomplete combobox so that it calls the above 2 functions when the
+ * user changes the text values in the combobox by typing or selecting a value
+ */
+export default () => {
+ $('.js-combobox').each((idx, el) => {
+ const debounced = debounce((e) => {
+ toggleClearButton(e);
+ updateIdField(e);
+ }, 500);
+
+ // When the value in the combobox changes update the hidden id field
+ $(el).on('keyup', (e) => {
+ debounced($(e.currentTarget));
+ });
+
+ // Clear the text and hide the button when the user clicks the clear button
+ $(el).parent().find('.combobox-clear-button').on('click', () => {
+ $(el).val('').focus();
+ debounced($(el));
+ });
+
+ // Show/hide the clear button on page load
+ toggleClearButton(el);
+ });
+};
\ No newline at end of file
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/new.js b/lib/assets/javascripts/views/plans/new.js
index 05ea6c6..dd7c547 100644
--- a/lib/assets/javascripts/views/plans/new.js
+++ b/lib/assets/javascripts/views/plans/new.js
@@ -1,3 +1,12 @@
+import ariatiseForm from '../../utils/ariatiseForm';
+import initAutoComplete from '../../utils/autoComplete';
+
+$().ready(() => {
+ initAutoComplete();
+ ariatiseForm({ selector: '#create-plan' });
+});
+
+/*
$(document).ready(function(){
$("#available-templates").hide();
@@ -73,3 +82,4 @@
$("#plan_" + name + "_name").siblings(".combobox-clear-button").hide();
}
}
+*/
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/package-lock.json b/lib/assets/package-lock.json
index 10cb86b..d43dad4 100644
--- a/lib/assets/package-lock.json
+++ b/lib/assets/package-lock.json
@@ -4652,9 +4652,7 @@
"integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c="
},
"jquery-accessible-autocomplete-list-aria": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/jquery-accessible-autocomplete-list-aria/-/jquery-accessible-autocomplete-list-aria-1.5.5.tgz",
- "integrity": "sha1-2EG0wfBwSQaGcri1FsIkhUaPi4s=",
+ "version": "github:nico3333fr/jquery-accessible-autocomplete-list-aria#38a057140ccafa9a1c8a948b1bbf4a410c0181c6",
"requires": {
"jquery": "3.2.1"
}
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