true
if the form is dirty, false
+ * otherwise.
+ */
+formIsDirty = function formIsDirty(form) {
+ for (var i = 0; i < form.elements.length; i++) {
+ var element = form.elements[i];
+ var type = element.type;
+ if (type == "checkbox" || type == "radio") {
+ if (element.checked != element.defaultChecked) {
+ return true;
+ }
+ }
+ else if (type == "hidden" || type == "password" ||
+ type == "text" || type == "textarea" ||
+ type == "number" ) {
+ if (element.value != element.defaultValue) {
+ return true;
+ }
+ }
+ else if (type == "select-one" || type == "select-multiple") {
+ for (var j = 0; j < element.options.length; j++) {
+ if (element.options[j].selected !=
+ element.options[j].defaultSelected) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
diff --git a/app/client/views/autoform/textarea.coffee b/app/client/views/autoform/textarea.coffee
new file mode 100644
index 0000000..076ff40
--- /dev/null
+++ b/app/client/views/autoform/textarea.coffee
@@ -0,0 +1,33 @@
+#work around autoform's missing defaultValues
+textareaRendered = ->
+ e = @$('textarea')[0]
+ e.defaultValue = e.value
+ return
+
+Template.afTextarea.rendered = textareaRendered
+Template.afTextarea_bootstrap3.rendered = textareaRendered
+
+
+inputRendered = ->
+ e = @$('input')[0]
+ e.defaultValue = e.value
+ return
+
+Template.afInputNumber.rendered = inputRendered
+Template.afInputNumber_bootstrap3.rendered = inputRendered
+
+afBootstrapDatepickerRendered = Template.afBootstrapDatepicker.rendered
+Template.afBootstrapDatepicker.rendered = ->
+ afBootstrapDatepickerRendered.call @
+ self = @
+ @autorun ->
+ data = Template.currentData()
+ inputRendered.call self
+
+afBootstrapDateTimePickerRendered = Template.afBootstrapDateTimePicker.rendered
+Template.afBootstrapDateTimePicker.rendered = ->
+ afBootstrapDateTimePickerRendered.call @
+ self = @
+ @autorun ->
+ data = Template.currentData()
+ inputRendered.call self
diff --git a/app/client/views/patients/patient_visit.coffee b/app/client/views/patients/patient_visit.coffee
index e27d319..2d8305f 100644
--- a/app/client/views/patients/patient_visit.coffee
+++ b/app/client/views/patients/patient_visit.coffee
@@ -1,47 +1,47 @@
waitingForPatientId = null
waitingForDesignVisitId = null
Template.patientVisit.rendered = ->
@autorun ->
data = Template.currentData()
patientId = data.patient._id
designVisitId = Session.get 'selectedDesignVisitId'
return if not designVisitId?
v = Visits.findOne
designVisitId: designVisitId
if not v? and (waitingForPatientId isnt patientId or waitingForDesignVisitId isnt designVisitId)
#console.log 'initVisit'
waitingForPatientId = patientId
waitingForDesignVisitId = designVisitId
Meteor.call "initVisit", designVisitId, patientId, (error, _id) ->
throwError error if error?
Template.patientVisit.helpers
#this templateData
visit: ->
designVisitId = Session.get 'selectedDesignVisitId'
v = Visits.findOne
designVisitId: designVisitId
v.validatedDoc() if v?
#with questionnaire=this visit=.. patient=../../patient
questionnaireCSS: ->
return "valid" if @questionnaire.answered
"invalid"
Template.patientVisit.events
#with questionnaire=this visit=.. patient=../../patient
"click .answerQuestionnaire": (evt, tmpl) ->
- Modal.show('questionnaireWizzard', @)
+ Modal.show('questionnaireWizzard', @, keyboard: false)
false
#this: {questionnaire, visit, patient}
"click .showQuestionnaire": (evt, tmpl) ->
data =
questionnaire: @questionnaire
visit: @visit
patient: @patient
readonly: true
- Modal.show('questionnaireWizzard', data)
+ Modal.show('questionnaireWizzard', data, keyboard: false)
false
diff --git a/app/client/views/questionnaires/questionnaire_wizzard.coffee b/app/client/views/questionnaires/questionnaire_wizzard.coffee
index 28a5816..cfaa4a9 100644
--- a/app/client/views/questionnaires/questionnaire_wizzard.coffee
+++ b/app/client/views/questionnaires/questionnaire_wizzard.coffee
@@ -1,249 +1,317 @@
_numQuestions = new ReactiveVar(0)
_numPages = new ReactiveVar(0)
_questionIdsForPage = new ReactiveVar({})
_pageIndex = new ReactiveVar(0)
_numFormsToSubmit = 0
_readonly = ReactiveVar(false)
-nextPage = ->
- if _pageIndex.get() is _numPages.get()-1
- Modal.hide('viewQuestionnaire')
- else
- _pageIndex.set _pageIndex.get()+1
-previousPage = ->
- index = _pageIndex.get()
- index -= 1 if index > 0
- _pageIndex.set index
+isAFormDirty = ->
+ if _readonly.get()
+ return false
+ isDirty = false
+ $("form").each () ->
+ return if isDirty
+ e = $(@)[0]
+ dirty = formIsDirty(e)
+ isDirty = dirty if dirty
+ isDirty
-_gotoNextPage = null
-submitAllForms = (gotoNextPage) ->
+_goto = null
+submitAllForms = (goto) ->
if _readonly.get()
throw new Error("Can't submitAllForms because _readonly == true")
- _gotoNextPage = gotoNextPage
+ _goto = goto
numFormsToSubmit = 0
$("form").each () ->
e = $(@)
classes = e.attr('class')
if classes? and classes.indexOf('question') > -1
numFormsToSubmit += 1
_numFormsToSubmit = numFormsToSubmit
$("form").each () ->
e = $(@)
classes = e.attr('class')
if classes? and classes.indexOf('question') > -1
e.submit()
formSubmitted = ->
if (_numFormsToSubmit -= 1) <= 0
- if _gotoNextPage
+ if _goto is 'nextPage'
nextPage()
- else
+ else if _goto is 'previousPage'
previousPage()
+ else if _goto is 'close'
+ Modal.hide('questionnaireWizzard')
+ else if _goto.pageIndex?
+ _pageIndex.set _goto.pageIndex
+
+
+close = ->
+ if isAFormDirty()
+ swal {
+ title: 'Unsaved Changes'
+ text: "Do you want to save the changes on this page?"
+ type: 'warning'
+ showCancelButton: true
+ confirmButtonText: 'Save and exit'
+ cancelButtonText: "Exit without saving"
+ }, (save) ->
+ if save
+ submitAllForms('close')
+ else
+ Modal.hide('questionnaireWizzard')
+ else
+ Modal.hide('questionnaireWizzard')
+
+
+nextPage = ->
+ if _pageIndex.get() is _numPages.get()-1
+ Modal.hide('questionnaireWizzard')
+ else
+ _pageIndex.set _pageIndex.get()+1
+
+previousPage = ->
+ index = _pageIndex.get()
+ index -= 1 if index > 0
+ _pageIndex.set index
autoformHooks =
onSubmit: (insertDoc, updateDoc, currentDoc) ->
insertDoc.visitId = currentDoc.visitId
insertDoc.questionId = currentDoc.questionId
insertDoc._id = currentDoc._id if currentDoc._id?
#console.log "submit questionAutoform"
#console.log insertDoc
if insertDoc.value? and (!currentDoc.value? or (currentDoc.value? and currentDoc.value isnt insertDoc.value))
Meteor.call "upsertAnswer", insertDoc, (error) ->
throwError error if error?
formSubmitted()
@done()
false
Template.questionnaireWizzard.created = ->
@subscribe("questionsForQuestionnaire", @data.questionnaire._id)
+
if @data.readonly
_readonly.set true
else
_readonly.set false
+
+ #close on escape key press
+ $(document).on('keyup.wizzard', (e)->
+ e.stopPropagation()
+ if e.keyCode is 27
+ close()
+ return
+ )
+
+ #collect autoformIds, count pages
self = @
@autorun ->
count = 0
page = 0
questionIdsForPage = {}
didBreakPage = false
autoformIds = []
Questions.find
questionnaireId: self.data.questionnaire._id
,
sort: {index: 1}
.forEach (q) ->
if q.type isnt "description" and q._id isnt "table" and q._id isnt "table_polar"
autoformIds.push q._id
count += 1
if questionIdsForPage[page]?
questionIdsForPage[page].push q._id
else
questionIdsForPage[page] = [q._id]
didBreakPage = false
if q.break
page += 1
didBreakPage = true
page -= 1 if didBreakPage
_questionIdsForPage.set questionIdsForPage
_numQuestions.set count
_numPages.set page+1
_pageIndex.set 0
AutoForm.addHooks(autoformIds, autoformHooks)
+Template.questionnaireWizzard.destroyed = ->
+ $(document).unbind('keyup.wizzard')
+
Template.questionnaireWizzard.helpers
templateGestures:
'swipeleft div': (evt, templateInstance) ->
nextQuestion()
'swiperight div': (evt, templateInstance) ->
previousQuestion()
questionsForPage: ->
questionIdsForPage = _questionIdsForPage.get()[_pageIndex.get()]
Questions.find
questionnaireId: @questionnaire._id
_id: {$in: questionIdsForPage}
,
sort: {index: 1}
answerForQuestion: (visitId, questionId) ->
Answers.findOne
visitId: visitId
questionId: questionId
readonly: ->
_readonly.get()
formType: ->
if _readonly.get()
"disabled"
else
"normal"
answerFormSchema: ->
schema =
_id:
type: String
optional: true
visitId:
type: String
optional: true
questionId:
type: String
optional: true
value: @question.getSchemaDict()
new SimpleSchema(schema)
doc: ->
@answer or
visitId: @visit._id
questionId: @question._id
pages: ->
answers = {}
questionIds = Questions.find
questionnaireId: @questionnaire._id
.map (question) ->
question._id
Answers.find
visitId: @visit._id
questionId: {$in: questionIds}
.forEach (answer) ->
answers[answer.questionId] = answer
activeIndex = _pageIndex.get()
questionIdsForPage = _questionIdsForPage.get()
pages = []
for i in [0.._numPages.get()-1]
css = ""
allQuestionsAnsweredInPage = true
someQuestionsAnsweredInPage = false
Questions.find
questionnaireId: @questionnaire._id
_id: {$in: questionIdsForPage[i]}
.forEach (question) ->
return if question.type is "description"
answer = answers[question._id]
if question.type is "table" or question.type is "table_polar" or question.type is "multipleChoice"
if !answer? or answer.value.length < question.subquestions.length
allQuestionsAnsweredInPage = false
if answer? and answer.value.length > 0
someQuestionsAnsweredInPage = true
else if !answer?
allQuestionsAnsweredInPage = false
if allQuestionsAnsweredInPage
css = "answered"
else if someQuestionsAnsweredInPage
css = "answeredPartly"
if i is activeIndex
css += " active"
pages[i] =
index: i+1
css: css
pages
isOnFirstPage: ->
_pageIndex.get() is 0
isOnLastPageOfLastQuestionnaire: ->
validatedQuestionnaires = @visit.validatedQuestionnaires
_pageIndex.get() is _numPages.get()-1 and
@questionnaire._id is validatedQuestionnaires[validatedQuestionnaires.length-1]._id
Template.questionnaireWizzard.events
"click #next": (evt, tmpl) ->
if _readonly.get()
nextPage()
else
- submitAllForms(true)
+ submitAllForms('nextPage')
false
"click #back": (evt, tmpl) ->
if _readonly.get()
previousPage()
else
- submitAllForms(false)
+ submitAllForms('previousPage')
false
"click .jumpToPage": (evt) ->
- _pageIndex.set @index-1
+ pageIndex = @index-1
+ if isAFormDirty()
+ swal {
+ title: 'Unsaved Changes'
+ text: "Do you want to save the changes on this page?"
+ type: 'warning'
+ showCancelButton: true
+ confirmButtonText: 'Save'
+ cancelButtonText: "Don't save"
+ }, (save) ->
+ if save
+ submitAllForms(pageIndex: pageIndex)
+ else
+ _pageIndex.set pageIndex
+ else
+ _pageIndex.set pageIndex
false
-
+
+ "click #close": (evt) ->
+ close()
+ false
+
"submit .questionForm": (evt) ->
if _readonly.get()
return
evt.preventDefault()
evt.stopPropagation()
if @question.type is "description"
formSubmitted()
return
answer =
visitId: @visit._id
questionId: @question._id
value: []
_id: @answer._id if @answer?
for subquestion in @question.subquestions
inputs = $(evt.target).find("input[data-subquestion_code=#{subquestion.code}]:checked")
checkedChoices=[]
inputs.each -> #checked choices
input = $(@)
checkedChoices.push
value: input.data('choice_value').toString()
variable: input.data('choice_variable').toString()
if checkedChoices.length > 0
answer.value.push
code: subquestion.code
checkedChoices: checkedChoices
if answer.value.length > 0
Meteor.call "upsertAnswer", answer, (error) ->
throwError error if error?
formSubmitted()
else
formSubmitted()
false
diff --git a/app/client/views/questionnaires/questionnaire_wizzard.html b/app/client/views/questionnaires/questionnaire_wizzard.html
index 93d63f8..3328c24 100644
--- a/app/client/views/questionnaires/questionnaire_wizzard.html
+++ b/app/client/views/questionnaires/questionnaire_wizzard.html
@@ -1,75 +1,75 @@