http://www.mediawiki.org/wiki/Special:Code/MediaWiki/100337
Revision: 100337
Author: questpc
Date: 2011-10-20 11:02:33 +0000 (Thu, 20 Oct 2011)
Log Message:
-----------
Added client side radiobutton switch logic for question type text. Fixes for
checkboxes and radiobuttons in question type text controller / view pair.
Capitalized CSS X11 color names.
Modified Paths:
--------------
trunk/extensions/QPoll/clientside/qp_results.css
trunk/extensions/QPoll/clientside/qp_user.css
trunk/extensions/QPoll/clientside/qp_user.js
trunk/extensions/QPoll/ctrl/question/qp_mixedquestion.php
trunk/extensions/QPoll/ctrl/question/qp_tabularquestion.php
trunk/extensions/QPoll/ctrl/question/qp_textquestion.php
trunk/extensions/QPoll/includes/qp_renderer.php
trunk/extensions/QPoll/view/question/qp_textquestionview.php
trunk/extensions/QPoll/view/results/qp_textquestiondataresults.php
Modified: trunk/extensions/QPoll/clientside/qp_results.css
===================================================================
--- trunk/extensions/QPoll/clientside/qp_results.css 2011-10-20 10:29:51 UTC
(rev 100336)
+++ trunk/extensions/QPoll/clientside/qp_results.css 2011-10-20 11:02:33 UTC
(rev 100337)
@@ -1,17 +1,18 @@
-.qpoll .head {font-weight:bold; color:gray;}
+.qpoll .head {font-weight:bold; color:Gray;}
.qpoll table.qdata {border-collapse: collapse;}
-.qpoll table.qdata th {border: 1px gray solid; background-color:lightgray;
padding: 3px;}
+.qpoll table.qdata th {border: 1px Gray solid; background-color:LightGrey;
padding: 3px;}
.qpoll table.qdata tr.spans { color:Navy; }
-.qpoll table.qdata td {border: 1px gray solid; text-align: center; padding:
3px;}
+.qpoll table.qdata td {border: 1px Gray solid; text-align: center; padding:
3px;}
.qpoll table.qdata td.stats {background-color: Azure;}
.qpoll table.qdata td.spaneven {background-color: Aquamarine;}
.qpoll table.qdata td.spanodd {background-color: Moccasin;}
.qpoll table.qdata tr.qdatatext td { text-align: left; }
-.qpoll .cat_part { background-color: Lightyellow; border: 1px solid gray;
padding: 0 0.5em 0 0.5em; }
-.qpoll .cat_unknown { color: white; background-color: IndianRed; border: 1px
solid gray; padding: 0 0.5em 0 0.5em; }
-.qpoll .interp_header { background-color: lightgray; padding: 0.2em; border:
1px solid gray; margin: 0.3em 0 0.3em 0; }
-.qpoll .interp_answer { border: 1px solid gray; padding: 0.2em; margin: 0.3em
0 0.3em 0; color: black; background-color: lightblue; font-weight:600;
line-height:2em; }
-.qpoll .interp_answer_body { border: none; border-top: 1px solid gray;
padding: 0.5em; margin-top: 0; color: black; background-color: Azure;
font-weight: normal; line-height:normal; }
+.qpoll .cat_part { background-color: LightYellow; border: 1px solid Gray;
padding: 0 0.5em 0 0.5em; }
+.qpoll .cat_unanswered { color: White; background-color: PowderBlue; border:
1px solid Gray; padding: 0 0.5em 0 0.5em; }
+.qpoll .cat_unknown { color: White; background-color: IndianRed; border: 1px
solid Gray; padding: 0 0.5em 0 0.5em; }
+.qpoll .interp_header { background-color: LightGrey; padding: 0.2em; border:
1px solid Gray; margin: 0.3em 0 0.3em 0; }
+.qpoll .interp_answer { border: 1px solid Gray; padding: 0.2em; margin: 0.3em
0 0.3em 0; color: Black; background-color: LightBlue; font-weight:600;
line-height:2em; }
+.qpoll .interp_answer_body { border: none; border-top: 1px solid Gray;
padding: 0.5em; margin-top: 0; color: Black; background-color: Azure;
font-weight: normal; line-height:normal; }
.qpoll table.structured_answer { border-collapse: collapse; margin: 0.3em 0
0.3em 0; }
-.qpoll table.structured_answer th { text-align:left; border-top:1px solid
gray; border-left:1px solid gray; border-right:1px solid gray;
background-color: lightgray; padding: 0 0.2em 0 0.2em; }
-.qpoll table.structured_answer td { border-bottom:1px solid gray;
border-left:1px solid gray; border-right:1px solid gray; background-color:
White; padding: 0 0.2em 0 0.2em; }
+.qpoll table.structured_answer th { text-align:left; border-top:1px solid
Gray; border-left:1px solid Gray; border-right:1px solid Gray;
background-color: LightGrey; padding: 0 0.2em 0 0.2em; }
+.qpoll table.structured_answer td { border-bottom:1px solid Gray;
border-left:1px solid Gray; border-right:1px solid Gray; background-color:
White; padding: 0 0.2em 0 0.2em; }
Modified: trunk/extensions/QPoll/clientside/qp_user.css
===================================================================
--- trunk/extensions/QPoll/clientside/qp_user.css 2011-10-20 10:29:51 UTC
(rev 100336)
+++ trunk/extensions/QPoll/clientside/qp_user.css 2011-10-20 11:02:33 UTC
(rev 100337)
@@ -1,58 +1,58 @@
.qpoll .error { background-color: LightYellow; }
.qpoll .object_error { background-color: #D700D7; }
-.qpoll .fatalerror { border: 1px solid gray; padding: 4px; background-color:
LightYellow; }
+.qpoll .fatalerror { border: 1px solid Gray; padding: 4px; background-color:
LightYellow; }
.qpoll .settings input.numerical { width:2em; }
.qpoll .header {font-weight: bold;}
*>.qpoll .header .questionId {text-indent: -1.5em; } /* *> prevent ie6 to
interprate it. */
.qpoll table.object { background-color:transparent; border-collapse:collapse; }
.qpoll table.object .proposaltext { }
.qpoll table.object th { padding-left: 5px; padding-right: 5px;
vertical-align:bottom; }
-.qpoll table.object .spans { color:gray; }
+.qpoll table.object .spans { color:Gray; }
.qpoll table.object .categories { color:#444455; }
.qpoll table.object .proposal { }
-.qpoll span.proposalerror { color:red; font-weight:600; }
+.qpoll span.proposalerror { color:Red; font-weight:600; }
.qpoll tr.proposalerror { background-color: Snow; }
.qpoll .settings td { padding: 0.1em 0.4em 0.1em 0.4em }
.qpoll table.settings { background-color:transparent; }
/* Part for the basic types's inputs. */
.qpoll table.stats td { text-align:center; margin:2px; padding:0px;
border:none; border-collapse:collapse; }
-.qpoll div.stats { text-align:center; font-size:7pt; color:green;
padding-left: 2px; padding-right: 2px;}
-.qpoll div.stats1 { text-align:center; font-size:7pt; color:green;
width:162px; margin:0px auto; margin-top:2px; }
-.qpoll div.stats2 { text-align:center; font-size:7pt; height:20px;
font-weight:bold; color:green; width:105px; margin:0px auto; padding-left:3px;
padding-bottom:1px; margin-top:1px; margin-bottom:2px; }
+.qpoll div.stats { text-align:center; font-size:7pt; color:Green;
padding-left: 2px; padding-right: 2px;}
+.qpoll div.stats1 { text-align:center; font-size:7pt; color:Green;
width:162px; margin:0px auto; margin-top:2px; }
+.qpoll div.stats2 { text-align:center; font-size:7pt; height:20px;
font-weight:bold; color:Green; width:105px; margin:0px auto; padding-left:3px;
padding-bottom:1px; margin-top:1px; margin-bottom:2px; }
.qpoll div.stats2 div { height:20px; }
.qpoll div.bar0 { float:left; width:30px; }
-.qpoll div.bar1 { float:left; background-color:gainsboro; border-left: 1px
solid gray; border-top: 1px solid gray; border-bottom: 1px solid gray; }
-.qpoll div.bar2 { float:left; background-color:linen; border-right: 1px solid
gray; border-top: 1px solid gray; border-bottom: 1px solid gray; }
+.qpoll div.bar1 { float:left; background-color:Gainsboro; border-left: 1px
solid Gray; border-top: 1px solid Gray; border-bottom: 1px solid Gray; }
+.qpoll div.bar2 { float:left; background-color:Linen; border-right: 1px solid
Gray; border-top: 1px solid Gray; border-bottom: 1px solid Gray; }
.qpoll div.bar3 { width:100px; left:0px; top:-20px; position:relative;
z-index:10; }
.qpoll .sign { text-align:center; }
-.qpoll .signl { text-align:center; border-bottom:1px solid gray; border-top:
1px solid gray; border-left: 1px solid gray; }
-.qpoll .signc { text-align:center; border-bottom:1px solid gray; border-top:
1px solid gray; }
-.qpoll .signr { text-align:center; border-bottom:1px solid gray; border-top:
1px solid gray; border-right: 1px solid gray; }
-.qpoll .signt { text-align:center; border-left: 1px solid gray; border-right:
1px solid gray; border-top: 1px solid gray; }
-.qpoll .signm { text-align:center; border-left: 1px solid gray; border-right:
1px solid gray; }
-.qpoll .signb { text-align:center; border-left: 1px solid gray; border-right:
1px solid gray; border-bottom:1px solid gray; }
+.qpoll .signl { text-align:center; border-bottom:1px solid Gray; border-top:
1px solid Gray; border-left: 1px solid Gray; }
+.qpoll .signc { text-align:center; border-bottom:1px solid Gray; border-top:
1px solid Gray; }
+.qpoll .signr { text-align:center; border-bottom:1px solid Gray; border-top:
1px solid Gray; border-right: 1px solid Gray; }
+.qpoll .signt { text-align:center; border-left: 1px solid Gray; border-right:
1px solid Gray; border-top: 1px solid Gray; }
+.qpoll .signm { text-align:center; border-left: 1px solid Gray; border-right:
1px solid Gray; }
+.qpoll .signb { text-align:center; border-left: 1px solid Gray; border-right:
1px solid Gray; border-bottom:1px solid Gray; }
/* Part for the inputfields */
-.qpoll a.input, .qpoll a.input:hover, .qpoll a.input:active, .qpoll
a.input:visited { text-decoration:none; color:black; outline: 0 }
+.qpoll a.input, .qpoll a.input:hover, .qpoll a.input:active, .qpoll
a.input:visited { text-decoration:none; color:Black; outline: 0 }
.qpoll a.input span { outline:#7F9DB9 solid 1px; border:1px solid #7F9DB9; }
/* *border is for IE6/7 star is removed due to ff console error */
-.qpoll .cat_prefilled { color: blue; }
+.qpoll .cat_prefilled { color: Blue; }
.qpoll .cat_noanswer { background-color: LightYellow; }
-.qpoll .cat_error { background-color: LightYellow; border: 1px solid gray; }
-.qpoll .interp_error { border: 2px solid gray; padding: 0.5em; color: white;
background-color: red; margin: 0.3em 0 0.3em 0; }
-.qpoll .interp_answer { border: 2px solid gray; padding: 0.5em; color: black;
background-color: lightblue; margin: 0.3em 0 0.3em 0; }
+.qpoll .cat_error { background-color: LightYellow; border: 1px solid Gray; }
+.qpoll .interp_error { border: 2px solid Gray; padding: 0.5em; color: White;
background-color: Red; margin: 0.3em 0 0.3em 0; }
+.qpoll .interp_answer { border: 2px solid Gray; padding: 0.5em; color: Black;
background-color: LightBlue; margin: 0.3em 0 0.3em 0; }
.qpoll table.structured_answer { border-collapse: collapse; margin: 0.3em 0
0.3em 0; }
-.qpoll table.structured_answer th { text-align:left; border-top:1px solid
gray; border-left:1px solid gray; border-right:1px solid gray;
background-color: Moccasin; padding: 0 0.2em 0 0.2em; }
-.qpoll table.structured_answer td { border-bottom:1px solid gray;
border-left:1px solid gray; border-right:1px solid gray; background-color:
Azure; padding: 0 0.2em 0 0.2em; }
+.qpoll table.structured_answer th { text-align:left; border-top:1px solid
Gray; border-left:1px solid Gray; border-right:1px solid Gray;
background-color: Moccasin; padding: 0 0.2em 0 0.2em; }
+.qpoll table.structured_answer td { border-bottom:1px solid Gray;
border-left:1px solid Gray; border-right:1px solid Gray; background-color:
Azure; padding: 0 0.2em 0 0.2em; }
/* script view */
-.qpoll .line_numbers { font-family: monospace, "Courier New"; white-space:pre;
color:green; background-color: lightgray; float:left; border-left: 1px solid
darkgray; border-top: 1px solid darkgray; border-bottom: 1px solid darkgray;
padding: 0.5em; }
-.qpoll .script_view { font-family: monospace, "Courier New"; white-space:pre;
overflow:auto; color:black; background-color: lightgray; border-right: 1px
solid darkgray; border-top: 1px solid darkgray; border-bottom: 1px solid
darkgray; padding: 0.5em; }
+.qpoll .line_numbers { font-family: monospace, "Courier New"; white-space:pre;
color:Green; background-color: LightGray; float:left; border-left: 1px solid
DarkGray; border-top: 1px solid DarkGray; border-bottom: 1px solid DarkGray;
padding: 0.5em; }
+.qpoll .script_view { font-family: monospace, "Courier New"; white-space:pre;
overflow:auto; color:Black; background-color: LightGray; border-right: 1px
solid DarkGray; border-top: 1px solid DarkGray; border-bottom: 1px solid
DarkGray; padding: 0.5em; }
/* LTR part (RTL is included from separate file) */
-.qpoll .attempts_counter{ border: 1px solid gray; padding: 0.1em 0.5em 0.1em
0.5em; color: black; background-color: lightblue; margin-left: 1em; }
+.qpoll .attempts_counter{ border: 1px solid gray; padding: 0.1em 0.5em 0.1em
0.5em; color: Black; background-color: LightBlue; margin-left: 1em; }
.qpoll .question { margin-left:2em; padding:0.3em; }
.qpoll .margin { padding-left:20px; }
.qpoll .header .questionId { font-size: 1.1em; font-weight: bold; float: left;
}
.qpoll .header .questionId { *padding-right: 0.5em; } /* applies to IE7 and
below */
-.qpoll a.input em { color:black; background-color:#DFDFDF; margin-right:1px; }
+.qpoll a.input em { color:Black; background-color:#DFDFDF; margin-right:1px; }
.qpoll a.input input { padding-left:2px; border:0; }
.qpoll .error_mark { border-left: 3px solid #D700D7; }
Modified: trunk/extensions/QPoll/clientside/qp_user.js
===================================================================
--- trunk/extensions/QPoll/clientside/qp_user.js 2011-10-20 10:29:51 UTC
(rev 100336)
+++ trunk/extensions/QPoll/clientside/qp_user.js 2011-10-20 11:02:33 UTC
(rev 100337)
@@ -33,107 +33,203 @@
*/
(function() {
- function uniqueRadioButtonColumn() {
- // example of input id: 'uq1c2p2'
- var propId, inp;
- var id = new String( this.getAttribute('id') );
- // IE split is really buggy, so we have to search for p letter
instead, was:
- // var params = id.split('/(uq\d{1,}?c\d{1,}?p)(\d{1,}?)/g');
- // if ( params !== null && params[1] !== null && params[2] !=
null ) {
- // var currPropId = parseInt( params[2] );
- // var basepart = params[1];
- // ...
- var p = id.indexOf( 'p' ) + 1;
- var currPropId = parseInt( id.substring( p ) );
- var basepart = id.substring( 0, p );
- if ( p != 0) {
- for ( propId = 0; ( inp = document.getElementById(
basepart + propId ) ) !== null; propId++ ) {
- if ( propId != currPropId) {
+
+ var self = {
+
+ radioIsClicked : false,
+
+ // coordinate of current poll's question input id
+ catCoord : {
+ 'toq' : '', // question prefix which contains
"T"ype_of_poll + poll_"O"rder_id + "Q"uestion_id,
+ 'p' : '-1', // "P"roposal_id,
+ 'c' : '-1' // "C"ategory_id
+ },
+
+ /**
+ * Parses coordinate of poll's input stored in id and
+ * stores it into self.catCoord;
+ * @param id id attribute of input / select element
+ * @return true, when value of id has valid coordinate, false
otherwise.
+ */
+ setCatCoord : function( id ) {
+ // IE split is really buggy, so we have to search for p
and c letters instead
+ var p, c;
+ if ( ( p = id.indexOf( 'p' ) + 1 ) == -1 ||
+ ( c = id.indexOf( 'c' ) + 1 ) == -1 ) {
+ return false;
+ }
+ self.catCoord.toq = id.slice( 0, p - 1 );
+ if ( p > c ||
+ ( p = parseInt( id.substring( p ) ) )
=== NaN ||
+ ( c = parseInt( id.substring( c ) ) )
=== NaN ) {
+ self.catCoord.toq = '';
+ return false;
+ }
+ self.catCoord.p = p;
+ self.catCoord.c = c;
+ return true;
+ },
+
+ /**
+ * Get input node of current poll's question (stored in
self.catCoord.toq) with specific coordinates.
+ * @param params object 'prop' (optional) property id; 'cat'
(optional) category id.
+ */
+ getCatByCoord : function( params ) {
+ var p = ( typeof params['prop'] !== 'undefined' ) ?
params['prop'] : self.catCoord.p;
+ var c = ( typeof params['cat'] !== 'undefined' ) ?
params['cat'] : self.catCoord.c;
+ return document.getElementById( self.catCoord.toq + 'p'
+ p + 'c' + c );
+ },
+
+ /**
+ * Logic of switching on / off radiobuttons for all inputs in
one proposal line.
+ * @param catElem DOM node of
poll/question/proposal/category; input or select only.
+ */
+ applyRadio : function( catElem ) {
+ if ( self.radioIsClicked ) {
+ // deselect all inputs
+ if ( catElem.nodeName == 'SELECT' ||
catElem.type == 'text' ) {
+ catElem.value = '';
+ } else {
+ catElem.checked = false;
+ }
+ } else {
+ // deselect only radio
+ if ( catElem.nodeName == 'INPUT' &&
catElem.type == 'radio' ) {
+ catElem.checked = false;
+ }
+ }
+ },
+
+ /**
+ * Makes this radio button to be unique in their column
+ * (it is already unique in their row)
+ * Used for question type="unique()"
+ */
+ uniqueRadioButtonColumn : function() {
+ // example of input id: 'uq3q1p4c2'
+ // where uq3 is mOrderId of poll on the page
+ // q1 first question of that poll
+ // p4 proposal index 4 of that question
+ // c2 category index 2 of that proposal
+ var propId, inp;
+ if ( !self.setCatCoord( this.getAttribute( 'id' ) ) ) {
+ return;
+ }
+ for ( propId = 0; ( inp = self.getCatByCoord( { 'prop'
: propId } ) ) !== null; propId++ ) {
+ if ( propId != self.catCoord.p ) {
inp.checked = false;
}
}
- }
- }
+ },
- function clickMixedRow() {
- // example of input id: 'mx1p2c2'
- var catId, inp;
- var id = new String( this.getAttribute('id') );
- var c = id.indexOf( 'c' ) + 1;
- var currCatId = parseInt( id.substring( c ) );
- var basepart = id.substring( 0, c );
- if ( c != 0) {
- for ( catId = 0; ( inp = document.getElementById(
basepart + catId ) ) !== null; catId++ ) {
- if ( catId != currCatId ) {
- if ( this.type == 'radio' ) {
- if (inp.type == 'text' ) {
- inp.value = '';
- } else {
- inp.checked = false;
- }
- } else {
- if (inp.type == 'radio' ) {
- inp.checked = false;
- }
- }
+ processMixedRow : function( currElem ) {
+ var catId, inp;
+ self.radioIsClicked = ( currElem.nodeName == 'INPUT' &&
currElem.type == 'radio' );
+ if ( !self.setCatCoord( currElem.getAttribute( 'id' ) )
) {
+ return false;
+ }
+ for ( catId = 0; ( inp = self.getCatByCoord( { 'cat' :
catId } ) ) !== null; catId++ ) {
+ if ( catId != self.catCoord.c ) {
+ self.applyRadio( inp );
}
}
- }
- }
+ return true;
+ },
- function clickPrefilledText() {
- if ( this.className === 'cat_prefilled' ) {
- this.select();
- this.className = 'cat_part';
- }
- }
+ /**
+ * Makes this input to switch radiobuttons in the same row
+ * Used for questions type="mixed"
+ */
+ clickMixedRow : function() {
+ // example of input id: 'mx1q3p2c4'
+ self.processMixedRow( this );
+ },
- /**
- * Prepare the Poll for "javascriptable" browsers
- */
- function preparePoll() {
- var bodyContentDiv = document.getElementById( 'bodyContent'
).getElementsByTagName( 'div' );
- var inputFuncId;
- for ( var i=0; i<bodyContentDiv.length; ++i ) {
- if ( bodyContentDiv[i].className == 'qpoll' ) {
+ /**
+ * Used for questions type="text", type="text!"
+ */
+ clickTextRow : function() {
+ // example of input id: 'tx1q3p2c4'
+ if ( !self.processMixedRow( this ) ) {
+ return;
+ }
+ // clear pre_filled text attribute, when available
+ if ( hasClass( this, 'cat_prefilled' ) ) {
+ this.select();
+ removeClass( this, 'cat_prefilled' );
+ }
+ },
+
+ /**
+ * Prepare the Poll for "javascriptable" browsers
+ */
+ preparePoll : function() {
+ var bodyContentDiv = document.getElementById(
'bodyContent' ).getElementsByTagName( 'div' );
+ var i, j;
+ for ( i=0; i<bodyContentDiv.length; i++ ) {
+ if ( !hasClass( bodyContentDiv[i], 'qpoll' ) ) {
+ continue;
+ }
var input =
bodyContentDiv[i].getElementsByTagName( 'input' );
- for ( var j=0; j<input.length; ++j ) {
+ for ( j=0; j<input.length; j++ ) {
if ( input[j].id ) {
// only unique or mixed
questions currently have id
- inputFuncId = new String(
input[j].id ).slice( 0, 2 );
- switch ( inputFuncId ) {
- case "uq":
+ switch ( input[j].id.slice( 0,
2 ) ) {
+ case 'uq' :
if ( input[j].type ==
"radio" ) {
// unset the
column of radiobuttons in case of unique proposal
- addEvent(
input[j], "click", uniqueRadioButtonColumn );
+ addEvent(
input[j], "click", self.uniqueRadioButtonColumn );
}
break;
- case "mx":
+ case 'mx' :
// unset the row of
checkboxes in case of "mixed" question type
- addEvent( input[j],
"click", clickMixedRow );
+ addEvent( input[j],
"click", self.clickMixedRow );
break;
+ case 'tx' :
+ // handler for text
question proposal rows
+ addEvent( input[j],
"click", self.clickTextRow );
}
} else {
- // check non-unique, non-mixed
tabular questions and
- // text questions
- switch ( input[j].getAttribute(
'type' ) ) {
- case 'radio' :
+ // non-unique, non-mixed
tabular questions
+ if ( input[j].getAttribute(
'type' ) == 'radio' ) {
// Add the possibility
of unchecking radio buttons
addEvent( input[j],
"dblclick", function() { this.checked = false; } );
- break;
- case 'text' :
- if ( input[j].className
=== 'cat_prefilled' ) {
- // Add the
handler to clear prefilled text
- addEvent(
input[j], "click", clickPrefilledText );
- }
- break;
}
}
}
+ var select =
bodyContentDiv[i].getElementsByTagName( 'select' );
+ for ( j = 0; j < select.length; j++ ) {
+ // selects currently are used only with
question type="text", type="text!"
+ if ( select[j].id &&
select[j].id.slice( 0, 2 ) == 'tx' ) {
+ addEvent( select[j], "click",
self.clickTextRow );
+ }
+ }
}
}
+ };
+
+ /**
+ * The following functions are defined to not to be dependant on jQuery
(MW 1.15).
+ * They should not cause any trouble, running in the closure.
+ * Class manipulation is taken from
http://www.openjs.com/scripts/dom/class_manipulation.php
+ */
+ function hasClass( ele, cls ) {
+ return ele.className.match( new RegExp('(\\s|^)'+cls+'(\\s|$)')
);
}
+ function addClass( ele, cls ) {
+ if ( !this.hasClass(ele,cls)) {
+ ele.className += " "+cls;
+ }
+ }
+
+ function removeClass( ele, cls ) {
+ if ( hasClass(ele,cls) ) {
+ var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
+ ele.className = ele.className.replace(reg,'
').replace(/\s+/g,' ').replace(/^\s|\s$/,'');
+ }
+ }
+
function addEvent( obj, type, fn ) {
if (obj.addEventListener) {
obj.addEventListener( type, fn, false );
@@ -148,7 +244,7 @@
}
}
- if (document.getElementById && document.createTextNode) {
- addEvent(window,"load",preparePoll);
+ if ( document.getElementById && document.createTextNode ) {
+ addEvent( window, "load", self.preparePoll );
}
-})();
\ No newline at end of file
+})();
Modified: trunk/extensions/QPoll/ctrl/question/qp_mixedquestion.php
===================================================================
--- trunk/extensions/QPoll/ctrl/question/qp_mixedquestion.php 2011-10-20
10:29:51 UTC (rev 100336)
+++ trunk/extensions/QPoll/ctrl/question/qp_mixedquestion.php 2011-10-20
11:02:33 UTC (rev 100337)
@@ -102,8 +102,8 @@
}
# always borderless (mixed questions do not
have spans)
$pview->setCategorySpan();
- # unique (question,proposal,category)
"coordinate" for javascript
- $inp['id'] = 'mx' . $this->mQuestionId . 'p' .
$proposalId . 'c' . $catId;
+ # unique (poll,question,proposal,category)
"coordinate" for javascript
+ $inp['id'] =
"mx{$this->poll->mOrderId}q{$this->mQuestionId}p{$proposalId}c{$catId}";
$inp['class'] = 'check';
$inp['type'] = $inputType;
$inp['name'] = $name;
Modified: trunk/extensions/QPoll/ctrl/question/qp_tabularquestion.php
===================================================================
--- trunk/extensions/QPoll/ctrl/question/qp_tabularquestion.php 2011-10-20
10:29:51 UTC (rev 100336)
+++ trunk/extensions/QPoll/ctrl/question/qp_tabularquestion.php 2011-10-20
11:02:33 UTC (rev 100337)
@@ -329,8 +329,8 @@
}
$pview->setCategorySpan();
if ( $this->mSubType == 'unique' ) {
- # unique (question,category,proposal)
"coordinate" for javascript
- $inp['id'] = 'uq' . $this->mQuestionId
. 'c' . $catId . 'p' . $proposalId;
+ # unique
(orderid,question,proposal,category) "coordinate" for javascript
+ $inp['id'] =
"uq{$this->poll->mOrderId}q{$this->mQuestionId}p{$proposalId}c{$catId}";
# If type='unique()' question has more
proposals than categories, such question is impossible to complete
if ( count( $this->mProposalText ) >
count( $this->mCategories ) ) {
# if there was no previous
errors, hightlight the whole row
Modified: trunk/extensions/QPoll/ctrl/question/qp_textquestion.php
===================================================================
--- trunk/extensions/QPoll/ctrl/question/qp_textquestion.php 2011-10-20
10:29:51 UTC (rev 100336)
+++ trunk/extensions/QPoll/ctrl/question/qp_textquestion.php 2011-10-20
11:02:33 UTC (rev 100337)
@@ -159,7 +159,7 @@
class qp_TextQuestion extends qp_StubQuestion {
# regexp for separation of proposal line tokens
- static $propCatPattern = null;
+ var $propCatPattern = null;
# source "raw" tokens (preg_split)
var $rawtokens;
@@ -167,14 +167,14 @@
/**
* array with parsed braces pairs
* every element may have the following keys:
- * 'type' : type of brace string _key_values_ of
self::$matching_braces
+ * 'type' : type of brace string _key_values_ of
$this->matching_braces
* 'closed_at' : indicates opening brace;
* false when no matching closing brace was found
* int key of $this->rawtokens a "link" to matching closing brace
* 'opened_at' : indicates closing brace;
* false when no matching opening brace was found
* int key of $this->rawtokens a "link" to matching opening brace
- * 'iscat' : indicates brace that belongs to
self::$input_braces_types AND
+ * 'iscat' : indicates brace that belongs to
$this->input_braces_types AND
* has a proper match (both 'closed_at' and 'opened_at'
are int)
*/
var $brace_matches;
@@ -190,13 +190,13 @@
var $dbtokens = array();
# list of opening input braces types
- static $input_braces_types = array(
+ var $input_braces_types = array(
'<<' => 'text',
'<(' => 'radio',
'<[' => 'checkbox'
);
# matches of opening / closing braces
- static $matching_braces = array(
+ var $matching_braces = array(
# wiki link
'[[' => ']]',
# wiki magicword
@@ -210,23 +210,30 @@
);
/**
- * Constructor
- * @public
- * @param $poll an instance of question's parent controller
- * @param $view an instance of question view "linked" to
this question
- * @param $questionId the identifier of the question used to
generate input names
- */
- function __construct( qp_AbstractPoll $poll, qp_StubQuestionView $view,
$questionId ) {
- parent::__construct( $poll, $view, $questionId );
- if ( self::$propCatPattern === null ) {
+ * Applies previousely parsed attributes from main header into
question's view
+ * (all attributes but type)
+ *
+ * @param $attr_str - source text with question attributes
+ * @return string : type of the question, empty when not defined
+ */
+ function applyAttributes( $paramkeys ) {
+ parent::applyAttributes( $paramkeys );
+ if ( $this->mSubType === 'requireAllCategories' ) {
+ # radio button prevents from filling all categories,
disable it
+ if ( ( $radio_brace = array_search( 'radio',
$this->input_braces_types, true ) ) !== false ) {
+ unset( $this->input_braces_types[$radio_brace]
);
+ unset( $this->matching_braces[$radio_brace] );
+ }
+ }
+ if ( $this->propCatPattern === null ) {
$braces_list = array_map( 'preg_quote',
array_merge(
- ( array_values( self::$matching_braces
) ),
- array_keys( self::$matching_braces ),
+ ( array_values( $this->matching_braces
) ),
+ array_keys( $this->matching_braces ),
array( '|' )
)
);
- self::$propCatPattern = '/(' . implode( '|',
$braces_list ) . ')/u';
+ $this->propCatPattern = '/(' . implode( '|',
$braces_list ) . ')/u';
}
}
@@ -241,38 +248,54 @@
}
/**
- * Load text answer to the selected (proposal,category) pair, when
available
- * Also, stores text answer into the parsed tokens list (propview)
+ * Load checkbox / radio / text answer to the selected
(proposal,category) pair, when available
+ * Also, stores checkbox / radio / text answer into the parsed tokens
list (propview)
*/
function loadProposalCategory( qp_TextQuestionOptions $opt,
$proposalId, $catId ) {
$name = "q{$this->mQuestionId}p{$proposalId}s{$catId}";
- $text_answer = '';
- $user_answered = false;
+ # default value for unanswered category
+ # boolean true "checked" checkbox / radiobutton
+ # string - text input / select option value
+ $text_answer = false;
# try to load from POST data
- if ( $this->poll->mBeingCorrected && $this->mRequest->getVal(
$name ) !== null ) {
- $text_answer = trim( $this->mRequest->getText( $name )
);
+ if ( $this->poll->mBeingCorrected && $this->mRequest->getVal(
$name ) !== null ) {
+ if ( $opt->type === 'text' ) {
+ if ( ( $ta = trim( $this->mRequest->getText(
$name ) ) ) != '' ) {
+ if ( strlen( $ta ) >
qp_Setup::MAX_TEXT_ANSWER_LENGTH ) {
+ $text_answer = substr( $ta, 0,
qp_Setup::MAX_TEXT_ANSWER_LENGTH );
+ } else {
+ $text_answer = $ta;
+ }
+ }
+ } else {
+ $text_answer = true;
+ }
}
- if ( strlen( $text_answer ) > qp_Setup::MAX_TEXT_ANSWER_LENGTH
) {
- $text_answer = substr( $text_answer, 0,
qp_Setup::MAX_TEXT_ANSWER_LENGTH );
- }
- if ( $text_answer != '' ) {
- $user_answered = true;
- }
# try to load from pollStore
# pollStore optionally overrides POST data
- $prev_text_answer = $this->answerExists( 'text', $proposalId,
$catId );
- if ( $prev_text_answer !== false ) {
- $user_answered = true;
+ $prev_text_answer = $this->answerExists( $opt->type,
$proposalId, $catId );
+ if ( is_string( $prev_text_answer ) ) {
$text_answer = $prev_text_answer;
+ } else {
+ if ( $prev_text_answer === true ) {
+ $text_answer = true;
+ }
}
- if ( $user_answered ) {
+ if ( $text_answer !== false ) {
# add category to the list of user answers for current
proposal (row)
$this->mProposalCategoryId[ $proposalId ][] = $catId;
- $this->mProposalCategoryText[ $proposalId ][] =
$text_answer;
+ if ( is_string( $text_answer ) ) {
+ $this->mProposalCategoryText[ $proposalId ][] =
$text_answer;
+ } else {
+ $this->mProposalCategoryText[ $proposalId ][] =
'';
+ if ( $opt->type !== 'text' ) {
+ $opt->attributes['checked'] = true;
+ }
+ }
}
# finally, add new category input options for the view
$opt->closeCategory();
- $this->propview->addCatDef( $opt, $name, $text_answer,
$this->poll->mBeingCorrected && !$user_answered );
+ $this->propview->addCatDef( $opt, $name, $text_answer,
$this->poll->mBeingCorrected && $text_answer === false );
}
/**
@@ -285,30 +308,30 @@
$matching_closed_brace = '';
# building $this->brace_matches
foreach ( $this->rawtokens as $tkey => $token ) {
- if ( array_key_exists( $token, self::$matching_braces )
) {
+ if ( array_key_exists( $token, $this->matching_braces )
) {
# opening braces
$this->brace_matches[$tkey] = array(
'closed_at' => false,
'type' => $token
);
- $match = self::$matching_braces[$token];
+ $match = $this->matching_braces[$token];
# create new brace_stack element:
$last_brace_def = array(
'match' => $match,
'idx' => $tkey
);
- if ( array_key_exists( $token,
self::$input_braces_types ) &&
+ if ( array_key_exists( $token,
$this->input_braces_types ) &&
count( $brace_stack ) == 0 ) {
# will try to start category definiton
(on closing)
$matching_closed_brace = $match;
}
array_push( $brace_stack, $last_brace_def );
- } elseif ( in_array( $token, self::$matching_braces ) )
{
+ } elseif ( in_array( $token, $this->matching_braces ) )
{
# closing braces
$this->brace_matches[$tkey] = array(
'opened_at' => false,
# we always put opening brace in 'type'
- 'type' => array_search( $token,
self::$matching_braces, true )
+ 'type' => array_search( $token,
$this->matching_braces, true )
);
if ( count( $brace_stack ) > 0 ) {
$last_brace_def = array_pop(
$brace_stack );
@@ -322,7 +345,7 @@
$this->brace_matches[$tkey]['opened_at'] = $idx;
$this->brace_matches[$idx]['closed_at']
= $tkey;
if ( count( $brace_stack ) > 0 ||
$token !== $matching_closed_brace ) {
- # brace does not belong to
self::$input_braces_types
+ # brace does not belong to
$this->input_braces_types
continue;
}
# stack level 1 and found a
matching_closed_brace;
@@ -336,15 +359,15 @@
}
}
# trying to backtrack non-closed braces only these which belong
to
- # self::$input_braces_types
+ # $this->input_braces_types
$brace_keys = array_keys( $this->brace_matches, true );
for ( $i = count( $brace_keys ) - 1; $i >= 0; $i-- ) {
$brace_match = &$this->brace_matches[$brace_keys[$i]];
- # match non-closed brace which belongs to
self::$input_braces_types
+ # match non-closed brace which belongs to
$this->input_braces_types
# (non-closed category definitions)
if ( array_key_exists( 'opened_at', $brace_match ) &&
$brace_match['opened_at'] === false &&
- array_key_exists( $brace_match['type'],
self::$input_braces_types ) ) {
+ array_key_exists( $brace_match['type'],
$this->input_braces_types ) ) {
# try to find matching opening brace for
current non-closed closing brace
for ( $j = $i - 1; $j >= 0; $j-- ) {
$checked_brace =
&$this->brace_matches[$brace_keys[$j]];
@@ -398,7 +421,7 @@
$this->dbtokens = $brace_stack = array();
$catId = 0;
$last_brace = '';
- $this->rawtokens = preg_split( self::$propCatPattern,
$raw, -1, PREG_SPLIT_DELIM_CAPTURE );
+ $this->rawtokens = preg_split( $this->propCatPattern,
$raw, -1, PREG_SPLIT_DELIM_CAPTURE );
$matching_closed_brace = '';
$this->findMatchingBraces();
foreach ( $this->rawtokens as $tkey => $token ) {
@@ -421,11 +444,11 @@
if ( array_key_exists( 'closed_at',
$brace_match ) &&
$brace_match['closed_at'] !== false ) {
# valid opening brace
- array_push( $brace_stack,
self::$matching_braces[$token] );
+ array_push( $brace_stack,
$this->matching_braces[$token] );
if ( array_key_exists( 'iscat',
$brace_match ) ) {
# start category
definition
- $matching_closed_brace
= self::$matching_braces[$token];
- $opt->startOptionsList(
self::$input_braces_types[$token] );
+ $matching_closed_brace
= $this->matching_braces[$token];
+ $opt->startOptionsList(
$this->input_braces_types[$token] );
$toBeStored = false;
}
} elseif ( array_key_exists(
'opened_at', $brace_match ) &&
Modified: trunk/extensions/QPoll/includes/qp_renderer.php
===================================================================
--- trunk/extensions/QPoll/includes/qp_renderer.php 2011-10-20 10:29:51 UTC
(rev 100336)
+++ trunk/extensions/QPoll/includes/qp_renderer.php 2011-10-20 11:02:33 UTC
(rev 100337)
@@ -132,12 +132,19 @@
$tag['class'] = $className;
return;
}
- if ( array_search( $className, explode( ' ', $tag['class'] ) )
=== false ) {
+ if ( !self::hasClassName( $tag['class'], $className ) ) {
$tag['class'] .= " $className";
}
}
/**
+ * Finds whether the class name already exists in class attribute
+ */
+ static function hasClassName( $classAttr, $className ) {
+ return preg_match( '/(\s|^)' . preg_quote( $className ).
'(\s|$)/', $classAttr );
+ }
+
+ /**
* Creates one tagarray row of the table
* @param $row a string/number value of cell or
* an array( "count"=>colspannum, "attribute"=>value,
0=>html_inside_tag )
Modified: trunk/extensions/QPoll/view/question/qp_textquestionview.php
===================================================================
--- trunk/extensions/QPoll/view/question/qp_textquestionview.php
2011-10-20 10:29:51 UTC (rev 100336)
+++ trunk/extensions/QPoll/view/question/qp_textquestionview.php
2011-10-20 11:02:33 UTC (rev 100337)
@@ -177,23 +177,32 @@
}
/**
- * Generates tagarray representation from the list of viewtokens
+ * Generates tagarray representation from the list of viewtokens.
+ * @param $pkey proposal index (starting from 0):
+ * it is required for JS code, because text questions
+ * now may optionally have "tabular transposed" layout.
* @param $viewtokens array of viewtokens
* @return tagarray
*/
- function renderParsedProposal( &$viewtokens ) {
+ function renderParsedProposal( $pkey, &$viewtokens ) {
+ # proposal prefix for id generation
+ $id_prefix =
"tx{$this->ctrl->poll->mOrderId}q{$this->ctrl->mQuestionId}p{$pkey}";
$vr = $this->vr;
$vr->reset();
+ # category index, starting from 0
+ $ckey = 0;
foreach ( $viewtokens as $elem ) {
$vr->cell = array();
if ( is_object( $elem ) ) {
if ( isset( $elem->options ) ) {
$className = 'cat_part';
if ( $this->ctrl->mSubType ===
'requireAllCategories' && $elem->unanswered ) {
- $className = 'cat_noanswer';
+ $className .= ' cat_noanswer';
}
if ( isset( $elem->interpError ) ) {
- $className = 'cat_noanswer';
+ if (
!qp_Renderer::hasClassName( $className, 'cat_noanswer' ) ) {
+ $className .= '
cat_noanswer';
+ }
# create view for
proposal/category error message
$vr->cell[] = array(
'__tag' => 'span',
@@ -210,16 +219,19 @@
if ( !$elem->unanswered &&
$elem->value === '' && $elem->options[0] !== '' ) {
# input text pre-fill
$value =
$elem->options[0];
- $className =
'cat_prefilled';
+ $className .= '
cat_prefilled';
}
$input = array(
'__tag' => 'input',
+ # unique
(orderid,question,proposal,category) "coordinate" for javascript
+ 'id' =>
"{$id_prefix}c{$ckey}",
'class' => $className,
'type' => $elem->type,
'name' => $elem->name,
'value' =>
qp_Setup::specialchars( $value )
);
- if (
$elem->attributes['checked'] !== null ) {
+ $ckey++;
+ if ( $elem->type !== 'text' &&
$elem->attributes['checked'] === true ) {
$input['checked'] =
'checked';
}
if ( $this->textInputStyle !=
'' ) {
@@ -253,10 +265,13 @@
}
$vr->cell[] = array(
'__tag' => 'select',
+ # unique
(poll,question,proposal,category) "coordinate" for javascript
+ 'id' => "{$id_prefix}c{$ckey}",
'class' => $className,
'name' => $elem->name,
$html_options
);
+ $ckey++;
$vr->addCell();
} elseif ( isset( $elem->error ) ) {
# create view for proposal/category
error message
@@ -305,8 +320,8 @@
}
qp_Renderer::addRow( $questionTable, $row, $rowattrs,
'th', $attribute_maps );
}
- foreach ( $this->pviews as &$propview ) {
- $prop = $this->renderParsedProposal(
$propview->viewtokens );
+ foreach ( $this->pviews as $pkey => &$propview ) {
+ $prop = $this->renderParsedProposal( $pkey,
$propview->viewtokens );
$rowattrs = array( 'class' => $propview->rowClass );
if ( $this->transposed ) {
qp_Renderer::addColumn( $questionTable, $prop,
$rowattrs );
Modified: trunk/extensions/QPoll/view/results/qp_textquestiondataresults.php
===================================================================
--- trunk/extensions/QPoll/view/results/qp_textquestiondataresults.php
2011-10-20 10:29:51 UTC (rev 100336)
+++ trunk/extensions/QPoll/view/results/qp_textquestiondataresults.php
2011-10-20 11:02:33 UTC (rev 100337)
@@ -30,13 +30,25 @@
$row[] = array( '__tag' => 'span',
'class' => 'prop_part', qp_Setup::entities( $token ) );
} elseif ( is_array( $token ) ) {
# add a category definition with
selected text answer (if any)
+ $className = 'cat_part';
if ( array_key_exists( $propkey,
$ctrl->ProposalCategoryId ) &&
( $id_key = array_search(
$catId, $ctrl->ProposalCategoryId[$propkey] ) ) !== false ) {
- $text_answer =
$ctrl->ProposalCategoryText[$propkey][$id_key];
+ if ( ( $text_answer =
$ctrl->ProposalCategoryText[$propkey][$id_key] ) === '' &&
+ count(
$token ) === 1 ) {
+ # indicate selected
checkbox / radiobuttn
+ $text_answer = '+';
+ }
} else {
$text_answer = '';
+ $className .= ' cat_unanswered';
}
- $className = ( count( $token ) === 1 ||
in_array( $text_answer, $token ) ) ? 'cat_part' : 'cat_unknown';
+ if ( count( $token ) > 1 &&
+ $text_answer !== '' &&
+ !in_array(
$text_answer, $token ) ) {
+ if (
!qp_Renderer::hasClassName( $className, 'cat_unknown' ) ) {
+ $className .= '
cat_unknown';
+ };
+ }
$titleAttr = '';
foreach ( $token as &$option ) {
if ( $option !== $text_answer )
{
@@ -46,7 +58,14 @@
$titleAttr .=
qp_Setup::entities( $option );
}
}
- $row[] = array( '__tag' => 'span',
'class' => $className, 'title'=>$titleAttr, qp_Setup::entities( $text_answer )
);
+ if ( $text_answer === '' ) {
+ # many browsers trim the spaces
between spans when the text node is empty;
+ # use non-breaking space to
prevent this
+ $text_answer = ' ';
+ } else {
+ $text_answer =
qp_Setup::entities( $text_answer );
+ }
+ $row[] = array( '__tag' => 'span',
'class' => $className, 'title'=>$titleAttr, $text_answer );
# move to the next category (if any)
$catId++;
} else {
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs