Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xblock-poll
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
xblock-poll
Commits
866f7529
Commit
866f7529
authored
Jan 06, 2015
by
Jonathan Piacenti
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Studio view and images implemented for Surveys.
parent
21ab57d0
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
302 additions
and
172 deletions
+302
-172
poll/poll.py
+145
-92
poll/public/css/poll.css
+13
-2
poll/public/handlebars/poll_studio.handlebars
+1
-1
poll/public/handlebars/survey_results.handlebars
+6
-0
poll/public/html/poll_edit.html
+30
-8
poll/public/html/survey.html
+5
-0
poll/public/js/poll_edit.js
+102
-69
No files found.
poll/poll.py
View file @
866f7529
...
@@ -54,58 +54,68 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
...
@@ -54,58 +54,68 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
choice_data
,
choice_data
,
)
)
@XBlock.json_handler
@staticmethod
def
load_answers
(
self
,
data
,
suffix
=
''
):
def
any_image
(
field
):
return
{
'items'
:
[
{
'key'
:
key
,
'text'
:
value
[
'label'
],
'img'
:
value
[
'img'
],
'noun'
:
'answer'
,
'image'
:
True
,
}
for
key
,
value
in
self
.
answers
],
}
@XBlock.json_handler
def
get_results
(
self
,
data
,
suffix
=
''
):
self
.
publish_event_from_dict
(
self
.
event_namespace
+
'.view_results'
,
{})
detail
,
total
=
self
.
tally_detail
()
return
{
'question'
:
markdown
(
self
.
question
),
'tally'
:
detail
,
'total'
:
total
,
'feedback'
:
markdown
(
self
.
feedback
),
'plural'
:
total
>
1
,
'display_name'
:
self
.
display_name
,
}
@XBlock.json_handler
def
vote
(
self
,
data
,
suffix
=
''
):
"""
"""
Sets the user's vote
.
Find out if any answer has an image, since it affects layout
.
"""
"""
result
=
{
'success'
:
False
,
'errors'
:
[]}
return
any
(
value
[
'img'
]
for
value
in
dict
(
field
)
.
values
())
if
self
.
get_choice
()
is
not
None
:
result
[
'errors'
]
.
append
(
'You have already voted in this poll.'
)
return
result
try
:
choice
=
data
[
'choice'
]
except
KeyError
:
result
[
'errors'
]
.
append
(
'Answer not included with request.'
)
return
result
# Just to show data coming in...
try
:
OrderedDict
(
self
.
answers
)[
choice
]
except
KeyError
:
result
[
'errors'
]
.
append
(
'No key "{choice}" in answers table.'
.
format
(
choice
=
choice
))
return
result
self
.
clean_tally
()
@staticmethod
self
.
choice
=
choice
def
gather_items
(
data
,
result
,
noun
,
field
,
image
=
True
):
self
.
tally
[
choice
]
=
self
.
tally
.
get
(
choice
,
0
)
+
1
"""
Gathers a set of label-img pairs from a data dict and puts them in order.
"""
items
=
[]
if
field
not
in
data
or
not
isinstance
(
data
[
field
],
list
):
source_items
=
[]
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"'{0}' is not present, or not a JSON array."
.
format
(
field
))
else
:
source_items
=
data
[
field
]
result
[
'success'
]
=
True
# Make sure all components are present and clean them.
for
item
in
source_items
:
if
not
isinstance
(
item
,
dict
):
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"{0} {1} not a javascript object!"
.
format
(
noun
,
item
))
continue
key
=
item
.
get
(
'key'
,
''
)
.
strip
()
if
not
key
:
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"{0} {1} contains no key."
.
format
(
noun
,
item
))
image_link
=
item
.
get
(
'img'
,
''
)
.
strip
()
label
=
item
.
get
(
'label'
,
''
)
.
strip
()
if
not
label
:
if
image
and
not
image_link
:
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"{0} has no text or img. Please make sure all {0}s "
"have one or the other, or both."
.
format
(
noun
))
elif
not
image
:
result
[
'success'
]
=
False
# If there's a bug in the code or the user just forgot to relabel a question,
# votes could be accidentally lost if we assume the omission was an
# intended deletion.
result
[
'errors'
]
.
append
(
"{0} was added with no label. "
"All {1}s must have labels. Please check the form. "
"Check the form and explicitly delete {1}s "
"if not needed."
.
format
(
noun
,
noun
.
lower
()))
if
image
:
# Labels might have prefixed space for markdown, though it's unlikely.
items
.
append
((
key
,
{
'label'
:
label
,
'img'
:
image_link
.
strip
()}))
else
:
items
.
append
([
key
,
label
])
self
.
send_vote_event
({
'choice'
:
self
.
choice
})
if
not
len
(
items
)
>
1
:
result
[
'errors'
]
.
append
(
"You must include at least two {0}s."
.
format
(
noun
.
lower
()))
result
[
'success'
]
=
False
return
result
return
items
class
PollBlock
(
PollBase
):
class
PollBlock
(
PollBase
):
...
@@ -145,12 +155,6 @@ class PollBlock(PollBase):
...
@@ -145,12 +155,6 @@ class PollBlock(PollBase):
if
key
not
in
answers
:
if
key
not
in
answers
:
del
self
.
tally
[
key
]
del
self
.
tally
[
key
]
def
any_image
(
self
):
"""
Find out if any answer has an image, since it affects layout.
"""
return
any
(
value
[
'img'
]
for
value
in
dict
(
self
.
answers
)
.
values
())
def
tally_detail
(
self
):
def
tally_detail
(
self
):
"""
"""
Return a detailed dictionary from the stored tally that the
Return a detailed dictionary from the stored tally that the
...
@@ -162,7 +166,7 @@ class PollBlock(PollBase):
...
@@ -162,7 +166,7 @@ class PollBlock(PollBase):
total
=
0
total
=
0
self
.
clean_tally
()
self
.
clean_tally
()
source_tally
=
self
.
tally
source_tally
=
self
.
tally
any_img
=
self
.
any_image
()
any_img
=
self
.
any_image
(
self
.
answers
)
for
key
,
value
in
answers
.
items
():
for
key
,
value
in
answers
.
items
():
count
=
int
(
source_tally
[
key
])
count
=
int
(
source_tally
[
key
])
tally
.
append
({
tally
.
append
({
...
@@ -225,7 +229,7 @@ class PollBlock(PollBase):
...
@@ -225,7 +229,7 @@ class PollBlock(PollBase):
# Mustache is treating an empty string as true.
# Mustache is treating an empty string as true.
'feedback'
:
markdown
(
self
.
feedback
)
or
False
,
'feedback'
:
markdown
(
self
.
feedback
)
or
False
,
'js_template'
:
js_template
,
'js_template'
:
js_template
,
'any_img'
:
self
.
any_image
(),
'any_img'
:
self
.
any_image
(
self
.
answers
),
# The SDK doesn't set url_name.
# The SDK doesn't set url_name.
'url_name'
:
getattr
(
self
,
'url_name'
,
''
),
'url_name'
:
getattr
(
self
,
'url_name'
,
''
),
"display_name"
:
self
.
display_name
,
"display_name"
:
self
.
display_name
,
...
@@ -246,6 +250,7 @@ class PollBlock(PollBase):
...
@@ -246,6 +250,7 @@ class PollBlock(PollBase):
js_template
=
self
.
resource_string
(
'/public/handlebars/poll_studio.handlebars'
)
js_template
=
self
.
resource_string
(
'/public/handlebars/poll_studio.handlebars'
)
context
.
update
({
context
.
update
({
'question'
:
self
.
question
,
'question'
:
self
.
question
,
'display_name'
:
self
.
display_name
,
'feedback'
:
self
.
feedback
,
'feedback'
:
self
.
feedback
,
'js_template'
:
js_template
'js_template'
:
js_template
})
})
...
@@ -254,57 +259,79 @@ class PollBlock(PollBase):
...
@@ -254,57 +259,79 @@ class PollBlock(PollBase):
"/public/css/poll_edit.css"
,
"public/js/poll_edit.js"
,
"PollEdit"
)
"/public/css/poll_edit.css"
,
"public/js/poll_edit.js"
,
"PollEdit"
)
@XBlock.json_handler
@XBlock.json_handler
def
load_answers
(
self
,
data
,
suffix
=
''
):
return
{
'items'
:
[
{
'key'
:
key
,
'text'
:
value
[
'label'
],
'img'
:
value
[
'img'
],
'noun'
:
'answer'
,
'image'
:
True
,
}
for
key
,
value
in
self
.
answers
],
}
@XBlock.json_handler
def
get_results
(
self
,
data
,
suffix
=
''
):
self
.
publish_event_from_dict
(
self
.
event_namespace
+
'.view_results'
,
{})
detail
,
total
=
self
.
tally_detail
()
return
{
'question'
:
markdown
(
self
.
question
),
'tally'
:
detail
,
'total'
:
total
,
'feedback'
:
markdown
(
self
.
feedback
),
'plural'
:
total
>
1
,
'display_name'
:
self
.
display_name
,
}
@XBlock.json_handler
def
vote
(
self
,
data
,
suffix
=
''
):
"""
Sets the user's vote.
"""
result
=
{
'success'
:
False
,
'errors'
:
[]}
if
self
.
get_choice
()
is
not
None
:
result
[
'errors'
]
.
append
(
'You have already voted in this poll.'
)
return
result
try
:
choice
=
data
[
'choice'
]
except
KeyError
:
result
[
'errors'
]
.
append
(
'Answer not included with request.'
)
return
result
# Just to show data coming in...
try
:
OrderedDict
(
self
.
answers
)[
choice
]
except
KeyError
:
result
[
'errors'
]
.
append
(
'No key "{choice}" in answers table.'
.
format
(
choice
=
choice
))
return
result
self
.
clean_tally
()
self
.
choice
=
choice
self
.
tally
[
choice
]
=
self
.
tally
.
get
(
choice
,
0
)
+
1
result
[
'success'
]
=
True
self
.
send_vote_event
({
'choice'
:
self
.
choice
})
return
result
@XBlock.json_handler
def
studio_submit
(
self
,
data
,
suffix
=
''
):
def
studio_submit
(
self
,
data
,
suffix
=
''
):
# I wonder if there's something for live validation feedback already.
# I wonder if there's something for live validation feedback already.
result
=
{
'success'
:
True
,
'errors'
:
[]}
result
=
{
'success'
:
True
,
'errors'
:
[]}
question
=
data
.
get
(
'question'
,
''
)
.
strip
()
question
=
data
.
get
(
'question'
,
''
)
.
strip
()
feedback
=
data
.
get
(
'feedback'
,
''
)
.
strip
()
feedback
=
data
.
get
(
'feedback'
,
''
)
.
strip
()
display_name
=
data
.
get
(
'display_name'
,
''
)
.
strip
()
if
not
question
:
if
not
question
:
result
[
'errors'
]
.
append
(
"You must specify a question."
)
result
[
'errors'
]
.
append
(
"You must specify a question."
)
result
[
'success'
]
=
False
result
[
'success'
]
=
False
answers
=
[]
if
'answers'
not
in
data
or
not
isinstance
(
data
[
'answers'
],
list
):
source_answers
=
[]
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"'answers' is not present, or not a JSON array."
)
else
:
source_answers
=
data
[
'answers'
]
# Make sure all components are present and clean them.
for
answer
in
source_answers
:
if
not
isinstance
(
answer
,
dict
):
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"Answer {0} not a javascript object!"
.
format
(
answer
))
continue
key
=
answer
.
get
(
'key'
,
''
)
.
strip
()
if
not
key
:
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"Answer {0} contains no key."
.
format
(
answer
))
img
=
answer
.
get
(
'img'
,
''
)
.
strip
()
label
=
answer
.
get
(
'label'
,
''
)
.
strip
()
if
not
(
img
or
label
):
result
[
'success'
]
=
False
result
[
'errors'
]
.
append
(
"Answer {0} has no text or img. One is needed."
.
format
(
answer
))
answers
.
append
((
key
,
{
'label'
:
label
,
'img'
:
img
}))
if
not
len
(
answers
)
>
1
:
result
[
'errors'
]
.
append
(
"You must include at least two answers."
)
result
[
'success'
]
=
False
if
not
result
[
'success'
]:
if
not
result
[
'success'
]:
return
result
return
result
answers
=
self
.
gather_items
(
data
,
result
,
'Answer'
,
'answers'
)
self
.
answers
=
answers
self
.
answers
=
answers
self
.
question
=
question
self
.
question
=
question
self
.
feedback
=
feedback
self
.
feedback
=
feedback
self
.
display_name
=
display_name
# Tally will not be updated until the next attempt to use it, per
# Tally will not be updated until the next attempt to use it, per
# scoping limitations.
# scoping limitations.
...
@@ -379,6 +406,7 @@ class SurveyBlock(PollBase):
...
@@ -379,6 +406,7 @@ class SurveyBlock(PollBase):
'answers'
:
self
.
answers
,
'answers'
:
self
.
answers
,
'js_template'
:
js_template
,
'js_template'
:
js_template
,
'questions'
:
self
.
questions
,
'questions'
:
self
.
questions
,
'any_img'
:
self
.
any_image
(
self
.
questions
),
# Mustache is treating an empty string as true.
# Mustache is treating an empty string as true.
'feedback'
:
markdown
(
self
.
feedback
)
or
False
,
'feedback'
:
markdown
(
self
.
feedback
)
or
False
,
# The SDK doesn't set url_name.
# The SDK doesn't set url_name.
...
@@ -396,13 +424,14 @@ class SurveyBlock(PollBase):
...
@@ -396,13 +424,14 @@ class SurveyBlock(PollBase):
js_template
=
self
.
resource_string
(
'/public/handlebars/poll_studio.handlebars'
)
js_template
=
self
.
resource_string
(
'/public/handlebars/poll_studio.handlebars'
)
context
.
update
({
context
.
update
({
'question'
:
self
.
question
,
'feedback'
:
self
.
feedback
,
'feedback'
:
self
.
feedback
,
'js_template'
:
js_template
'display_name'
:
self
.
display_name
,
'js_template'
:
js_template
,
'multiquestion'
:
True
,
})
})
return
self
.
create_fragment
(
return
self
.
create_fragment
(
context
,
"public/html/poll_edit.html"
,
context
,
"public/html/poll_edit.html"
,
"/public/css/poll_edit.css"
,
"public/js/poll_edit.js"
,
"SurveyEdit
Block
"
)
"/public/css/poll_edit.css"
,
"public/js/poll_edit.js"
,
"SurveyEdit"
)
def
tally_detail
(
self
):
def
tally_detail
(
self
):
"""
"""
...
@@ -569,6 +598,30 @@ class SurveyBlock(PollBase):
...
@@ -569,6 +598,30 @@ class SurveyBlock(PollBase):
return
result
return
result
@XBlock.json_handler
def
studio_submit
(
self
,
data
,
suffix
=
''
):
# I wonder if there's something for live validation feedback already.
result
=
{
'success'
:
True
,
'errors'
:
[]}
feedback
=
data
.
get
(
'feedback'
,
''
)
.
strip
()
display_name
=
data
.
get
(
'display_name'
,
''
)
.
strip
()
answers
=
self
.
gather_items
(
data
,
result
,
'Answer'
,
'answers'
,
image
=
False
)
questions
=
self
.
gather_items
(
data
,
result
,
'Question'
,
'questions'
)
if
not
result
[
'success'
]:
return
result
self
.
answers
=
answers
self
.
questions
=
questions
self
.
feedback
=
feedback
self
.
display_name
=
display_name
# Tally will not be updated until the next attempt to use it, per
# scoping limitations.
return
result
@staticmethod
@staticmethod
def
workbench_scenarios
():
def
workbench_scenarios
():
"""
"""
...
...
poll/public/css/poll.css
View file @
866f7529
...
@@ -78,10 +78,18 @@ li.poll-result .poll-image {
...
@@ -78,10 +78,18 @@ li.poll-result .poll-image {
margin-left
:
0
;
margin-left
:
0
;
}
}
.poll-image
img
{
.poll-image-td
{
width
:
25%
;
}
.poll-image
img
,
.poll-image-td
img
{
width
:
100%
;
width
:
100%
;
}
}
.poll-image-td
{
display
:
inline-block
;
}
.poll-percent-container
{
.poll-percent-container
{
display
:
table-cell
;
display
:
table-cell
;
text-align
:
left
;
text-align
:
left
;
...
@@ -154,7 +162,9 @@ li.poll-result .poll-image {
...
@@ -154,7 +162,9 @@ li.poll-result .poll-image {
th
.survey-answer
{
th
.survey-answer
{
text-align
:
center
;
text-align
:
center
;
width
:
2em
;
width
:
7%
;
line-height
:
1em
;
padding-bottom
:
.25em
;
}
}
.poll-header
{
.poll-header
{
...
@@ -168,6 +178,7 @@ th.survey-answer {
...
@@ -168,6 +178,7 @@ th.survey-answer {
.survey-question
{
.survey-question
{
font-weight
:
bold
;
font-weight
:
bold
;
vertical-align
:
middle
;
}
}
.survey-choice
{
.survey-choice
{
...
...
poll/public/handlebars/poll_studio.handlebars
View file @
866f7529
<script
id=
"
answer
-form-component"
type=
"text/html"
>
<script
id=
"
poll
-form-component"
type=
"text/html"
>
{{#
each
items
}}
{{#
each
items
}}
<
li
class
=
"field comp-setting-entry is-set poll-
{{
noun
}}
-studio-item"
>
<
li
class
=
"field comp-setting-entry is-set poll-
{{
noun
}}
-studio-item"
>
<
div
class
=
"wrapper-comp-setting"
>
<
div
class
=
"wrapper-comp-setting"
>
...
...
poll/public/handlebars/survey_results.handlebars
View file @
866f7529
...
@@ -11,6 +11,12 @@
...
@@ -11,6 +11,12 @@
<
/thead
>
<
/thead
>
{{#
each
tally
}}
{{#
each
tally
}}
<
tr
class
=
"survey-row"
>
<
tr
class
=
"survey-row"
>
{{#if
img
}}
<
div
class
=
"poll-image-td"
>
<
img
src
=
"
{{
img
}}
"
/>
<
/div
>
{{/if}}
{
%
endif
%
}
<
td
class
=
"survey-question"
>
{{{
text
}}}
<
/td
>
<
td
class
=
"survey-question"
>
{{{
text
}}}
<
/td
>
{{#
each
answers
}}
{{#
each
answers
}}
<
td
class
=
"survey-percentage survey-option
{{#if
choice
}}
survey-choice
{{/if}}{{#if
top
}}
poll-top-choice
{{/if}}
"
>
{{
percent
}}
%<
/td
>
<
td
class
=
"survey-percentage survey-option
{{#if
choice
}}
survey-choice
{{/if}}{{#if
top
}}
poll-top-choice
{{/if}}
"
>
{{
percent
}}
%<
/td
>
...
...
poll/public/html/poll_edit.html
View file @
866f7529
...
@@ -3,13 +3,21 @@
...
@@ -3,13 +3,21 @@
<form
id=
"poll-form"
>
<form
id=
"poll-form"
>
<ul
class=
"list-input settings-list"
id=
"poll-line-items"
>
<ul
class=
"list-input settings-list"
id=
"poll-line-items"
>
<li
class=
"field comp-setting-entry is-set"
>
<li
class=
"field comp-setting-entry is-set"
>
<h2><label
for=
"poll-question-editor"
>
Question/Prompt
</label></h2>
<div
class=
"wrapper-comp-setting"
>
<a
href=
"//daringfireball.net/projects/markdown/syntax"
target=
"_blank"
>
Markdown Syntax
</a>
is supported.
<label
class=
"label setting-label poll-setting-label"
for=
"display_name"
>
Display Name
</label>
<div
id=
"poll-question-editor-container"
>
<input
class=
"input setting-input"
name=
"display_name"
id=
"poll-display-name"
value=
"{{ display_name }}"
type=
"text"
/>
<textarea
class=
"input setting-input"
name=
"question"
id=
"poll-question-editor"
>
{{question}}
</textarea>
</div>
</div>
<span
class=
"tip setting-help"
>
Enter the prompt for the user.
</span>
</li>
</li>
{% if not multiquestion %}
<li
class=
"field comp-setting-entry is-set"
>
<h2><label
for=
"poll-question-editor"
>
Question/Prompt
</label></h2>
<a
href=
"//daringfireball.net/projects/markdown/syntax"
target=
"_blank"
>
Markdown Syntax
</a>
is supported.
<div
id=
"poll-question-editor-container"
>
<textarea
class=
"input setting-input"
name=
"question"
id=
"poll-question-editor"
>
{{question}}
</textarea>
</div>
<span
class=
"tip setting-help"
>
Enter the prompt for the user.
</span>
</li>
{% endif %}
<li
class=
"field comp-setting-entry is-set"
>
<li
class=
"field comp-setting-entry is-set"
>
<h2><label
for=
"poll-feedback-editor"
>
Feedback
</label></h2>
<h2><label
for=
"poll-feedback-editor"
>
Feedback
</label></h2>
<a
href=
"//daringfireball.net/projects/markdown/syntax"
target=
"_blank"
>
Markdown Syntax
</a>
is supported.
<a
href=
"//daringfireball.net/projects/markdown/syntax"
target=
"_blank"
>
Markdown Syntax
</a>
is supported.
...
@@ -29,15 +37,29 @@
...
@@ -29,15 +37,29 @@
If you delete an answer, any votes for that answer will also be deleted. Students whose choices are deleted
If you delete an answer, any votes for that answer will also be deleted. Students whose choices are deleted
may vote again, but will not lose course progress.
may vote again, but will not lose course progress.
</p>
</p>
{% if multiquestion %}
<p>
Questions must be similarly cared for. If a question's text is changed, any votes for that question will remain.
If a question is deleted, any student who previously took the survey will be permitted to retake it, but will not
lose course progress.
</p>
{% endif %}
</li>
</li>
<li
id=
"poll-answer-marker"
></li>
<li
id=
"poll-answer-marker"
></li>
<li
id=
"poll-answer-end-marker"
>
<li
id=
"poll-answer-end-marker"
></li>
<li
id=
"poll-question-marker"
></li>
<li
id=
"poll-question-end-marker"
></li>
</ul>
</ul>
<div
class=
"xblock-actions"
>
<div
class=
"xblock-actions"
>
<ul>
<ul>
<li
class=
"action-item"
id=
"poll-add-answer"
>
<li
class=
"action-item"
id=
"poll-add-answer"
>
<a
href=
"#"
class=
"button action-button"
class=
"poll-add-
answer
-link"
onclick=
"return false;"
>
Add Answer
</a>
<a
href=
"#"
class=
"button action-button"
class=
"poll-add-
item
-link"
onclick=
"return false;"
>
Add Answer
</a>
</li>
</li>
{% if multiquestion %}
<li
class=
"action-item"
id=
"poll-add-question"
>
<a
href=
"#"
class=
"button action-button"
class=
"poll-add-item-link"
onclick=
"return false;"
>
Add Question
</a>
</li>
{% endif %}
<li
class=
"action-item"
>
<li
class=
"action-item"
>
<input
id=
"poll-submit-options"
type=
"submit"
class=
"button action-primary save-button"
value=
"Save"
onclick=
"return false;"
/>
<input
id=
"poll-submit-options"
type=
"submit"
class=
"button action-primary save-button"
value=
"Save"
onclick=
"return false;"
/>
</li>
</li>
...
...
poll/public/html/survey.html
View file @
866f7529
...
@@ -16,6 +16,11 @@
...
@@ -16,6 +16,11 @@
{% for key, question in questions %}
{% for key, question in questions %}
<tr
class=
"survey-row"
>
<tr
class=
"survey-row"
>
<td
class=
"survey-question"
>
<td
class=
"survey-question"
>
{% if question.img %}
<div
class=
"poll-image-td"
>
<img
src=
"{{question.img}}"
/>
</div>
{% endif %}
{{question.label}}
{{question.label}}
</td>
</td>
{% for answer, answer_details in answers %}
{% for answer, answer_details in answers %}
...
...
poll/public/js/poll_edit.js
View file @
866f7529
...
@@ -2,49 +2,60 @@
...
@@ -2,49 +2,60 @@
function
PollEditUtil
(
runtime
,
element
,
pollType
)
{
function
PollEditUtil
(
runtime
,
element
,
pollType
)
{
var
self
=
this
;
var
self
=
this
;
// These URLs aren't validated in real time, so even if they don't exist for a type of block
// we can create a reference to them.
this
.
loadAnswers
=
runtime
.
handlerUrl
(
element
,
'load_answers'
);
this
.
loadQuestions
=
runtime
.
handlerUrl
(
element
,
'load_questions'
);
this
.
init
=
function
()
{
this
.
init
=
function
()
{
// Set up the editing form for a Poll or Survey.
// Set up the editing form for a Poll or Survey.
self
.
loadAnswers
=
runtime
.
handlerUrl
(
element
,
'load_answers'
);
var
temp
=
$
(
'#poll-form-component'
,
element
).
html
();
var
temp
=
$
(
'#answer-form-component'
,
element
).
html
();
self
.
answerTemplate
=
Handlebars
.
compile
(
temp
);
self
.
answerTemplate
=
Handlebars
.
compile
(
temp
);
$
(
element
).
find
(
'.cancel-button'
,
element
).
bind
(
'click'
,
function
()
{
$
(
element
).
find
(
'.cancel-button'
,
element
).
bind
(
'click'
,
function
()
{
runtime
.
notify
(
'cancel'
,
{});
runtime
.
notify
(
'cancel'
,
{});
});
});
var
mapping
=
self
.
mappings
[
pollType
][
'buttons'
];
var
button_
mapping
=
self
.
mappings
[
pollType
][
'buttons'
];
for
(
var
key
in
mapping
)
{
for
(
var
key
in
button_
mapping
)
{
if
(
mapping
.
hasOwnProperty
(
key
))
{
if
(
button_
mapping
.
hasOwnProperty
(
key
))
{
$
(
key
,
element
).
click
(
$
(
key
,
element
).
click
(
// The nature of the closure forces us to make a custom function here.
// The nature of the closure forces us to make a custom function here.
function
(
context_key
,
topMarker
,
bottomMarker
)
{
function
(
context_key
)
{
return
function
()
{
return
function
()
{
// The degree of precision on date should be precise enough to avoid
// The degree of precision on date should be precise enough to avoid
// collisions in the real world.
// collisions in the real world.
var
bottom
=
$
(
b
ottomMarker
);
var
bottom
=
$
(
b
utton_mapping
[
context_key
][
'bottomMarker'
]
);
$
(
self
.
answerTemplate
(
mapping
[
context_key
][
'itemList'
])).
before
(
bottom
);
var
new_item
=
$
(
self
.
answerTemplate
(
button_mapping
[
context_key
][
'itemList'
])
);
var
new_item
=
bottom
.
prev
(
);
bottom
.
before
(
new_item
);
self
.
empowerDeletes
(
new_item
);
self
.
empowerDeletes
(
new_item
);
self
.
empowerArrows
(
self
.
empowerArrows
(
new_item
,
mapping
[
context_key
][
'topMarker'
],
new_item
,
button_
mapping
[
context_key
][
'topMarker'
],
mapping
[
context_key
][
'bottomMarker'
]
button_
mapping
[
context_key
][
'bottomMarker'
]
);
);
new_item
.
fadeOut
(
250
).
fadeIn
(
250
);
new_item
.
fadeOut
(
250
).
fadeIn
(
250
);
}
}
}(
key
,
self
.
mappings
[
pollType
]
)
}(
key
)
)
)
}
}
}
}
$
(
element
).
find
(
'.save-button'
,
element
).
bind
(
'click'
,
self
.
pollSubmitHandler
);
$
(
element
).
find
(
'.save-button'
,
element
).
bind
(
'click'
,
self
.
pollSubmitHandler
);
$
(
function
(
$
)
{
var
mapping
=
self
.
mappings
[
pollType
][
'onLoad'
];
$
.
ajax
({
for
(
var
task
in
mapping
)
{
type
:
"POST"
,
function
load
(
taskItem
){
url
:
self
.
loadAnswers
,
$
(
function
(
$
)
{
data
:
JSON
.
stringify
({}),
$
.
ajax
({
success
:
self
.
displayAnswers
type
:
"POST"
,
});
url
:
taskItem
[
'url'
],
});
data
:
JSON
.
stringify
({}),
success
:
taskItem
[
'function'
]
});
});
}
load
(
mapping
[
task
]);
}
};
};
this
.
extend
=
function
(
obj1
,
obj2
)
{
this
.
extend
=
function
(
obj1
,
obj2
)
{
...
@@ -65,32 +76,6 @@ function PollEditUtil(runtime, element, pollType) {
...
@@ -65,32 +76,6 @@ function PollEditUtil(runtime, element, pollType) {
return
self
.
extend
({
'key'
:
new
Date
().
getTime
(),
'text'
:
''
,
'img'
:
''
},
extra
)
return
self
.
extend
({
'key'
:
new
Date
().
getTime
(),
'text'
:
''
,
'img'
:
''
},
extra
)
};
};
// This object is used to swap out values which differ between Survey and Poll blocks.
this
.
mappings
=
{
'poll'
:
{
'buttons'
:
{
'#poll-add-answer'
:
{
'itemList'
:
{
'items'
:
[
self
.
makeNew
({
'image'
:
true
,
'noun'
:
'answer'
})]},
'topMarker'
:
'#poll-answer-marker'
,
'bottomMarker'
:
'#poll-answer-end-marker'
}
},
'onLoad'
:
{
}
},
'survey'
:
{
'buttons'
:
{
'#poll-add-answer'
:
{
'itemList'
:
{
'items'
:
[
self
.
makeNew
({
'image'
:
false
,
'noun'
:
'answer'
})]},
'topMarker'
:
'#poll-answer-marker'
,
'bottomMarker'
:
'#poll-answer-end-marker'
},
'#poll-add-question'
:
{
'itemList'
:
{
'items'
:
[
self
.
makeNew
({
'image'
:
true
,
'noun'
:
'question'
})]}
}
}
}
};
this
.
empowerDeletes
=
function
(
scope
)
{
this
.
empowerDeletes
=
function
(
scope
)
{
// Activates the delete buttons on rendered line items.
// Activates the delete buttons on rendered line items.
$
(
'.poll-delete-answer'
,
scope
).
click
(
function
()
{
$
(
'.poll-delete-answer'
,
scope
).
click
(
function
()
{
...
@@ -122,9 +107,44 @@ function PollEditUtil(runtime, element, pollType) {
...
@@ -122,9 +107,44 @@ function PollEditUtil(runtime, element, pollType) {
self
.
displayItems
(
data
,
'#poll-answer-marker'
,
'#poll-answer-end-marker'
)
self
.
displayItems
(
data
,
'#poll-answer-marker'
,
'#poll-answer-end-marker'
)
};
};
this
.
displayQuestions
=
function
(
data
)
{
self
.
displayItems
(
data
,
"#poll-question-marker"
,
'#poll-question-end-marker'
)
};
// This object is used to swap out values which differ between Survey and Poll blocks.
this
.
mappings
=
{
'poll'
:
{
'buttons'
:
{
'#poll-add-answer'
:
{
'itemList'
:
{
'items'
:
[
self
.
makeNew
({
'image'
:
true
,
'noun'
:
'answer'
})]},
'topMarker'
:
'#poll-answer-marker'
,
'bottomMarker'
:
'#poll-answer-end-marker'
}
},
'onLoad'
:
[{
'url'
:
self
.
loadAnswers
,
'function'
:
self
.
displayAnswers
}],
'gather'
:
[{
'prefix'
:
'answer'
,
'field'
:
'answers'
}]
},
'survey'
:
{
'buttons'
:
{
'#poll-add-answer'
:
{
'itemList'
:
{
'items'
:
[
self
.
makeNew
({
'image'
:
false
,
'noun'
:
'answer'
})]},
'topMarker'
:
'#poll-answer-marker'
,
'bottomMarker'
:
'#poll-answer-end-marker'
},
'#poll-add-question'
:
{
'itemList'
:
{
'items'
:
[
self
.
makeNew
({
'image'
:
true
,
'noun'
:
'question'
})]},
'topMarker'
:
'#poll-question-marker'
,
'bottomMarker'
:
'#poll-question-end-marker'
}
},
'onLoad'
:
[
{
'url'
:
self
.
loadQuestions
,
'function'
:
self
.
displayQuestions
},
{
'url'
:
self
.
loadAnswers
,
'function'
:
self
.
displayAnswers
}
],
'gather'
:
[{
'prefix'
:
'answer'
,
'field'
:
'answers'
},
{
'prefix'
:
'question'
,
'field'
:
'questions'
}]
}
};
this
.
displayItems
=
function
(
data
,
topMarker
,
bottomMarker
)
{
this
.
displayItems
=
function
(
data
,
topMarker
,
bottomMarker
)
{
// Loads the initial set of items that the block needs to edit.
// Loads the initial set of items that the block needs to edit.
$
(
'#poll-answer-end-marker'
).
before
(
self
.
answerTemplate
(
data
));
$
(
bottomMarker
).
before
(
self
.
answerTemplate
(
data
));
self
.
empowerDeletes
(
element
,
topMarker
,
bottomMarker
);
self
.
empowerDeletes
(
element
,
topMarker
,
bottomMarker
);
self
.
empowerArrows
(
element
,
topMarker
,
bottomMarker
);
self
.
empowerArrows
(
element
,
topMarker
,
bottomMarker
);
};
};
...
@@ -142,31 +162,44 @@ function PollEditUtil(runtime, element, pollType) {
...
@@ -142,31 +162,44 @@ function PollEditUtil(runtime, element, pollType) {
alert
(
data
[
'errors'
].
join
(
'
\
n'
));
alert
(
data
[
'errors'
].
join
(
'
\
n'
));
};
};
this
.
pollSubmitHandler
=
function
()
{
this
.
gather
=
function
(
scope
,
tracker
,
data
,
prefix
,
field
)
{
var
key
=
'label'
;
var
name
=
scope
.
name
.
replace
(
prefix
+
'-'
,
''
);
if
(
scope
.
name
.
indexOf
(
'img-'
)
==
0
){
name
=
name
.
replace
(
'img-'
,
''
);
key
=
'img'
}
if
(
!
(
scope
.
name
.
indexOf
(
prefix
+
'-'
)
>=
0
))
{
return
}
if
(
tracker
.
indexOf
(
name
)
==
-
1
){
tracker
.
push
(
name
);
data
[
field
].
push
({
'key'
:
name
})
}
var
index
=
tracker
.
indexOf
(
name
);
console
.
log
(
data
[
field
]);
console
.
log
(
index
);
data
[
field
][
index
][
key
]
=
scope
.
value
;
return
true
};
this
.
pollSubmitHandler
=
function
()
{
// Take all of the fields, serialize them, and pass them to the
// Take all of the fields, serialize them, and pass them to the
// server for saving.
// server for saving.
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'studio_submit'
);
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'studio_submit'
);
var
data
=
{
'answers'
:
[]};
var
data
=
{};
var
tracker
=
[];
var
tracker
;
$
(
'#poll-form input'
,
element
).
each
(
function
()
{
var
gatherings
=
self
.
mappings
[
pollType
][
'gather'
];
var
key
=
'label'
;
for
(
var
gathering
in
gatherings
)
{
if
(
this
.
name
.
indexOf
(
'answer-'
)
>=
0
){
tracker
=
[];
var
name
=
this
.
name
.
replace
(
'answer-'
,
''
);
var
field
=
gatherings
[
gathering
][
'field'
];
if
(
this
.
name
.
indexOf
(
'img-'
)
==
0
){
var
prefix
=
gatherings
[
gathering
][
'prefix'
];
name
=
name
.
replace
(
'img-'
,
''
);
data
[
field
]
=
[];
key
=
'img'
$
(
'#poll-form input'
,
element
).
each
(
function
()
{
}
self
.
gather
(
this
,
tracker
,
data
,
prefix
,
field
)
if
(
tracker
.
indexOf
(
name
)
==
-
1
){
});
tracker
.
push
(
name
);
}
data
[
'answers'
].
push
({
'key'
:
name
})
data
[
'display_name'
]
=
$
(
'#poll-display-name'
,
element
).
val
();
}
var
index
=
tracker
.
indexOf
(
name
);
data
[
'answers'
][
index
][
key
]
=
this
.
value
;
return
}
data
[
this
.
name
]
=
this
.
value
});
data
[
'title'
]
=
$
(
'#poll-title'
,
element
).
val
();
data
[
'question'
]
=
$
(
'#poll-question-editor'
,
element
).
val
();
data
[
'question'
]
=
$
(
'#poll-question-editor'
,
element
).
val
();
data
[
'feedback'
]
=
$
(
'#poll-feedback-editor'
,
element
).
val
();
data
[
'feedback'
]
=
$
(
'#poll-feedback-editor'
,
element
).
val
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment