Replaces the existing range drop-down widget with a html5 range input.
* For older browsers there's a javascript fallback which ensures that the values entered into the text input are fall within min-max and are divisable by step. * The new widget adds two new checks - step and unit. * The unit is displayed next to the input element.EmailTemplateFixes
parent
9945ecae79
commit
1d2f5def85
|
@ -1,41 +1,83 @@
|
|||
from questionnaire import *
|
||||
from django.conf.urls.static import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.simplejson import dumps
|
||||
|
||||
@question_proc('range')
|
||||
def question_range(request, question):
|
||||
cd = question.getcheckdict()
|
||||
Range = cd.get('range', '1-5')
|
||||
try:
|
||||
rmin, rmax = Range.split('-', 1)
|
||||
rmin, rmax = int(rmin), int(rmax)
|
||||
except ValueError:
|
||||
rmin = 0
|
||||
rmax = int(range)
|
||||
selected = int(request.POST.get('question_%s' % question.number, rmin))
|
||||
Range = range(rmin, rmax+1)
|
||||
|
||||
rmin, rmax = parse_range(cd)
|
||||
rstep = parse_step(cd)
|
||||
runit = cd.get('unit', '')
|
||||
|
||||
current = request.POST.get('question_%s' % question.number, rmin)
|
||||
|
||||
return {
|
||||
'required' : True,
|
||||
'range' : Range,
|
||||
'selected' : selected,
|
||||
'rmin' : rmin,
|
||||
'rmax' : rmax,
|
||||
'rstep' : rstep,
|
||||
'runit' : runit,
|
||||
'current' : current,
|
||||
'jsinclude' : [settings.STATIC_URL+'range.js']
|
||||
}
|
||||
|
||||
@answer_proc('range')
|
||||
def process_range(question, answer):
|
||||
checkdict = question.getcheckdict()
|
||||
cd = question.getcheckdict()
|
||||
|
||||
rmin, rmax = parse_range(cd)
|
||||
rstep = parse_step(cd)
|
||||
|
||||
convert = range_type(rmin, rmax, rstep)
|
||||
|
||||
try:
|
||||
rmin,rmax = checkdict.get('range','1-10').split('-',1)
|
||||
rmin, rmax = int(rmin), int(rmax)
|
||||
ans = convert(answer['ANSWER'])
|
||||
except:
|
||||
raise AnswerException("Error in question. Additional checks field should contain range='min-max'")
|
||||
try:
|
||||
ans = int(answer['ANSWER'])
|
||||
except:
|
||||
raise AnswerException("Could not convert `%r` to integer.")
|
||||
if ans > rmax or ans < rmin:
|
||||
raise AnswerException("Could not convert `%r`")
|
||||
|
||||
if ans > convert(rmax) or ans < convert(rmin):
|
||||
raise AnswerException(_(u"Out of range"))
|
||||
|
||||
return dumps([ans])
|
||||
|
||||
add_type('range', 'Range of numbers [select]')
|
||||
|
||||
def parse_range(checkdict):
|
||||
"Given a checkdict for a range widget return the min and max string values."
|
||||
|
||||
Range = checkdict.get('range', '1-5')
|
||||
|
||||
try:
|
||||
rmin, rmax = Range.split('-', 1)
|
||||
except ValueError:
|
||||
rmin, rmax = '1', '5'
|
||||
|
||||
return rmin, rmax
|
||||
|
||||
def parse_step(checkdict):
|
||||
"Given a checkdict for a range widget return the step as string value."
|
||||
|
||||
return checkdict.get('step', '1')
|
||||
|
||||
def range_type(rmin, rmax, step):
|
||||
"""Given the min, max and step value return float or int depending on
|
||||
the number of digits after 0.
|
||||
|
||||
"""
|
||||
|
||||
if any((digits(rmin), digits(rmax), digits(step))):
|
||||
return float
|
||||
else:
|
||||
return int
|
||||
|
||||
def digits(number):
|
||||
"Given a number as string return the number of digits after 0."
|
||||
if '.' in number or ',' in number:
|
||||
if '.' in number:
|
||||
return len(number.split('.')[1])
|
||||
else:
|
||||
return len(number.split(',')[1])
|
||||
else:
|
||||
return 0
|
|
@ -80,4 +80,20 @@ html, body {
|
|||
background-color: #f5f5f5;
|
||||
padding: 20px 20px 10px;
|
||||
margin: -20px -20px 20px;
|
||||
}
|
||||
|
||||
.rangeinput div.input {
|
||||
display: inline;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.rangeinput label {
|
||||
text-align: left;
|
||||
max-width: 40px;
|
||||
margin-left: 10px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.rangeinput {
|
||||
margin-top: 5px;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
(function($){$(document).ready(function() {
|
||||
|
||||
// true if the native html5 range input type is supported by the browser
|
||||
var range_support = (function(){
|
||||
var el=document.createElement("input");
|
||||
el.setAttribute("type", "range");
|
||||
return el.type=="range";
|
||||
})();
|
||||
|
||||
// if range is not supported the input will be a simple text field
|
||||
// in which case the following function ensures that the values
|
||||
// in this text field are within the constraints of min, max, step
|
||||
var normalize_value = function(input) {
|
||||
var input = $(input);
|
||||
|
||||
var min = parseFloat(input.attr('min'));
|
||||
var max = parseFloat(input.attr('max'));
|
||||
var step = parseFloat(input.attr('step'));
|
||||
var val = parseFloat(input.attr('value'));
|
||||
|
||||
if (val > max) val = max;
|
||||
if (val < min) val = min;
|
||||
|
||||
// returns the number of digits after the decimal point
|
||||
var digits = function(value){
|
||||
var str = value.toString();
|
||||
|
||||
if (str.indexOf('.') == -1 && str.indexOf(',') == -1)
|
||||
return 0;
|
||||
|
||||
if (str.indexOf('.') > -1)
|
||||
return str.split('.')[1].length;
|
||||
else
|
||||
return str.split(',')[1].length;
|
||||
};
|
||||
|
||||
// rounds the number to the next step
|
||||
var round = function(val, step) {
|
||||
return Math.round(val * (1 / step)) / (1 / step);
|
||||
};
|
||||
|
||||
// round the number and fix the digits
|
||||
return round(val, step).toFixed(digits(val));
|
||||
};
|
||||
|
||||
if (range_support) {
|
||||
$('.rangeinput input').change(function() {
|
||||
var label = $(this).parent().parent().find('label');
|
||||
var unit = label.attr('data-unit');
|
||||
label.html($(this).val() + unit);
|
||||
});
|
||||
$('.rangeinput input').trigger('change');
|
||||
} else {
|
||||
$('.rangeinput input').change(function() {
|
||||
$(this).val(normalize_value(this));
|
||||
});
|
||||
}
|
||||
|
||||
});})(jQuery);
|
|
@ -2,8 +2,8 @@
|
|||
{% load markup questionnaire i18n %}
|
||||
|
||||
{% block headextra %}
|
||||
<script type="text/javascript" src="/static/questionset.js"></script>
|
||||
<link rel="stylesheet" href="/static/progressbar.css"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}questionset.js"></script>
|
||||
<link rel="stylesheet" href="{{ STATIC_URL }}progressbar.css"></script>
|
||||
|
||||
{% for x in jsinclude %}
|
||||
<script type="text/javascript" src="{{ x }}"></script>
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<div class="clearfix">
|
||||
<div class="clearfix rangeinput">
|
||||
<div class="input">
|
||||
<select onchange="valchanged('{{ question.number }}', this.options[this.selectedIndex].value);" name="question_{{ question.number }}">
|
||||
{% for x in qdict.range %}
|
||||
<option value="{{ x }}" {% ifequal qdict.selected x %}selected{% endifequal %}> {{ x }} </option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input name="question_{{ question.number }}" type="range" min="{{ qdict.rmin }}" max="{{ qdict.rmax }}" step="{{ qdict.rstep }}" value="{{ qdict.current }}">
|
||||
</div>
|
||||
</div>
|
||||
<label data-unit="{{ qdict.runit }}">{{ qdict.runit }}</label>
|
||||
</div>
|
Loading…
Reference in New Issue