Fixed Survey's handling of multi-answer questions (e.g. maxAnswers != 1)

Now stores all supplied answers to multi-answer question as arrayref
Variable [[var]] text replacement converts to comma-separated list
Expression Engine's value() returns appropriate value depending on context
Expression Engine's score() does sum of multi-answers, as you would expect
Updated related i18n & hover-help, and tests
Cleaned up survey js somewhat
This commit is contained in:
Patrick Donelan 2009-05-08 08:00:04 +00:00
parent 88d6a0d067
commit cf98c81461
6 changed files with 220 additions and 203 deletions

View file

@ -45,8 +45,19 @@ Returns the recorded response value for the answer to question_variable
sub value {
my $key = shift;
my $value = $tags->{$key} || $values->{$key};
$session->log->debug("value($key) resolves to [$value]");
return $value; # scalar variable, so no need to clone
if (ref $value eq 'ARRAY') {
my $joined = join ', ', @$value;
if (wantarray) {
$session->log->debug("value($key) in list context resolves to ($joined)");
return @$value;
} else {
$session->log->debug("value($key) in scalar|void context resolves to \"$joined\"");
return $joined;
}
} else {
$session->log->debug("value($key) resolves to [$value]");
return $value;
}
}
=head2 valueX
@ -64,8 +75,19 @@ sub valueX {
if (my $other_instance = $otherInstances->{$asset_spec}) {
my $values = $other_instance->{values};
my $value = $values->{$key};
$session->log->debug("valueX($asset_spec, $key) resolves to [$value]");
return $value;
if (ref $value eq 'ARRAY') {
my $joined = join ', ', @$value;
if (wantarray) {
$session->log->debug("valueX($asset_spec, $key) in list context resolves to ($joined)");
return @$value;
} else {
$session->log->debug("valueX($asset_spec, $key) in scalar|void context resolves to \"$joined\"");
return $joined;
}
} else {
$session->log->debug("valueX($asset_spec, $key) resolves to [$value]");
return $value;
}
} else {
# Throw an exception, triggering run() to resolve the external reference and re-run
die( { other_instance => $asset_spec } );

View file

@ -815,8 +815,13 @@ sub responseValuesByVariableName {
my %options = validate(@_, { useText => 0 });
my %lookup;
while (my ($address, $response) = each %{$self->responses}) {
next if (!$response || !$address);
# Process responses in id order (so that questions with maxAnswers != 1 stringify according
# to natural ordering of answers (e.g. answer 0, answer 1, etc..
for my $address (sort keys %{$self->responses}) {
next if !$address;
my $response = $self->responses->{$address};
next if !$response;
# Turn responses s-q-a string into an address array
my @address = split /-/, $address;
@ -846,7 +851,11 @@ sub responseValuesByVariableName {
}
# Add variable => value to our hash
$lookup{$question->{variable}} = $value;
if (!$question->{maxAnswers} || $question->{maxAnswers} > 1) {
push @{$lookup{$question->{variable}}}, $value;
} else {
$lookup{$question->{variable}} = $value;
}
}
return \%lookup;
}
@ -866,8 +875,11 @@ sub responseScoresByVariableName {
my $self = shift;
my %lookup;
while (my ($address, $response) = each %{$self->responses}) {
next if (!$response || !$address);
# Process responses in id order, just to be consistent with responseValuesByVariableName
for my $address (sort keys %{$self->responses}) {
next if !$address;
my $response = $self->responses->{$address};
next if !$response;
# Turn responses s-q-a string into an address array
my @address = split /-/, $address;
@ -887,8 +899,8 @@ sub responseScoresByVariableName {
# Use question score if answer score undefined
my $score = (exists $answer->{value} && length $answer->{value} > 0) ? $answer->{value} : $question->{value};
# Add variable => score to our hash
$lookup{$question->{variable}} = $score;
# Add variable => score to our hash (or add to existing score for multi-answer questions, e.g. maxAnswers != 1)
$lookup{$question->{variable}} += $score;
}
# Add section score totals
@ -933,6 +945,11 @@ A hash reference. Each matching key in the string will be replaced with its asso
sub getTemplatedText {
my $self = shift;
my ($text, $params) = validate_pos(@_, { type => SCALAR }, { type => HASHREF });
# Turn multi-valued answers into comma-separated text
for my $value (values %$params) {
$value = join(',', @$value) if ref $value eq 'ARRAY';
}
# Replace all instances of [[var]] with the value from the $params hash reference
$text =~ s/\[\[([^\%]*?)\]\]/$params->{$1}/eg;

View file

@ -285,10 +285,10 @@ our $I18N = {
context => q|Description of the 'cols' field, used as hoverhelp in the edit question dialog.|,
lastUpdated => 1241588599
},
'comment rows' => {
message => q|Comment Rows:|,
lastUpdated => 1224686319
},
'comment rows' => {
message => q|Comment Rows:|,
lastUpdated => 1224686319
},
'rows description' => {
message => q|The number of rows used for the input field (for TextArea question types).|,
context => q|Description of the 'rows' field, used as hoverhelp in the edit question dialog.|,
@ -299,68 +299,68 @@ our $I18N = {
lastUpdated => 1224686319
},
'maximum number of answers description' => {
message => q|Enter the maximum number of answers.|,
message => q|For multi-choice questions, how many answers the user can select. <br>0 = unlimited.<br>1 = radio group style.<br>2 and above = checkbox style.|,
context => q|Description of the 'maximum number of answers' field, used as hoverhelp in the edit question dialog.|,
lastUpdated => 0
lastUpdated => 1241764603,
},
'required label' => {
message => q|Required|,
lastUpdated => 1224686319
},
'required label' => {
message => q|Required|,
lastUpdated => 1224686319
},
'required description' => {
message => q|Is this a required question?|,
context => q|Description of the 'required' field, used as hoverhelp in the edit question dialog.|,
lastUpdated => 0
},
'question score' => {
message => q|Question score:|,
lastUpdated => 1224686319
},
'question score' => {
message => q|Question score:|,
lastUpdated => 1224686319
},
'question score description' => {
message => q|Default score to use for answers in this question that don't have an answer score value set.|,
context => q|Description of the 'question value' field, used as hoverhelp in the edit question dialog.|,
lastUpdated => 1239255403
},
'please enter answer information' => {
message => q|Please enter answer information:|,
context => q|Title of the edit answer dialog.|,
lastUpdated => 1224686319
},
'answer number' => {
message => q|Answer number:|,
lastUpdated => 1224686319
},
'please enter answer information' => {
message => q|Please enter answer information:|,
context => q|Title of the edit answer dialog.|,
lastUpdated => 1224686319
},
'answer number' => {
message => q|Answer number:|,
lastUpdated => 1224686319
},
'answer number description' => {
message => q|The number of this answer|,
context => q|Description of the 'answer number' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 0
},
'answer text' => {
message => q|Answer text:|,
lastUpdated => 1224686319
},
'answer text' => {
message => q|Answer text:|,
lastUpdated => 1224686319
},
'answer text description' => {
message => q|Enter a text for this answer. For multiple choice questions this answer will be displayed above the buttons.|,
context => q|Description of the 'answer text' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 0
},
'recorded answer' => {
message => q|Recorded Answer:|,
lastUpdated => 1224686319
},
'recorded answer' => {
message => q|Recorded Answer:|,
lastUpdated => 1224686319
},
'recorded answer description' => {
message => q|Determines what gets recorded as the response value if this answer is selected. Allows you to 'recode' recorded responses, e.g. 'Yes' could be recorded as '1' and 'No' as '0'. Relevant only for Multiple Choice questions (other question types record the input actually entered by the user: free text, selected date, etc..).|,
context => q|Description of the 'recorded answer' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 1239251436
},
'jump to' => {
message => q|Jump to:|,
lastUpdated => 1224686319
},
'jump expression' => {
message => q|Jump expression:|,
lastUpdated => 1229318805
},
'jump to' => {
message => q|Jump to:|,
lastUpdated => 1224686319
},
'jump expression' => {
message => q|Jump expression:|,
lastUpdated => 1229318805
},
'jump to description' => {
message => q|The section or question with this variable name will be the next to be displayed after this answer.|,
context => q|Description of the 'jump to' field, used as hoverhelp in the edit answer dialog.|,

View file

@ -22,7 +22,7 @@ my $session = WebGUI::Test->session;
#----------------------------------------------------------------------------
# Tests
my $tests = 50;
my $tests = 52;
plan tests => $tests + 1;
#----------------------------------------------------------------------------
@ -46,6 +46,7 @@ SKIP: {
my %values = (
n => 5,
s1 => 'my string',
multi => [ 'answer1', 'answer2' ],
);
my %scores = (
@ -76,6 +77,8 @@ SKIP: {
q{jump { sum(value(n),1,1,1) == 8 } target}, # List::Util sum, etc..
q{jump { score(n1) == 1 && score(n2) == 2 } target}, # score() works
q{jump { answered(n) && !answered(X) } target}, # answered() works
q{jump { value(multi) eq 'answer1, answer2' } target}, # multi-answer question stringifies in scalar context
q{jump { (value(multi))[1] eq 'answer2' } target}, # multi-answer question returns list in list context
);
my @should_not_jump = (

View file

@ -22,7 +22,7 @@ my $session = WebGUI::Test->session;
#----------------------------------------------------------------------------
# Tests
my $tests = 87;
my $tests = 91;
plan tests => $tests + 1;
#----------------------------------------------------------------------------
@ -445,56 +445,35 @@ $rJSON->processExpression('tag(b,50); jump {tagged(a) + tagged(b) == 150} s1');
cmp_deeply($rJSON->tags, { a => 100, b => 50 }, 'Tag data cumulative');
is($rJSON->nextResponse, 3, '..and is useful for jump expressions');
# Check mult-answer questions
$rJSON->survey->question([0,2])->{maxAnswers} = 2; # Make it possible to select both "Yes" and "No" to this Yes/No mc question
$rJSON->survey->answer([0,2,0])->{value} = 4; # set 'Yes' answer score
$rJSON->survey->answer([0,2,1])->{value} = 6; # set 'No' answer score
# Record the next question in section 0
$rJSON->nextResponse(2); # pretend we just finished s0q2
$rJSON->recordResponses({
'0-2-0' => 'I chose both Yes',
'0-2-1' => '..and No to this mc question',
});
is($rJSON->nextResponse, 3, 'nextResponse at 3 (s1q0) after first response');
$rJSON->processExpression(q{jump { value(s0q2) eq '1, 0' } s2});
is($rJSON->nextResponse, 5, 'value() understands multi-answer questions, and knows how to stringify');
$rJSON->nextResponse(2); # pretend we just finished s0q2
$rJSON->processExpression(q{jump { (value(s0q2))[0] == 1 && (value(s0q2))[1] == 0 } s2});
is($rJSON->nextResponse, 5, '..and it can give us a list if thats what we want');
$rJSON->nextResponse(2); # pretend we just finished s0q2
$rJSON->processExpression(q{jump { score(s0q2) == 10 } s2});
is($rJSON->nextResponse, 5, '..and score() knows how to sum multi-answer questions');
# Clean up after this set of tests
$rJSON->responses({});
$rJSON->questionsAnswered(-1 * $rJSON->questionsAnswered);
####################################################
#
# recordedNamedResponses (coming soon)
#
####################################################
# {
#
# # $rJSON->survey->question([1,0])->{questionType} = 'Multiple Choice';
# # $rJSON->survey->answer([1,0,0])->{value} = 5;
# # cmp_deeply($rJSON->recordedNamedResponses, {}, 'recordedNamedResponses initially empty');
# # $rJSON->lastResponse(2);
# # $rJSON->recordResponses({
# # '1-0comment' => 'Section 1, question 0 comment',
# # '1-0-0' => 'My chosen answer',
# # '1-0-0comment' => 'Section 1, question 0, answer 0 comment',
# # });
# # cmp_deeply($rJSON->recordedNamedResponses, { s1q0 => 5 }, '..now shows multi-choice answer value');
# # $rJSON->survey->answer([1,0,0])->{value} = 'blah';
# # cmp_deeply($rJSON->recordedNamedResponses, { s1q0 => 'blah' }, '..also works with string value');
# # $rJSON->survey->loadTypes;
# # my $a =
# # diag(Dumper ($rJSON->survey->multipleChoiceTypes));
#
# $rJSON->survey->question([1,0])->{variable} = 's1q0';
#
# # First try with generic Multi Choice
# $rJSON->survey->question( [ 1, 0 ] )->{questionType} = 'Multiple Choice';
# $rJSON->survey->answer( [ 1, 0, 0 ] )->{recordedAnswer} = 'My recordedAnswer';
# $rJSON->lastResponse(2);
# $rJSON->recordResponses( { '1-0-0' => 'My chosen answer', } );
# is( $rJSON->responses->{'1-0-0'}->{value}, 'My recordedAnswer', 'Multi-choice uses recordedAnswer' );
#
# # Then with Yes/No bundle
# $rJSON->survey->question( [ 1, 0 ] )->{questionType} = 'Yes/No';
# $rJSON->lastResponse(2);
# $rJSON->recordResponses( { '1-0-0' => 'My chosen answer', } );
# is( $rJSON->responses->{'1-0-0'}->{value}, 'My recordedAnswer', 'Multi-choice bundle also uses recordedAnswer' );
#
# # Then with Text
# $rJSON->survey->question( [ 1, 0 ] )->{questionType} = 'Text';
# $rJSON->lastResponse(2);
# $rJSON->recordResponses( { '1-0-0' => 'My entered text', } );
# is( $rJSON->responses->{'1-0-0'}->{value}, 'My entered text', 'Text type uses entered text' );
# diag( Dumper( $rJSON->responses ) );
# diag( Dumper( $rJSON->recordedNamedResponses ) );
# }
####################################################
#
# recordResponses

View file

@ -76,13 +76,15 @@ if (typeof Survey === "undefined") {
else if (toValidate[i].type === 'Number') {
answered = 1;
for (var z1 in toValidate[i].answers) {
var m = parseFloat(document.getElementById(z1).value);
var ansValues = toValidate[i].answers[z1];
if((ansValues.max != '' && m > ansValues.max) ||
(ansValues.min != '' && m < ansValues.min) ||
(ansValues.step != '' && ( (m % ansValues.step) != 0) )){
answered = 0;
break;
if (YAHOO.lang.hasOwnProperty(toValidate[i].answers, z1)) {
var m = parseFloat(document.getElementById(z1).value);
var ansValues = toValidate[i].answers[z1];
if((ansValues.max != '' && m > ansValues.max) ||
(ansValues.min != '' && m < ansValues.min) ||
(ansValues.step != '' && ( (m % ansValues.step) != 0) )){
answered = 0;
break;
}
}
}
@ -90,13 +92,15 @@ if (typeof Survey === "undefined") {
else if (toValidate[i].type === 'Year Month') {
answered = 1;//set to true, then let a single failure set it back to false.
for (var z1 in toValidate[i].answers) {
var m = document.getElementById(z1+'-month').value;
var y = document.getElementById(z1+'-year').value;
if(m == ''){ answered = 0; }
var yInt = parseInt(y, 10);
if(!yInt) { answered = 0; }
if(yInt < 1000 || yInt > 3000) { answered = 0; }
if(answered == 1){ document.getElementById(z1).value = m + "-" + y; }
if (YAHOO.lang.hasOwnProperty(toValidate[i].answers, z1)) {
var m = document.getElementById(z1+'-month').value;
var y = document.getElementById(z1+'-year').value;
if(m == ''){ answered = 0; }
var yInt = parseInt(y, 10);
if(!yInt) { answered = 0; }
if(yInt < 1000 || yInt > 3000) { answered = 0; }
if(answered == 1){ document.getElementById(z1).value = m + "-" + y; }
}
}
}
else {
@ -218,10 +222,10 @@ if (typeof Survey === "undefined") {
var min = Math.round(parseFloat(q.answers[0].min));
//The number of values in between the max and min values
var distance = parseInt(parseFloat(q.answers[0].max) + (-1 * min));
var distance = parseInt(parseFloat(q.answers[0].max) + (-1 * min), 10);
//Number of pixels each bug step takes
var bugSteps = parseInt(total / ((+q.answers[0].max + (-1 * q.answers[0].min) ) / step));
var bugSteps = parseInt(total / ((+q.answers[0].max + (-1 * q.answers[0].min) ) / step), 10);
//redefine number of pixels to round number of steps
total = distance * bugSteps / step;
@ -305,7 +309,7 @@ if (typeof Survey === "undefined") {
Event.on(document.getElementById(s.input), "blur", manualEntry);
Event.on(document.getElementById(s.input), "keypress", manualEntry);
var getRealValue = function(){
return parseInt((this.getValue() / bugSteps * step) + +min);
return parseInt((this.getValue() / bugSteps * step) + +min, 10);
};
s.getRealValue = getRealValue;
document.getElementById(s.input).value = s.getRealValue();
@ -365,71 +369,78 @@ if (typeof Survey === "undefined") {
obj[0].hide();
}
function buttonChanged(event, objs){
var b = objs[0];
var qid = objs[1];
var maxA = objs[2];
var butts = objs[3];
var qsize = objs[4];
var aid = objs[5];
//max = parseFloat(max);
// clearTimeout(Survey.Form.submittimer);
if (maxA) {
if (b.className === 'mcbutton-selected') {
document.getElementById(b.hid).value = 0;
b.className = 'mcbutton';
function toggleButtonOn( button ) {
document.getElementById(button.hid).value = 1;
button.className = 'mcbutton-selected';
}
function toggleButtonOff( button ) {
document.getElementById(button.hid).value = '';
button.className = 'mcbutton';
}
function buttonChanged(event, o){
var b = o.button;
var maxAnswers = parseInt(o.maxAnswers, 10);
var buttons = o.buttons;
// When maxAnswers is 2 or greater, user is required to manually toggle options on/off
// (e.g. simulate checkboxes), and enforce maxAnswers limit
// When maxAnswers is 0, user is also required to manually toggle options on/off,
// but no limit is placed on maxAnswers
// When maxAnswers is 1, simulate radio group instead
// (e.g. no need to untoggle selected answer to change to a different answer)
if (!maxAnswers || maxAnswers > 1) {
// Simulate checkbox..
// See if answer is currently toggled on or off
if (b.className === 'mcbutton') {
// Answer currently off (unselected). User wants to toggle it on.
// See if we need to enforce maxAnswers limit
if (maxAnswers) {
// Count how many buttons are currently selected
var bscount = 0;
for (var ib in buttons) {
if (YAHOO.lang.hasOwnProperty(buttons, ib) && buttons[ib].className === 'mcbutton-selected') {
bscount++;
}
}
// Proceed if we haven't filled all the allowed selections
if (maxAnswers - bscount > 0) {
toggleButtonOn(b);
}
}
else {
// No maxAnswers limit, toggle answer on without counting
toggleButtonOn(b);
}
}
else {
document.getElementById(b.hid).value = 1;
b.className = 'mcbutton-selected';
// Answer currently on (selected). User wants to toggle it off.
toggleButtonOff(b);
}
for (var i in butts) {
if (YAHOO.lang.hasOwnProperty(butts, i)) {
if (butts[i] !== b) {
butts[i].className = 'mcbutton';
document.getElementById(butts[i].hid).value = '';
}
}
else {
// maxAnswers == 1, so simulate Radio group instead
if (b.className === 'mcbutton-selected') {
toggleButtonOff(b);
}
else {
toggleButtonOn(b);
}
// Toggle off all other answers
for (var i in buttons) {
if (YAHOO.lang.hasOwnProperty(buttons, i) && buttons[i] !== b) {
toggleButtonOff(buttons[i]);
}
}
}
else
if (b.className === 'mcbutton') {
var bscount = 0;//button selected count
for (var ib in butts) {
if (butts[ib].className === 'mcbutton-selected') {
bscount++;
}
}
var max = maxA - bscount;//= parseFloat(document.getElementById(qid+'max').innerHTML);
if (max === 0) {
b.className = 'mcbutton';
//warn that options used up
}
else {
b.className = 'mcbutton-selected';
//document.getElementById(qid+'max').innerHTML = parseFloat(max-1);
document.getElementById(b.hid).value = 1;
}
}
else {
b.className = 'mcbutton';
var bscount1 = 0;//button selected count
for (var ibb in butts) {
if (butts[ibb].className === 'mcbutton-selected') {
bscount1++;
}
}
//var max = maxA - bscount1;//= parseFloat(document.getElementById(qid+'max').innerHTML);
// document.getElementById(qid+'max').innerHTML = parseFloat(max+1);
document.getElementById(b.hid).value = '';
}
/*
if(qsize == 1 && b.className == 'mcbutton-selected'){
if(! document.getElementById(aid+'verbatim')){
Survey.Form.submittimer=setTimeout("Survey.Form.formsubmit()",500);
}
}
*/
}
YAHOO.widget.Chart.SWFURL = "/extras/yui/build/charts/assets/charts.swf";
@ -447,18 +458,16 @@ if (typeof Survey === "undefined") {
//Add totoal summary pie chart
totalSummary =
[
var totalSummary = [
{ correct: "Correct", count: summary.totalCorrect },
{ correct: "Incorrect", count: summary.totalIncorrect }
]
];
var totalSummaryDS = new YAHOO.util.DataSource( totalSummary );
totalSummaryDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
totalSummaryDS.responseSchema = { fields: [ "correct", "count" ] };
new YAHOO.widget.PieChart( "chart", totalSummaryDS,
{
new YAHOO.widget.PieChart( "chart", totalSummaryDS, {
dataField: "count",
categoryField: "correct",
style:
@ -496,13 +505,13 @@ if (typeof Survey === "undefined") {
var temp = summary.sections[i];
sectionSummary[sectionSummary.length] = {"Total Responses": temp.total, "Correct": temp.correct, "Incorrect": temp.inCorrect, "section": (i+1)};
var myDataSource = new YAHOO.util.DataSource(summary.sections[i].responses);
//These needs to be put in a destroy call list for when the html dom is recreated, if summaries are going to be uses with page reloads, else memory leak.
//These needs to be put in a destroy call list for when the html dom is recreated, if summaries are going to be uses with page reloads, else memory leak.
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
myDataSource.responseSchema = {
fields: ["Question ID","Question Text","Answer ID","Correct","Answer Text","Score","Value"]
};
var tempText = "section"+ (i+1) + "datatable";
new YAHOO.widget.DataTable(tempText, myColumnDefs, myDataSource, {caption:"Section "+(i+1)});
var tempText = "section" + (i+1) + "datatable";
new YAHOO.widget.DataTable(tempText, myColumnDefs, myDataSource, { caption: "Section " + (i+1) } );
}
//Now create section summary bar charts
@ -706,10 +715,12 @@ if (typeof Survey === "undefined") {
}
if (NUMBER_TYPES[q.questionType]) {
for (var x in q.answers) {
if (toValidate[q.id]) {
toValidate[q.id].answers[q.answers[x].id] = {'min':q.answers[x].min,'max':q.answers[x].max,'step':q.answers[x].step};
if (YAHOO.lang.hasOwnProperty(q.answers, x)) {
if (toValidate[q.id]) {
toValidate[q.id].answers[q.answers[x].id] = {'min':q.answers[x].min,'max':q.answers[x].max,'step':q.answers[x].step};
}
YAHOO.util.Event.addListener(q.answers[x].id, "keyup", numberHandler, q.answers[x]);
}
YAHOO.util.Event.addListener(q.answers[x].id, "keyup", numberHandler, q.answers[x]);
}
continue;
}
@ -722,17 +733,10 @@ if (typeof Survey === "undefined") {
toValidate[q.id].answers[a.id] = 1;
}
var b = document.getElementById(a.id + 'button');
/*
b = new YAHOO.widget.Button({ type: "checkbox", label: a.answerText, id: a.id+'button', name: a.id+'button',
value: a.id,
container: a.id+"container", checked: false });
*/
// b.on("click", buttonChanged,[b,a.id,q.maxAnswers,butts,qs.length,a.id]);
// YAHOO.util.Event.addListener(a.id+'button', "click", buttonChanged,[b,a.id,q.maxAnswers,butts,qs.length,a.id]);
if (a.verbatim) {
verb = 1;
}
YAHOO.util.Event.addListener(a.id + 'button', "click", buttonChanged, [b, a.id, q.maxAnswers, butts, qs.length, a.id]);
YAHOO.util.Event.addListener(a.id + 'button', "click", buttonChanged, { button: b, maxAnswers: q.maxAnswers, buttons: butts });
b.hid = a.id;
butts.push(b);
}
@ -748,12 +752,4 @@ if (typeof Survey === "undefined") {
YAHOO.util.Event.onDOMReady(function(){
// Survey.Comm.setUrl('/' + document.getElementById('assetPath').value);
Survey.Comm.callServer('', 'loadQuestions');
});
YAHOO.example.getDataTipText = function( item, index, series )
{
var toolTipText = series.displayName + " for " + item.month;
// toolTipText += "\n" + YAHOO.example.formatCurrencyAxisLabel( item[series.yField] );
return toolTipText;
}
});