diff --git a/js/widgets/forms/textinput.js b/js/widgets/forms/textinput.js index 40d100218a1..ffc08afecd3 100644 --- a/js/widgets/forms/textinput.js +++ b/js/widgets/forms/textinput.js @@ -5,7 +5,7 @@ //>>css.structure: ../css/structure/jquery.mobile.forms.textinput.css //>>css.theme: ../css/themes/default/jquery.mobile.theme.css -define( [ "jquery", "../../jquery.mobile.core", "../../jquery.mobile.widget", "../../jquery.mobile.degradeInputs", "../../jquery.mobile.buttonMarkup", "../../jquery.mobile.zoom" ], function( jQuery ) { +define( [ "jquery", "../../jquery.mobile.core", "../../jquery.mobile.widget", "../../jquery.mobile.degradeInputs", "../../jquery.mobile.buttonMarkup", "../../jquery.mobile.zoom", "../../jquery.mobile.registry" ], function( jQuery ) { //>>excludeEnd("jqmBuildExclude"); (function( $, undefined ) { @@ -15,28 +15,32 @@ $.widget( "mobile.textinput", $.mobile.widget, { mini: false, // This option defaults to true on iOS devices. preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1, - initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type]), input[type='file']", clearBtn: false, - clearSearchButtonText: null, //deprecating for 1.3... clearBtnText: "clear text", disabled: false }, _create: function() { + this._refresh(true); + }, + _refresh: function (create) { var self = this, input = this.element, o = this.options, - theme = o.theme || $.mobile.getInheritedTheme( this.element, "c" ), - themeclass = " ui-body-" + theme, - miniclass = o.mini ? " ui-mini" : "", - isSearch = input.is( "[type='search'], :jqmData(type='search')" ), - focusedEl, clearbtn, - clearBtnText = o.clearSearchButtonText || o.clearBtnText, - clearBtnBlacklist = input.is( "textarea, :jqmData(type='range')" ), - inputNeedsClearBtn = !!o.clearBtn && !clearBtnBlacklist, - inputNeedsWrap = input.is( "input" ) && !input.is( ":jqmData(type='range')" ); + clearBtnText, + focusedEl, + miniclass, + inputNeedsClearBtn, + theme, + themeclass, + isSearch = input.is("[type='search'], :jqmData(type='search')"), + clearBtnBlacklist = input.is("textarea, :jqmData(type='range')"), + inputNeedsWrap = input.is( "input" ) && !input.is( ":jqmData(type='range')" ), + extraLineHeight = 15, + keyupTimeoutBuffer = 100, + keyupTimeout; function toggleClear() { setTimeout( function() { @@ -44,50 +48,25 @@ $.widget( "mobile.textinput", $.mobile.widget, { }, 0 ); } - $( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" ); - - focusedEl = input.addClass( "ui-input-text ui-body-"+ theme ); - - // XXX: Temporary workaround for issue 785 (Apple bug 8910589). - // Turn off autocorrect and autocomplete on non-iOS 5 devices - // since the popup they use can't be dismissed by the user. Note - // that we test for the presence of the feature by looking for - // the autocorrect property on the input element. We currently - // have no test for iOS 5 or newer so we're temporarily using - // the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - jblas - if ( typeof input[0].autocorrect !== "undefined" && !$.support.touchOverflow ) { - // Set the attribute instead of the property just in case there - // is code that attempts to make modifications via HTML. - input[0].setAttribute( "autocorrect", "off" ); - input[0].setAttribute( "autocomplete", "off" ); - } - - //"search" and "text" input widgets - if ( isSearch ) { - focusedEl = input.wrap( "" ).parent(); - } else if ( inputNeedsWrap ) { - focusedEl = input.wrap( "
" ).parent(); - } - - if( inputNeedsClearBtn || isSearch ) { - clearbtn = $( "" + clearBtnText + "" ) - .bind( "click", function( event ) { + function buildClearBtn() { + clearbtn = $( "" + clearBtnText + "") + .bind( "click" , function( event ) { input - .val( "" ) - .focus() - .trigger( "change" ); + .val("") + .focus() + .trigger( "change" ); clearbtn.addClass( "ui-input-clear-hidden" ); event.preventDefault(); }) - .appendTo( focusedEl ) + .appendTo(focusedEl) .buttonMarkup({ - icon: "delete", - iconpos: "notext", - corners: true, - shadow: true, - mini: o.mini + icon: "delete", + iconpos: "notext", + corners: true, + shadow: true, + mini: o.mini }); - + if ( !isSearch ) { focusedEl.addClass( "ui-input-has-clear" ); } @@ -96,58 +75,168 @@ $.widget( "mobile.textinput", $.mobile.widget, { input.bind( "paste cut keyup input focus change blur", toggleClear ); } - else if ( !inputNeedsWrap && !isSearch ) { - input.addClass( "ui-corner-all ui-shadow-inset" + themeclass + miniclass ); - } - input.focus(function() { + if ( !create ) { + var oldTheme, dataMini, refreshMini = false; + + focusedEl = input; + oldTheme = o.theme || $.mobile.getInheritedTheme( input , "c"); + theme = input.attr( "data-" + $.mobile.ns + "theme" ) || ( o.theme || $.mobile.getInheritedTheme( input , "c" ) ); + o.clearBtn = input.attr( "data-" + $.mobile.ns + "clear-btn" ) === 'true'; + inputNeedsClearBtn = !!o.clearBtn && !clearBtnBlacklist; + + if ( isSearch || inputNeedsWrap) { + focusedEl = input.parent(); + focusedEl.removeClass( "ui-body-" + oldTheme ).addClass( "ui-body-" + theme ); + } + + input.removeClass( "ui-body-" + oldTheme ).addClass( "ui-body-" + theme ); + + o.theme = theme; + + //is data-mini changed? + dataMini = input.attr( "data-" + $.mobile.ns + "mini" ) === 'true'; + + if ( o.mini !== dataMini ) { + o.mini = dataMini; + refreshMini = true; + miniclass = o.mini ? "ui-mini" : ""; + focusedEl.removeClass( "ui-mini" ).addClass( miniclass ); + } + + //if textinput or search + if ( inputNeedsClearBtn || isSearch ) { + clearBtnText = input.attr( "data-" + $.mobile.ns + "clear-btn-text" ); + + //accept even an empty string + if ( typeof clearBtnText === "undefined") { + clearBtnText = o.clearBtnText; + } + + clearbtn = focusedEl.find( ".ui-input-clear" ); + + if ( !clearbtn.length ) { + buildClearBtn(); + } else { + clearbtn[0].title = clearBtnText; + clearbtn.find( ".ui-btn-text" ).text( clearBtnText ); + } + + clearbtn.removeClass( "ui-btn-up-" + oldTheme ).addClass( "ui-btn-up-" + theme ); + + if ( refreshMini ) { + if ( miniclass ) { + clearbtn.removeClass( "ui-fullsize" ).addClass( miniclass ); + } else { + clearbtn.removeClass( "ui-mini" ).addClass( "ui-fullsize" ); + } + } + + clearbtn.attr( "data-theme", theme ); + clearbtn.toggleClass( "ui-input-clear-hidden" , !input.val() ); + + } else { + if (focusedEl.length) { + focusedEl.find(".ui-input-clear").remove(); + } + } + + if ( input.attr("disabled") ) { + this.disable(); + } else { + this.enable(); + } + } else { + theme = o.theme || $.mobile.getInheritedTheme(this.element, "c"); + themeclass = " ui-body-" + theme; + miniclass = o.mini ? " ui-mini" : ""; + inputNeedsClearBtn = !!o.clearBtn && !clearBtnBlacklist; + clearBtnText = o.clearBtnText; + + $( "label[for='" + input.attr("id") + "']" ).addClass( "ui-input-text" ); + + focusedEl = input.addClass( "ui-input-text ui-body-" + theme ); + + // XXX: Temporary workaround for issue 785 (Apple bug 8910589). + // Turn off autocorrect and autocomplete on non-iOS 5 devices + // since the popup they use can't be dismissed by the user. Note + // that we test for the presence of the feature by looking for + // the autocorrect property on the input element. We currently + // have no test for iOS 5 or newer so we're temporarily using + // the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - jblas + if ( typeof input[0].autocorrect !== "undefined" && !$.support.touchOverflow ) { + // Set the attribute instead of the property just in case there + // is code that attempts to make modifications via HTML. + input[0].setAttribute( "autocorrect" , "off" ); + input[0].setAttribute( "autocomplete" , "off" ); + } + + //"search" and "text" input widgets + if ( isSearch ) { + focusedEl = input.wrap( "" ).parent(); + } else if (inputNeedsWrap) { + focusedEl = input.wrap( "
" ).parent(); + } + + if (inputNeedsClearBtn || isSearch) { + buildClearBtn(); + } else if (!inputNeedsWrap && !isSearch) { + input.addClass("ui-corner-all ui-shadow-inset" + themeclass + miniclass); + } + + input.focus(function() { // In many situations, iOS will zoom into the input upon tap, this prevents that from happening if ( o.preventFocusZoom ) { $.mobile.zoom.disable( true ); - } + } focusedEl.addClass( $.mobile.focusClass ); - }) - .blur(function() { + }).blur(function() { focusedEl.removeClass( $.mobile.focusClass ); if ( o.preventFocusZoom ) { $.mobile.zoom.enable( true ); - } - }) + } + } ); + + // Autogrow + if ( input.is( "textarea" ) ) { + + this._keyup = function() { + var scrollHeight = input[0].scrollHeight, + clientHeight = input[0].clientHeight; - // Autogrow - if ( input.is( "textarea" ) ) { - var extraLineHeight = 15, - keyupTimeoutBuffer = 100, - keyupTimeout; + if ( clientHeight < scrollHeight ) { + var paddingTop = parseFloat( input.css("padding-top") ), + paddingBottom = parseFloat( input.css("padding-bottom") ), + paddingHeight = paddingTop + paddingBottom; + + input.height( scrollHeight - paddingHeight + extraLineHeight ); + } + }; + + input.on( "keyup change input paste" , function() { + clearTimeout( keyupTimeout ); + keyupTimeout = setTimeout( self._keyup , keyupTimeoutBuffer ); + }); - this._keyup = function() { - var scrollHeight = input[ 0 ].scrollHeight, - clientHeight = input[ 0 ].clientHeight; + // binding to pagechange here ensures that for pages loaded via + // ajax the height is recalculated without user input + this._on( true , $.mobile.document , { "pagechange": "_keyup" }); - if ( clientHeight < scrollHeight ) { - input.height( scrollHeight + extraLineHeight ); + // Issue 509: the browser is not providing scrollHeight properly until the styles load + if ( $.trim( input.val() ) ) { + // bind to the window load to make sure the height is calculated based on BOTH + // the DOM and CSS + this._on( true , $.mobile.window , { "load": "_keyup" } ); } - }; - - input.on( "keyup change input paste", function() { - clearTimeout( keyupTimeout ); - keyupTimeout = setTimeout( self._keyup, keyupTimeoutBuffer ); - }); - - // binding to pagechange here ensures that for pages loaded via - // ajax the height is recalculated without user input - this._on( $.mobile.document, { "pagechange": "_keyup" }); - - // Issue 509: the browser is not providing scrollHeight properly until the styles load - if ( $.trim( input.val() ) ) { - // bind to the window load to make sure the height is calculated based on BOTH - // the DOM and CSS - this._on( $.mobile.window, {"load": "_keyup"}); + } + if ( input.attr( "disabled" ) ) { + this.disable(); } } - if ( input.attr( "disabled" ) ) { - this.disable(); - } + }, + + refresh: function() { + this._refresh( false ); }, disable: function() { @@ -155,7 +244,7 @@ $.widget( "mobile.textinput", $.mobile.widget, { isSearch = this.element.is( "[type='search'], :jqmData(type='search')" ), inputNeedsWrap = this.element.is( "input" ) && !this.element.is( ":jqmData(type='range')" ), parentNeedsDisabled = this.element.attr( "disabled", true ) && ( inputNeedsWrap || isSearch ); - + if ( parentNeedsDisabled ) { $el = this.element.parent(); } else { @@ -181,10 +270,10 @@ $.widget( "mobile.textinput", $.mobile.widget, { } }); +$.mobile.textinput.initSelector = "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type]), input[type='file']"; + //auto self-init widgets -$.mobile.document.bind( "pagecreate create", function( e ) { - $.mobile.textinput.prototype.enhanceWithin( e.target, true ); -}); +$.mobile._enhancer.add( "mobile.textinput" ); })( jQuery ); //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);