inp.datepicker( "destroy" );
} );
+QUnit.test( "Ticket #15284: escaping text parameters", function( assert ) {
+ assert.expect( 7 );
+
+ var done = assert.async();
+
+ var qf = $( "#qunit-fixture" );
+
+ window.uiGlobalXss = [];
+
+ var inp = testHelper.init( "#inp", {
+ showButtonPanel: true,
+ showOn: "both",
+ prevText: "<script>uiGlobalXss = uiGlobalXss.concat( [ 'prevText XSS' ] )</script>",
+ nextText: "<script>uiGlobalXss = uiGlobalXss.concat( [ 'nextText XSS' ] )</script>",
+ currentText: "<script>uiGlobalXss = uiGlobalXss.concat( [ 'currentText XSS' ] )</script>",
+ closeText: "<script>uiGlobalXss = uiGlobalXss.concat( [ 'closeText XSS' ] )</script>",
+ buttonText: "<script>uiGlobalXss = uiGlobalXss.concat( [ 'buttonText XSS' ] )</script>",
+ appendText: "<script>uiGlobalXss = uiGlobalXss.concat( [ 'appendText XSS' ] )</script>"
+ } );
+
+ var dp = $( "#ui-datepicker-div" );
+
+ testHelper.onFocus( inp, function() {
+ assert.equal( dp.find( ".ui-datepicker-prev" ).text().trim(),
+ "<script>uiGlobalXss = uiGlobalXss.concat( [ 'prevText XSS' ] )</script>",
+ "prevText escaped" );
+ assert.equal( dp.find( ".ui-datepicker-next" ).text().trim(),
+ "<script>uiGlobalXss = uiGlobalXss.concat( [ 'nextText XSS' ] )</script>",
+ "nextText escaped" );
+ assert.equal( dp.find( ".ui-datepicker-current" ).text().trim(),
+ "<script>uiGlobalXss = uiGlobalXss.concat( [ 'currentText XSS' ] )</script>",
+ "currentText escaped" );
+ assert.equal( dp.find( ".ui-datepicker-close" ).text().trim(),
+ "<script>uiGlobalXss = uiGlobalXss.concat( [ 'closeText XSS' ] )</script>",
+ "closeText escaped" );
+
+ assert.equal( qf.find( ".ui-datepicker-trigger" ).text().trim(),
+ "<script>uiGlobalXss = uiGlobalXss.concat( [ 'buttonText XSS' ] )</script>",
+ "buttonText escaped" );
+ assert.equal( qf.find( ".ui-datepicker-append" ).text().trim(),
+ "<script>uiGlobalXss = uiGlobalXss.concat( [ 'appendText XSS' ] )</script>",
+ "appendText escaped" );
+
+ assert.deepEqual( window.uiGlobalXss, [], "No XSS" );
+
+ delete window.uiGlobalXss;
+ inp.datepicker( "hide" ).datepicker( "destroy" );
+ done();
+ } );
+} );
+
} );
inst.append.remove();
}
if ( appendText ) {
- inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + "</span>" );
+ inst.append = $( "<span>" )
+ .addClass( this._appendClass )
+ .text( appendText );
input[ isRTL ? "before" : "after" ]( inst.append );
}
if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked
buttonText = this._get( inst, "buttonText" );
buttonImage = this._get( inst, "buttonImage" );
- inst.trigger = $( this._get( inst, "buttonImageOnly" ) ?
- $( "<img/>" ).addClass( this._triggerClass ).
- attr( { src: buttonImage, alt: buttonText, title: buttonText } ) :
- $( "<button type='button'></button>" ).addClass( this._triggerClass ).
- html( !buttonImage ? buttonText : $( "<img/>" ).attr(
- { src:buttonImage, alt:buttonText, title:buttonText } ) ) );
+
+ if ( this._get( inst, "buttonImageOnly" ) ) {
+ inst.trigger = $( "<img>" )
+ .addClass( this._triggerClass )
+ .attr( {
+ src: buttonImage,
+ alt: buttonText,
+ title: buttonText
+ } );
+ } else {
+ inst.trigger = $( "<button type='button'>" )
+ .addClass( this._triggerClass );
+ if ( buttonImage ) {
+ inst.trigger.html(
+ $( "<img>" )
+ .attr( {
+ src: buttonImage,
+ alt: buttonText,
+ title: buttonText
+ } )
+ );
+ } else {
+ inst.trigger.text( buttonText );
+ }
+ }
+
input[ isRTL ? "before" : "after" ]( inst.trigger );
inst.trigger.on( "click", function() {
if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) {
this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
this._getFormatConfig( inst ) ) );
- prev = ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ?
- "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
- " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" :
- ( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" ) );
+ if ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ) {
+ prev = $( "<a>" )
+ .attr( {
+ "class": "ui-datepicker-prev ui-corner-all",
+ "data-handler": "prev",
+ "data-event": "click",
+ title: prevText
+ } )
+ .append(
+ $( "<span>" )
+ .addClass( "ui-icon ui-icon-circle-triangle-" +
+ ( isRTL ? "e" : "w" ) )
+ .text( prevText )
+ )[ 0 ].outerHTML;
+ } else if ( hideIfNoPrevNext ) {
+ prev = "";
+ } else {
+ prev = $( "<a>" )
+ .attr( {
+ "class": "ui-datepicker-prev ui-corner-all ui-state-disabled",
+ title: prevText
+ } )
+ .append(
+ $( "<span>" )
+ .addClass( "ui-icon ui-icon-circle-triangle-" +
+ ( isRTL ? "e" : "w" ) )
+ .text( prevText )
+ )[ 0 ].outerHTML;
+ }
nextText = this._get( inst, "nextText" );
nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
this._getFormatConfig( inst ) ) );
- next = ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ?
- "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
- " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" :
- ( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" ) );
+ if ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ) {
+ next = $( "<a>" )
+ .attr( {
+ "class": "ui-datepicker-next ui-corner-all",
+ "data-handler": "next",
+ "data-event": "click",
+ title: nextText
+ } )
+ .append(
+ $( "<span>" )
+ .addClass( "ui-icon ui-icon-circle-triangle-" +
+ ( isRTL ? "w" : "e" ) )
+ .text( nextText )
+ )[ 0 ].outerHTML;
+ } else if ( hideIfNoPrevNext ) {
+ next = "";
+ } else {
+ next = $( "<a>" )
+ .attr( {
+ "class": "ui-datepicker-next ui-corner-all ui-state-disabled",
+ title: nextText
+ } )
+ .append(
+ $( "<span>" )
+ .attr( "class", "ui-icon ui-icon-circle-triangle-" +
+ ( isRTL ? "w" : "e" ) )
+ .text( nextText )
+ )[ 0 ].outerHTML;
+ }
currentText = this._get( inst, "currentText" );
gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
currentText = ( !navigationAsDateFormat ? currentText :
this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );
- controls = ( !inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
- this._get( inst, "closeText" ) + "</button>" : "" );
-
- buttonPanel = ( showButtonPanel ) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + ( isRTL ? controls : "" ) +
- ( this._isInRange( inst, gotoDate ) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
- ">" + currentText + "</button>" : "" ) + ( isRTL ? "" : controls ) + "</div>" : "";
+ controls = "";
+ if ( !inst.inline ) {
+ controls = $( "<button>" )
+ .attr( {
+ type: "button",
+ "class": "ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all",
+ "data-handler": "hide",
+ "data-event": "click"
+ } )
+ .text( this._get( inst, "closeText" ) )[ 0 ].outerHTML;
+ }
+
+ buttonPanel = "";
+ if ( showButtonPanel ) {
+ buttonPanel = $( "<div class='ui-datepicker-buttonpane ui-widget-content'>" )
+ .append( isRTL ? controls : "" )
+ .append( this._isInRange( inst, gotoDate ) ?
+ $( "<button>" )
+ .attr( {
+ type: "button",
+ "class": "ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all",
+ "data-handler": "today",
+ "data-event": "click"
+ } )
+ .text( currentText ) :
+ "" )
+ .append( isRTL ? "" : controls )[ 0 ].outerHTML;
+ }
firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
firstDay = ( isNaN( firstDay ) ? 0 : firstDay );