You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by pp...@apache.org on 2013/09/10 20:04:23 UTC
[18/51] [partial] [cordova-tizen] Tizen SDK 2.2 support mores samples
http://git-wip-us.apache.org/repos/asf/cordova-tizen/blob/4ebce38e/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/listview.js
----------------------------------------------------------------------
diff --git a/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/listview.js b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/listview.js
new file mode 100644
index 0000000..9a56d4b
--- /dev/null
+++ b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/listview.js
@@ -0,0 +1,550 @@
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: Applies listview styling of various types (standard, numbered, split button, etc.)
+//>>label: Listview
+//>>group: Widgets
+//>>css.structure: ../css/structure/jquery.mobile.listview.css
+//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
+
+define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.buttonMarkup", "./page", "./page.sections" ], function( jQuery ) {
+//>>excludeEnd("jqmBuildExclude");
+(function( $, undefined ) {
+
+//Keeps track of the number of lists per page UID
+//This allows support for multiple nested list in the same page
+//https://github.com/jquery/jquery-mobile/issues/1617
+var listCountPerPage = {};
+
+$.widget( "mobile.listview", $.mobile.widget, {
+
+ options: {
+ theme: null,
+ countTheme: "c",
+ headerTheme: "b",
+ dividerTheme: "b",
+ splitIcon: "arrow-r",
+ splitTheme: "b",
+ inset: false,
+ initSelector: ":jqmData(role='listview')"
+ },
+
+ _create: function() {
+ var t = this,
+ listviewClasses = "";
+
+ listviewClasses += t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "";
+
+ // create listview markup
+ t.element.addClass(function( i, orig ) {
+ return orig + " ui-listview " + listviewClasses;
+ });
+
+ t.refresh( true );
+ },
+
+ _removeCorners: function( li, which ) {
+ var top = "ui-corner-top ui-corner-tr ui-corner-tl",
+ bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
+
+ li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
+
+ if ( which === "top" ) {
+ li.removeClass( top );
+ } else if ( which === "bottom" ) {
+ li.removeClass( bot );
+ } else {
+ li.removeClass( top + " " + bot );
+ }
+ },
+
+ _refreshCorners: function( create ) {
+ var $li,
+ $visibleli,
+ $topli,
+ $bottomli;
+
+ $li = this.element.children( "li" );
+ // At create time and when autodividers calls refresh the li are not visible yet so we need to rely on .ui-screen-hidden
+ $visibleli = create || $li.filter( ":visible" ).length === 0 ? $li.not( ".ui-screen-hidden" ) : $li.filter( ":visible" );
+
+ // ui-li-last is used for setting border-bottom on the last li
+ $li.filter( ".ui-li-last" ).removeClass( "ui-li-last" );
+
+ if ( this.options.inset ) {
+ this._removeCorners( $li );
+
+ // Select the first visible li element
+ $topli = $visibleli.first()
+ .addClass( "ui-corner-top" );
+
+ $topli.add( $topli.find( ".ui-btn-inner" )
+ .not( ".ui-li-link-alt span:first-child" ) )
+ .addClass( "ui-corner-top" )
+ .end()
+ .find( ".ui-li-link-alt, .ui-li-link-alt span:first-child" )
+ .addClass( "ui-corner-tr" )
+ .end()
+ .find( ".ui-li-thumb" )
+ .not( ".ui-li-icon" )
+ .addClass( "ui-corner-tl" );
+
+ // Select the last visible li element
+ $bottomli = $visibleli.last()
+ .addClass( "ui-corner-bottom ui-li-last" );
+
+ $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
+ .find( ".ui-li-link-alt" )
+ .addClass( "ui-corner-br" )
+ .end()
+ .find( ".ui-li-thumb" )
+ .not( ".ui-li-icon" )
+ .addClass( "ui-corner-bl" );
+ } else {
+ $visibleli.last().addClass( "ui-li-last" );
+ }
+ if ( !create ) {
+ this.element.trigger( "updatelayout" );
+ }
+ },
+
+ // This is a generic utility method for finding the first
+ // node with a given nodeName. It uses basic DOM traversal
+ // to be fast and is meant to be a substitute for simple
+ // $.fn.closest() and $.fn.children() calls on a single
+ // element. Note that callers must pass both the lowerCase
+ // and upperCase version of the nodeName they are looking for.
+ // The main reason for this is that this function will be
+ // called many times and we want to avoid having to lowercase
+ // the nodeName from the element every time to ensure we have
+ // a match. Note that this function lives here for now, but may
+ // be moved into $.mobile if other components need a similar method.
+ _findFirstElementByTagName: function( ele, nextProp, lcName, ucName ) {
+ var dict = {};
+ dict[ lcName ] = dict[ ucName ] = true;
+ while ( ele ) {
+ if ( dict[ ele.nodeName ] ) {
+ return ele;
+ }
+ ele = ele[ nextProp ];
+ }
+ return null;
+ },
+ _getChildrenByTagName: function( ele, lcName, ucName ) {
+ var results = [],
+ dict = {};
+ dict[ lcName ] = dict[ ucName ] = true;
+ ele = ele.firstChild;
+ while ( ele ) {
+ if ( dict[ ele.nodeName ] ) {
+ results.push( ele );
+ }
+ ele = ele.nextSibling;
+ }
+ return $( results );
+ },
+
+ _addThumbClasses: function( containers ) {
+ var i, img, len = containers.length;
+ for ( i = 0; i < len; i++ ) {
+ img = $( this._findFirstElementByTagName( containers[ i ].firstChild, "nextSibling", "img", "IMG" ) );
+ if ( img.length ) {
+ img.addClass( "ui-li-thumb" ).attr( {
+ "role" : "",
+ "aria-label" : "icon"
+ });
+ $( this._findFirstElementByTagName( img[ 0 ].parentNode, "parentNode", "li", "LI" ) ).addClass( img.is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
+ }
+ }
+ },
+
+ _addCheckboxRadioClasses: function( containers )
+ {
+ var i, inputAttr, len = containers.length;
+ for ( i = 0; i < len; i++ ) {
+ inputAttr = $( containers[ i ] ).find( "input" );
+ if ( inputAttr.attr( "type" ) == "checkbox" ) {
+ $( containers[ i ] ).addClass( "ui-li-has-checkbox" );
+ } else if ( inputAttr.attr( "type" ) == "radio" ) {
+ $( containers[ i ] ).addClass( "ui-li-has-radio" );
+ }
+ }
+ },
+
+ _addRightBtnClasses: function( containers )
+ {
+ var i, btnAttr, len = containers.length;
+ for ( i = 0; i < len; i++ ) {
+ btnAttr = $( containers[ i ] ).find( ":jqmData(role='button'),input[type='button'],select:jqmData(role='slider')" );
+ if ( btnAttr.length ) {
+ if ( btnAttr.jqmData( "style" ) == "circle" ) {
+ $( containers[ i ] ).addClass( "ui-li-has-right-circle-btn" );
+ } else {
+ $( containers[ i ] ).addClass( "ui-li-has-right-btn" );
+ }
+ }
+ }
+ },
+
+ refresh: function( create ) {
+ this.parentPage = this.element.closest( ".ui-page" );
+ this._createSubPages();
+
+ var o = this.options,
+ $list = this.element,
+ self = this,
+ dividertheme = $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "dividertheme" ) || o.dividerTheme,
+ listsplittheme = $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "splittheme" ),
+ listspliticon = $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "spliticon" ),
+ li = this._getChildrenByTagName( $list[ 0 ], "li", "LI" ),
+ ol = !!$.nodeName( $list[ 0 ], "ol" ),
+ jsCount = !$.support.cssPseudoElement,
+ start = $list.attr( "start" ),
+ itemClassDict = {},
+ item, itemClass, itemTheme,
+ a, last, splittheme, counter, startCount, newStartCount, countParent, icon, imgParents, img, linkIcon;
+
+ if ( ol && jsCount ) {
+ $list.find( ".ui-li-dec" ).remove();
+ }
+
+ if ( ol ) {
+ // Check if a start attribute has been set while taking a value of 0 into account
+ if ( start || start === 0 ) {
+ if ( !jsCount ) {
+ startCount = parseFloat( start ) - 1;
+ $list.css( "counter-reset", "listnumbering " + startCount );
+ } else {
+ counter = parseFloat( start );
+ }
+ } else if ( jsCount ) {
+ counter = 1;
+ }
+ }
+
+ if ( !o.theme ) {
+ o.theme = $.mobile.getInheritedTheme( this.element, "c" );
+ }
+
+ for ( var pos = 0, numli = li.length; pos < numli; pos++ ) {
+ item = li.eq( pos );
+ itemClass = "ui-li";
+
+ // If we're creating the element, we update it regardless
+ if ( create || !item.hasClass( "ui-li" ) ) {
+ itemTheme = $.mobile.getAttrFixed( item[0], "data-" + $.mobile.ns + "theme" ) || o.theme;
+ a = this._getChildrenByTagName( item[ 0 ], "a", "A" ).attr( {
+ "role": "",
+ "tabindex": "0"
+ });
+ var isDivider = ( $.mobile.getAttrFixed( item[0], "data-" + $.mobile.ns + "role" ) === "list-divider" );
+
+ if ( item.hasClass( "ui-li-has-checkbox" ) || item.hasClass( "ui-li-has-radio" ) ) {
+ item.on( "vclick", function ( e ) {
+ var targetItem = $( e.target );
+ var checkboxradio = targetItem.find( ".ui-checkbox" );
+ if ( !checkboxradio.length ) {
+ checkboxradio = targetItem.find( ".ui-radio" );
+ }
+
+ if ( checkboxradio.length ) {
+ checkboxradio.children( "label" ).trigger( "vclick" );
+ }
+ });
+ }
+
+ if ( a.length && !isDivider ) {
+ icon = $.mobile.getAttrFixed( item[0], "data-" + $.mobile.ns + "icon" );
+
+ /* Remove auto populated right-arrow button. */
+ if ( icon === undefined ) {
+ icon = false;
+ }
+
+ item.buttonMarkup({
+ wrapperEls: "div",
+ shadow: false,
+ corners: false,
+ iconpos: "right",
+ icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
+ theme: itemTheme
+ });
+
+ if ( ( icon !== false ) && ( a.length === 1 ) ) {
+ item.addClass( "ui-li-has-arrow" );
+ }
+
+ a.first().removeClass( "ui-link" ).addClass( "ui-link-inherit" );
+
+ if ( a.length > 1 ) {
+ itemClass += " ui-li-has-alt";
+
+ last = a.last();
+ splittheme = listsplittheme || $.mobile.getAttrFixed( last[0], "data-" + $.mobile.ns + "theme" ) || o.splitTheme;
+ linkIcon = $.mobile.getAttrFixed( last[0], "data-" + $.mobile.ns + "icon" );
+
+ last.appendTo( item )
+ .attr( "title", last.getEncodedText() )
+ .addClass( "ui-li-link-alt" )
+ .empty()
+ .buttonMarkup({
+ shadow: false,
+ corners: false,
+ theme: itemTheme,
+ icon: false,
+ iconpos: "notext"
+ })
+ .find( ".ui-btn-inner" )
+ .append(
+ $( document.createElement( "span" ) ).buttonMarkup({
+ shadow: true,
+ corners: true,
+ theme: splittheme,
+ iconpos: "notext",
+ // link icon overrides list item icon overrides ul element overrides options
+ icon: linkIcon || icon || listspliticon || o.splitIcon
+ })
+ );
+ }
+ } else if ( isDivider ) {
+
+ itemClass += " ui-li-divider ui-bar-" + dividertheme;
+ item.attr( { "role": "heading", "tabindex": "0" } );
+
+ if ( ol ) {
+ //reset counter when a divider heading is encountered
+ if ( start || start === 0 ) {
+ if ( !jsCount ) {
+ newStartCount = parseFloat( start ) - 1;
+ item.css( "counter-reset", "listnumbering " + newStartCount );
+ } else {
+ counter = parseFloat( start );
+ }
+ } else if ( jsCount ) {
+ counter = 1;
+ }
+ }
+
+ } else {
+ itemClass += " ui-li-static ui-btn-up-" + itemTheme;
+ item.attr( "tabindex", "0" );
+ }
+ }
+
+ if ( ol && jsCount && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
+ countParent = itemClass.indexOf( "ui-li-static" ) > 0 ? item : item.find( ".ui-link-inherit" );
+
+ countParent.addClass( "ui-li-jsnumbering" )
+ .prepend( "<span class='ui-li-dec'>" + ( counter++ ) + ". </span>" );
+ }
+
+ // Instead of setting item class directly on the list item and its
+ // btn-inner at this point in time, push the item into a dictionary
+ // that tells us what class to set on it so we can do this after this
+ // processing loop is finished.
+
+ if ( !itemClassDict[ itemClass ] ) {
+ itemClassDict[ itemClass ] = [];
+ }
+
+ itemClassDict[ itemClass ].push( item[ 0 ] );
+ }
+
+ // Set the appropriate listview item classes on each list item
+ // and their btn-inner elements. The main reason we didn't do this
+ // in the for-loop above is because we can eliminate per-item function overhead
+ // by calling addClass() and children() once or twice afterwards. This
+ // can give us a significant boost on platforms like WP7.5.
+
+ for ( itemClass in itemClassDict ) {
+ $( itemClassDict[ itemClass ] ).addClass( itemClass ).children( ".ui-btn-inner" ).addClass( itemClass );
+ }
+
+ $list.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" )
+ .end()
+
+ .find( "p, dl" ).addClass( "ui-li-desc" )
+ .end()
+
+ .find( ".ui-li-aside" ).each(function() {
+ var $this = $( this );
+ $this.prependTo( $this.parent() ); //shift aside to front for css float
+ })
+ .end()
+
+ .find( ".ui-li-count" ).each(function() {
+ $( this ).closest( "li" ).addClass( "ui-li-has-count" );
+ }).addClass( "ui-btn-up-" + ( $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "counttheme" ) || this.options.countTheme) + " ui-btn-corner-all" );
+
+ // The idea here is to look at the first image in the list item
+ // itself, and any .ui-link-inherit element it may contain, so we
+ // can place the appropriate classes on the image and list item.
+ // Note that we used to use something like:
+ //
+ // li.find(">img:eq(0), .ui-link-inherit>img:eq(0)").each( ... );
+ //
+ // But executing a find() like that on Windows Phone 7.5 took a
+ // really long time. Walking things manually with the code below
+ // allows the 400 listview item page to load in about 3 seconds as
+ // opposed to 30 seconds.
+
+ this._addThumbClasses( li );
+ this._addThumbClasses( $list.find( ".ui-link-inherit" ) );
+
+ this._addCheckboxRadioClasses( li );
+ this._addCheckboxRadioClasses( $list.find( ".ui-link-inherit" ) );
+
+ this._addRightBtnClasses( li );
+ this._addRightBtnClasses( $list.find( ".ui-link-inherit" ) );
+
+ this._refreshCorners( create );
+
+ // autodividers binds to this to redraw dividers after the listview refresh
+ this._trigger( "afterrefresh" );
+ },
+
+ //create a string for ID/subpage url creation
+ _idStringEscape: function( str ) {
+ return str.replace(/[^a-zA-Z0-9]/g, '-');
+ },
+
+ _createSubPages: function() {
+ var parentList = this.element,
+ parentPage = parentList.closest( ".ui-page" ),
+ parentUrl = parentPage.jqmData( "url" ),
+ parentId = parentUrl || parentPage[ 0 ][ $.expando ],
+ parentListId = parentList.attr( "id" ),
+ o = this.options,
+ dns = "data-" + $.mobile.ns,
+ self = this,
+ persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
+ hasSubPages;
+
+ if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
+ listCountPerPage[ parentId ] = -1;
+ }
+
+ parentListId = parentListId || ++listCountPerPage[ parentId ];
+
+ $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
+ var self = this,
+ list = $( this ),
+ listId = list.attr( "id" ) || parentListId + "-" + i,
+ parent = list.parent(),
+ nodeElsFull = $( list.prevAll().toArray().reverse() ),
+ nodeEls = nodeElsFull.length ? nodeElsFull : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" ),
+ title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
+ id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
+ theme = $.mobile.getAttrFixed( list[0], "data-" + $.mobile.ns + "theme" ) || o.theme,
+ countTheme = $.mobile.getAttrFixed( list[0], "data-" + $.mobile.ns + "counttheme" ) || $.mobile.getAttrFixed( parentList[0], "data-" + $.mobile.ns + "counttheme" ) || o.countTheme,
+ newPage, anchor;
+
+ //define hasSubPages for use in later removal
+ hasSubPages = true;
+
+ newPage = list.detach()
+ .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
+ .parent()
+ .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
+ .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='"+ persistentFooterID +"'>" ) : "" )
+ .parent()
+ .appendTo( $.mobile.pageContainer );
+
+ newPage.page();
+
+ anchor = parent.find( 'a:first' );
+
+ if ( !anchor.length ) {
+ anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
+ }
+
+ anchor.attr( "href", "#" + id );
+
+ }).listview();
+
+ // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
+ // and aren't embedded
+ if ( hasSubPages &&
+ parentPage.is( ":jqmData(external-page='true')" ) &&
+ parentPage.data( "page" ).options.domCache === false ) {
+
+ var newRemove = function( e, ui ) {
+ var nextPage = ui.nextPage, npURL,
+ prEvent = new $.Event( "pageremove" );
+
+ if ( ui.nextPage ) {
+ npURL = nextPage.jqmData( "url" );
+ if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
+ self.childPages().remove();
+ parentPage.trigger( prEvent );
+ if ( !prEvent.isDefaultPrevented() ) {
+ parentPage.removeWithDependents();
+ }
+ }
+ }
+ };
+
+ // unbind the original page remove and replace with our specialized version
+ parentPage
+ .unbind( "pagehide.remove" )
+ .bind( "pagehide.remove", newRemove);
+ }
+ },
+
+ addItem : function( listitem , idx ) {
+ var $item = $(listitem),
+ $li,
+ _self = this;
+
+ $li = _self.element.children( 'li' );
+ $item.css( { 'opacity' : 0,
+ 'display' : 'none' } );
+ if( $li.length == 0
+ || $li.length <= idx)
+ {
+ $( _self.element ).append( $item );
+ } else {
+ $( $li.get( idx ) ).before( $item );
+ }
+ $(_self.element).trigger("create")
+ .listview( 'refresh' );
+
+ $item.css( 'min-height' , '0px' );
+
+ $item.slideDown( 'fast' , function( ){
+ $item.addClass("addli");
+ $item.css( { 'opacity' : 1 } );
+ } );
+ },
+
+ removeItem : function( idx ) {
+ var $item,
+ $li,
+ _self = this;
+
+ $li = _self.element.children( 'li' );
+ if( $li.length <= 0 ||
+ $li.length < idx ) {
+ return ;
+ }
+ $item = $( $li.get( idx ) );
+ $item.addClass("removeli");
+ $item.slideUp('normal',
+ function( ) {
+ $(this).remove();
+ });
+ },
+
+ // TODO sort out a better way to track sub pages of the listview this is brittle
+ childPages: function() {
+ var parentUrl = this.parentPage.jqmData( "url" );
+
+ return $( ":jqmData(url^='"+ parentUrl + "&" + $.mobile.subPageUrlKey + "')" );
+ }
+});
+
+//delegate auto self-init widgets
+$.delegateSelfInitWithSingleSelector( $.mobile.listview );
+
+})( jQuery );
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+});
+//>>excludeEnd("jqmBuildExclude");
http://git-wip-us.apache.org/repos/asf/cordova-tizen/blob/4ebce38e/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/loader.js
----------------------------------------------------------------------
diff --git a/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/loader.js b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/loader.js
new file mode 100644
index 0000000..ba5c190
--- /dev/null
+++ b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/loader.js
@@ -0,0 +1,185 @@
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: packaged loading message functionality
+//>>label: loading message
+//>>group: Navigation
+
+define( [ "jquery", "../jquery.mobile.core", "../jquery.mobile.widget" ], function( jQuery ) {
+//>>excludeEnd("jqmBuildExclude");
+
+(function( $, window ) {
+ // DEPRECATED
+ // NOTE global mobile object settings
+ $.extend( $.mobile, {
+ // DEPRECATED Should the text be visble in the loading message?
+ loadingMessageTextVisible: undefined,
+
+ // DEPRECATED When the text is visible, what theme does the loading box use?
+ loadingMessageTheme: undefined,
+
+ // DEPRECATED default message setting
+ loadingMessage: undefined,
+
+ // DEPRECATED
+ // Turn on/off page loading message. Theme doubles as an object argument
+ // with the following shape: { theme: '', text: '', html: '', textVisible: '' }
+ // NOTE that the $.mobile.loading* settings and params past the first are deprecated
+ showPageLoadingMsg: function( theme, msgText, textonly ) {
+ $.mobile.loading( 'show', theme, msgText, textonly );
+ },
+
+ // DEPRECATED
+ hidePageLoadingMsg: function() {
+ $.mobile.loading( 'hide' );
+ },
+
+ loading: function() {
+ this.loaderWidget.loader.apply( this.loaderWidget, arguments );
+ }
+ });
+
+ // TODO move loader class down into the widget settings
+ var loaderClass = "ui-loader", $html = $( "html" ), $window = $.mobile.$window;
+
+ $.widget( "mobile.loader", {
+ // NOTE if the global config settings are defined they will override these
+ // options
+ options: {
+ // the theme for the loading message
+ theme: "a",
+
+ // whether the text in the loading message is shown
+ textVisible: false,
+
+ // custom html for the inner content of the loading message
+ html: "",
+
+ // the text to be displayed when the popup is shown
+ text: "loading"
+ },
+
+ defaultHtml: "<div class='" + loaderClass + "'>" +
+ "<span class='ui-icon ui-icon-loading'></span>" +
+ "<h1></h1>" +
+ "</div>",
+
+ // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
+ fakeFixLoader: function() {
+ var activeBtn = $( "." + $.mobile.activeBtnClass ).first();
+
+ this.element
+ .css({
+ top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
+ activeBtn.length && activeBtn.offset().top || 100
+ });
+ },
+
+ // check position of loader to see if it appears to be "fixed" to center
+ // if not, use abs positioning
+ checkLoaderPosition: function() {
+ var offset = this.element.offset(),
+ scrollTop = $window.scrollTop(),
+ screenHeight = $.mobile.getScreenHeight();
+
+ if ( offset.top < scrollTop || ( offset.top - scrollTop ) > screenHeight ) {
+ this.element.addClass( "ui-loader-fakefix" );
+ this.fakeFixLoader();
+ $window
+ .unbind( "scroll", this.checkLoaderPosition )
+ .bind( "scroll", this.fakeFixLoader );
+ }
+ },
+
+ resetHtml: function() {
+ this.element.html( $( this.defaultHtml ).html() );
+ },
+
+ // Turn on/off page loading message. Theme doubles as an object argument
+ // with the following shape: { theme: '', text: '', html: '', textVisible: '' }
+ // NOTE that the $.mobile.loading* settings and params past the first are deprecated
+ // TODO sweet jesus we need to break some of this out
+ show: function( theme, msgText, textonly ) {
+ var textVisible, message, $header, loadSettings;
+
+ this.resetHtml();
+
+ // use the prototype options so that people can set them globally at
+ // mobile init. Consistency, it's what's for dinner
+ if ( $.type(theme) === "object" ) {
+ loadSettings = $.extend( {}, this.options, theme );
+
+ // prefer object property from the param then the old theme setting
+ theme = loadSettings.theme || $.mobile.loadingMessageTheme;
+ } else {
+ loadSettings = this.options;
+
+ // here we prefer the them value passed as a string argument, then
+ // we prefer the global option because we can't use undefined default
+ // prototype options, then the prototype option
+ theme = theme || $.mobile.loadingMessageTheme || loadSettings.theme;
+ }
+
+ // set the message text, prefer the param, then the settings object
+ // then loading message
+ message = msgText || $.mobile.loadingMessage || loadSettings.text;
+
+ // prepare the dom
+ $html.addClass( "ui-loading" );
+
+ if ( $.mobile.loadingMessage !== false || loadSettings.html ) {
+ // boolean values require a bit more work :P, supports object properties
+ // and old settings
+ if ( $.mobile.loadingMessageTextVisible !== undefined ) {
+ textVisible = $.mobile.loadingMessageTextVisible;
+ } else {
+ textVisible = loadSettings.textVisible;
+ }
+
+ // add the proper css given the options (theme, text, etc)
+ // Force text visibility if the second argument was supplied, or
+ // if the text was explicitly set in the object args
+ this.element.attr("class", loaderClass +
+ " ui-corner-all ui-body-" + theme +
+ " ui-loader-" + ( textVisible || msgText || theme.text ? "verbose" : "default" ) +
+ ( loadSettings.textonly || textonly ? " ui-loader-textonly" : "" ) );
+
+ // TODO verify that jquery.fn.html is ok to use in both cases here
+ // this might be overly defensive in preventing unknowing xss
+ // if the html attribute is defined on the loading settings, use that
+ // otherwise use the fallbacks from above
+ if ( loadSettings.html ) {
+ this.element.html( loadSettings.html );
+ } else {
+ this.element.find( "h1" ).text( message );
+ }
+
+ // attach the loader to the DOM
+ this.element.appendTo( $.mobile.pageContainer );
+
+ // check that the loader is visible
+ this.checkLoaderPosition();
+
+ // on scroll check the loader position
+ $window.bind( "scroll", $.proxy( this.checkLoaderPosition, this ) );
+ }
+ },
+
+ hide: function() {
+ $html.removeClass( "ui-loading" );
+
+ if ( $.mobile.loadingMessage ) {
+ this.element.removeClass( "ui-loader-fakefix" );
+ }
+
+ $.mobile.$window.unbind( "scroll", $.proxy( this.fakeFixLoader, this) );
+ $.mobile.$window.unbind( "scroll", $.proxy( this.checkLoaderPosition, this ) );
+ }
+ });
+
+ $window.bind( 'pagecontainercreate', function() {
+ $.mobile.loaderWidget = $.mobile.loaderWidget || $( $.mobile.loader.prototype.defaultHtml ).loader();
+ });
+})(jQuery, this);
+
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+});
+//>>excludeEnd("jqmBuildExclude");
http://git-wip-us.apache.org/repos/asf/cordova-tizen/blob/4ebce38e/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/navbar.js
----------------------------------------------------------------------
diff --git a/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/navbar.js b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/navbar.js
new file mode 100644
index 0000000..f4842f8
--- /dev/null
+++ b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/navbar.js
@@ -0,0 +1,60 @@
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: Formats groups of links as horizontal navigation bars.
+//>>label: Navbars
+//>>group: Widgets
+//>>css.structure: ../css/structure/jquery.mobile.navbar.css
+//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
+
+
+define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.buttonMarkup", "../jquery.mobile.grid" ], function( jQuery ) {
+//>>excludeEnd("jqmBuildExclude");
+(function( $, undefined ) {
+
+$.widget( "mobile.navbar", $.mobile.widget, {
+ options: {
+ iconpos: "top",
+ grid: null,
+ initSelector: ":jqmData(role='navbar')"
+ },
+
+ _create: function() {
+
+ var $navbar = this.element,
+ $navbtns = $navbar.find( "a" ),
+ iconpos = $navbtns.filter( ":jqmData(icon)" ).length ?
+ this.options.iconpos : undefined;
+
+ $navbar.addClass( "ui-navbar ui-mini" )
+ .attr( "role", "navigation" )
+ .find( "ul" )
+ .jqmEnhanceable()
+ .grid({ grid: this.options.grid });
+
+ $navbtns.buttonMarkup({
+ corners: false,
+ shadow: false,
+ inline: true,
+ iconpos: iconpos
+ });
+
+ $navbar.delegate( "a", "vclick", function( event ) {
+ if ( !$(event.target).hasClass( "ui-disabled" ) ) {
+ $navbtns.removeClass( $.mobile.activeBtnClass );
+ $( this ).addClass( $.mobile.activeBtnClass );
+ }
+ });
+
+ // Buttons in the navbar with ui-state-persist class should regain their active state before page show
+ $navbar.closest( ".ui-page" ).bind( "pagebeforeshow", function() {
+ $navbtns.filter( ".ui-state-persist" ).addClass( $.mobile.activeBtnClass );
+ });
+ }
+});
+
+//delegate auto self-init widgets
+$.delegateSelfInitWithSingleSelector( $.mobile.navbar );
+
+})( jQuery );
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+});
+//>>excludeEnd("jqmBuildExclude");
http://git-wip-us.apache.org/repos/asf/cordova-tizen/blob/4ebce38e/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.js
----------------------------------------------------------------------
diff --git a/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.js b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.js
new file mode 100644
index 0000000..cd7efa6
--- /dev/null
+++ b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.js
@@ -0,0 +1,84 @@
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: Basic page definition and formatting.
+//>>label: Page Creation
+//>>group: Core
+
+define( [ "jquery", "../jquery.mobile.widget" ], function( jQuery ) {
+//>>excludeEnd("jqmBuildExclude");
+(function( $, undefined ) {
+
+$.widget( "mobile.page", $.mobile.widget, {
+ options: {
+ theme: "c",
+ domCache: false,
+ keepNativeDefault: ":jqmData(role='none'), :jqmData(role='nojs')"
+ },
+
+ _create: function() {
+
+ var self = this;
+
+ // if false is returned by the callbacks do not create the page
+ if ( self._trigger( "beforecreate" ) === false ) {
+ return false;
+ }
+
+ self.element
+ .addClass( "ui-page ui-body-" + self.options.theme )
+ .bind( "pagebeforehide", function() {
+ self.removeContainerBackground();
+ } )
+ .bind( "pagebeforeshow", function() {
+ self.setContainerBackground();
+ } );
+
+ },
+
+ refresh: function() {
+ $( this.element ).children( ".ui-content" ).trigger("updatelayout", ["external"]);
+ },
+
+ /* GUI Builder only : redesign page when user drag&drop header, footer */
+ setToolbar: function () {
+ $( this.element ).trigger( "pagebeforeshow" );
+ },
+
+ removeContainerBackground: function() {
+ $.mobile.pageContainer.removeClass( "ui-overlay-" + $.mobile.getInheritedTheme( this.element.parent() ) );
+ },
+
+ // set the page container background to the page theme
+ setContainerBackground: function( theme ) {
+ if ( this.options.theme ) {
+ $.mobile.pageContainer.addClass( "ui-overlay-" + ( theme || this.options.theme ) );
+ }
+ },
+
+ addBackBtn : function ( target ) {
+ var $dest = $( ".ui-page-active .ui-footer" );
+
+ if ( target == "header" ) {
+ $dest = $( ".ui-page-active .ui-header" );
+ }
+ backBtn = $( "<a href='#' class='ui-btn-back' data-" + $.mobile.ns + "rel='back'></a>" )
+ .buttonMarkup( {icon: "header-back-btn", theme : "s"} );
+ if ( !$dest.find( ".ui-btn-back").length ) {
+ backBtn.prependTo( $dest );
+ }
+ },
+
+ keepNativeSelector: function() {
+ var options = this.options,
+ keepNativeDefined = options.keepNative && $.trim( options.keepNative );
+
+ if ( keepNativeDefined && options.keepNative !== options.keepNativeDefault ) {
+ return [options.keepNative, options.keepNativeDefault].join( ", " );
+ }
+
+ return options.keepNativeDefault;
+ }
+});
+})( jQuery );
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+});
+//>>excludeEnd("jqmBuildExclude");
http://git-wip-us.apache.org/repos/asf/cordova-tizen/blob/4ebce38e/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.sections.js
----------------------------------------------------------------------
diff --git a/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.sections.js b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.sections.js
new file mode 100644
index 0000000..352355e
--- /dev/null
+++ b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.sections.js
@@ -0,0 +1,119 @@
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: Theming and layout of headers, footers, and content areas
+//>>label: Page Sections
+//>>group: Core
+
+define( [ "jquery", "../jquery.mobile.core", "./page" ], function( jQuery ) {
+//>>excludeEnd("jqmBuildExclude");
+(function( $, undefined ) {
+
+$.mobile.page.prototype.options.backBtnText = "Back";
+$.mobile.page.prototype.options.addBackBtn = false;
+$.mobile.page.prototype.options.backBtnTheme = null;
+$.mobile.page.prototype.options.headerTheme = "a";
+$.mobile.page.prototype.options.footerTheme = "a";
+$.mobile.page.prototype.options.contentTheme = null;
+
+// NOTE bind used to force this binding to run before the buttonMarkup binding
+// which expects .ui-footer top be applied in its gigantic selector
+// TODO remove the buttonMarkup giant selector and move it to the various modules
+// on which it depends
+$.mobile.$document.bind( "pagecreate", function( e ) {
+ var $page = $( e.target ),
+ o = $page.data( "page" ).options,
+ prefix = "data-"+$.mobile.ns,
+ pageRole = $page[0].getAttribute( prefix + "role" ) || undefined,
+ pageTheme = o.theme;
+
+ $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", $page )
+ .jqmEnhanceable()
+ .each(function() {
+
+ var $this = $( this ),
+ role = $this[0].getAttribute( prefix + "role" ) || undefined,
+ theme = $this[0].getAttribute( prefix + "theme" ) || undefined,
+ contentTheme = theme || o.contentTheme || ( pageRole === "dialog" && pageTheme ),
+ $headeranchors,
+ leftbtn,
+ rightbtn,
+ $dest = $page.find( ".ui-footer" ),
+ backBtn;
+
+ $this.addClass( "ui-" + role );
+
+ //apply theming and markup modifications to page,header,content,footer
+ if ( role === "header" || role === "footer" ) {
+
+ var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
+
+ $this
+ //add theme class
+ .addClass( "ui-bar-" + thisTheme )
+ // Add ARIA role
+ .attr( "role", role === "header" ? "banner" : "contentinfo" );
+
+ if ( role === "header") {
+ // Right,left buttons
+ $headeranchors = $this.children( "a, div.naviframe-button, a.naviframe-button, button" );
+ leftbtn = $headeranchors.hasClass( "ui-btn-left" );
+ rightbtn = $headeranchors.hasClass( "ui-btn-right" );
+
+ leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
+
+ rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
+
+ $( $headeranchors.get().reverse() ).each( function ( i ) {
+ $( this ).addClass( "ui-btn-right-" + i );
+ });
+ }
+
+ // Auto-add back btn on pages beyond first view
+ if ( o.addBackBtn &&
+ ( role === "footer" || role === "header" ) &&
+ $page[0].getAttribute( prefix + "url" ) !== $.mobile.path.stripHash( location.hash ) &&
+ !leftbtn ) {
+
+ if ( o.addBackBtn == "header" ) {
+ $dest = $page.find( ".ui-header" );
+ } else {
+ $dest = $page.find( ".ui-footer" );
+ }
+
+ if ( !$dest.find( ".ui-btn-back" ).length ) {
+ backBtn = $( "<a href='javascript:void(0);' class='ui-btn-back' data-" + $.mobile.ns + "rel='back'></a>" )
+ // // If theme is provided, override default inheritance
+ .buttonMarkup( { icon: "header-back-btn", theme: o.backBtnTheme || thisTheme } );
+
+ backBtn.find( ".ui-btn-text" ).text( o.backBtnText );
+ backBtn.appendTo( $dest );
+ }
+ }
+
+ // Page title
+ $this.children( "h1, h2, h3, h4, h5, h6" )
+ .addClass( "ui-title" )
+ // Regardless of h element number in src, it becomes h1 for the enhanced page
+ .attr({
+ "role": "heading",
+ "aria-level": "1",
+ "aria-label": "title",
+ "tabindex": "0"
+ });
+
+ $( ".ui-title-text-sub" ).attr( { "tabindex": "0", "aria-label": "subtitle" } );
+
+ } else if ( role === "content" ) {
+ if ( contentTheme ) {
+ $this.addClass( "ui-body-" + ( contentTheme ) );
+ }
+
+ // Add ARIA role
+ $this.attr( "role", "main" );
+ }
+ });
+});
+
+})( jQuery );
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+});
+//>>excludeEnd("jqmBuildExclude");
http://git-wip-us.apache.org/repos/asf/cordova-tizen/blob/4ebce38e/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/popup.js
----------------------------------------------------------------------
diff --git a/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/popup.js b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/popup.js
new file mode 100644
index 0000000..136686c
--- /dev/null
+++ b/samples/TizenWebUI-sample-v1/tizen-web-ui-fw/latest/js/modules/jqm/widgets/popup.js
@@ -0,0 +1,977 @@
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: Popup windows
+//>>label: Popups
+//>>group: Widgets
+//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
+//>>css.structure: ../css/structure/jquery.mobile.popup.css,../css/structure/jquery.mobile.transition.css,../css/structure/jquery.mobile.transition.fade.css
+
+define( [ "jquery",
+ "../jquery.mobile.widget",
+ "../jquery.mobile.support",
+ "../jquery.mobile.navigation",
+ "../jquery.hashchange" ], function( jQuery ) {
+//>>excludeEnd("jqmBuildExclude");
+(function( $, undefined ) {
+
+ function fitSegmentInsideSegment( winSize, segSize, offset, desired ) {
+ var ret = desired;
+
+ if ( winSize < segSize ) {
+ // Center segment if it's bigger than the window
+ ret = offset + ( winSize - segSize ) / 2;
+ } else {
+ // Otherwise center it at the desired coordinate while keeping it completely inside the window
+ ret = Math.min( Math.max( offset, desired - segSize / 2 ), offset + winSize - segSize );
+ }
+
+ return ret;
+ }
+
+ function windowCoords() {
+ var $win = $.mobile.$window;
+
+ return {
+ x: $win.scrollLeft(),
+ y: $win.scrollTop(),
+ cx: ( window.innerWidth || $win.width() ),
+ cy: ( window.innerHeight || $win.height() )
+ };
+ }
+
+ $.widget( "mobile.popup", $.mobile.widget, {
+ options: {
+ theme: null,
+ overlayTheme: null,
+ shadow: true,
+ corners: true,
+ transition: "pop",
+ positionTo: "origin",
+ tolerance: null,
+ initSelector: ":jqmData(role='popup')",
+ closeLinkSelector: "a:jqmData(rel='back')",
+ closeLinkEvents: "click.popup",
+ navigateEvents: "navigate.popup",
+ closeEvents: "navigate.popup pagebeforechange.popup",
+ isHardwarePopup: false,
+ // NOTE Windows Phone 7 has a scroll position caching issue that
+ // requires us to disable popup history management by default
+ // https://github.com/jquery/jquery-mobile/issues/4784
+ //
+ // NOTE this option is modified in _create!
+ history: false
+ },
+
+ _eatEventAndClose: function( e ) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ this.close();
+ return false;
+ },
+
+ // Make sure the screen size is increased beyond the page height if the popup's causes the document to increase in height
+ _resizeScreen: function() {
+ var popupHeight = this._ui.container.outerHeight( true );
+
+ this._ui.screen.removeAttr( "style" );
+ if ( popupHeight > this._ui.screen.height() ) {
+ this._ui.screen.height( popupHeight );
+ }
+ },
+
+ _handleWindowKeyUp: function( e ) {
+ if ( this._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE ) {
+ return this._eatEventAndClose( e );
+ }
+ },
+
+ _maybeRefreshTimeout: function() {
+ var winCoords = windowCoords();
+
+ if ( this._resizeData ) {
+ if ( winCoords.x === this._resizeData.winCoords.x &&
+ winCoords.y === this._resizeData.winCoords.y &&
+ winCoords.cx === this._resizeData.winCoords.cx &&
+ winCoords.cy === this._resizeData.winCoords.cy ) {
+ // timeout not refreshed
+ return false;
+ } else {
+ // clear existing timeout - it will be refreshed below
+ clearTimeout( this._resizeData.timeoutId );
+ }
+ }
+
+ this._resizeData = {
+ timeoutId: setTimeout( $.proxy( this, "_resizeTimeout" ), 200 ),
+ winCoords: winCoords
+ };
+
+ return true;
+ },
+
+ _resizeTimeout: function() {
+ if ( !this._maybeRefreshTimeout() && this.positionTo === "window" && this._isOpen ) {
+ // effectively rapid-open the popup while leaving the screen intact
+ this._trigger( "beforeposition" );
+ this._ui.container
+ .removeClass( "ui-selectmenu-hidden" )
+ .offset( this._placementCoords( this._desiredCoords( undefined, undefined, "window" ) ) );
+
+ this._resizeScreen();
+ this._resizeData = null;
+ this._orientationchangeInProgress = false;
+ }
+ },
+
+ _handleWindowResize: function( e ) {
+ if ( this._isOpen ) {
+ // Context popup close when Window resize event
+ if( this.positionTo !== "window" ) {
+ this.close();
+ return false;
+ }
+ this._maybeRefreshTimeout();
+ }
+ },
+
+ _handleWindowOrientationchange: function( e ) {
+
+ if ( !this._orientationchangeInProgress ) {
+ // effectively rapid-close the popup while leaving the screen intact
+ this._ui.container
+ .addClass( "ui-selectmenu-hidden" )
+ .removeAttr( "style" );
+
+ this._orientationchangeInProgress = true;
+ }
+ },
+
+ _create: function() {
+ var ui = {
+ screen: $( "<div class='ui-screen-hidden ui-popup-screen'></div>" ),
+ placeholder: $( "<div style='display: none;'><!-- placeholder --></div>" ),
+ container: $( "<div class='ui-popup-container ui-selectmenu-hidden'></div>" ),
+ arrow : $("<div class='ui-arrow'></div>")
+ },
+ thisPage = this.element.closest( ".ui-page" ),
+ myId = this.element.attr( "id" ),
+ self = this;
+
+ // We need to adjust the history option to be false if there's no AJAX nav.
+ // We can't do it in the option declarations because those are run before
+ // it is determined whether there shall be AJAX nav.
+ this.options.history = this.options.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;
+
+ if ( thisPage.length === 0 ) {
+ thisPage = $( "body" );
+ }
+
+ // define the container for navigation event bindings
+ // TODO this would be nice at the the mobile widget level
+ this.options.container = this.options.container || $.mobile.pageContainer;
+
+ // Apply the proto
+ thisPage.append( ui.screen );
+ ui.container.insertAfter( ui.screen );
+ // Leave a placeholder where the element used to be
+ ui.placeholder.insertAfter( this.element );
+ if ( myId ) {
+ ui.screen.attr( "id", myId + "-screen" );
+ ui.container.attr( "id", myId + "-popup" );
+ ui.placeholder.html( "<!-- placeholder for " + myId + " -->" );
+ }
+ ui.container.append( this.element );
+ ui.container.append( ui.arrow );
+ // Add class to popup element
+ this.element.addClass( "ui-popup" );
+
+ // Define instance variables
+ $.extend( this, {
+ _page: thisPage,
+ _ui: ui,
+ _fallbackTransition: "",
+ _currentTransition: false,
+ _prereqs: null,
+ _isOpen: false,
+ _tolerance: null,
+ _resizeData: null,
+ _orientationchangeInProgress: false,
+ _globalHandlers: [
+ {
+ src: $.mobile.$window,
+ handler: {
+ orientationchange: $.proxy( this, "_handleWindowOrientationchange" ),
+ resize: $.proxy( this, "_handleWindowResize" ),
+ keyup: $.proxy( this, "_handleWindowKeyUp" )
+ }
+ }
+ ]
+ });
+
+ $.each( this.options, function( key, value ) {
+ // Cause initial options to be applied by their handler by temporarily setting the option to undefined
+ // - the handler then sets it to the initial value
+ self.options[ key ] = undefined;
+ self._setOption( key, value, true );
+ });
+
+ ui.screen.bind( "vclick", $.proxy( this, "_eatEventAndClose" ) );
+
+ $.each( this._globalHandlers, function( idx, value ) {
+ value.src.bind( value.handler );
+ });
+ },
+
+ _applyTheme: function( dst, theme, prefix ) {
+ var classes = ( dst.attr( "class" ) || "").split( " " ),
+ alreadyAdded = true,
+ currentTheme = null,
+ matches,
+ themeStr = String( theme );
+
+ while ( classes.length > 0 ) {
+ currentTheme = classes.pop();
+ matches = ( new RegExp( "^ui-" + prefix + "-([a-z])$" ) ).exec( currentTheme );
+ if ( matches && matches.length > 1 ) {
+ currentTheme = matches[ 1 ];
+ break;
+ } else {
+ currentTheme = null;
+ }
+ }
+
+ if ( theme !== currentTheme ) {
+ dst.removeClass( "ui-" + prefix + "-" + currentTheme );
+ if ( ! ( theme === null || theme === "none" ) ) {
+ dst.addClass( "ui-" + prefix + "-" + themeStr );
+ }
+ }
+ },
+
+ _setTheme: function( value ) {
+ this._applyTheme( this.element, value, "body" );
+ },
+
+ _setOverlayTheme: function( value ) {
+ this._applyTheme( this._ui.screen, value, "overlay" );
+
+ if ( this._isOpen ) {
+ this._ui.screen.addClass( "in" );
+ }
+ },
+
+ _setShadow: function( value ) {
+ this.element.toggleClass( "ui-overlay-shadow", value );
+ },
+
+ _setCorners: function( value ) {
+ this.element.toggleClass( "ui-corner-all", value );
+ },
+
+ _applyTransition: function( value ) {
+ this._ui.container.removeClass( this._fallbackTransition );
+ if ( value && value !== "none" ) {
+ this._fallbackTransition = $.mobile._maybeDegradeTransition( value );
+ this._ui.container.addClass( this._fallbackTransition );
+ }
+ },
+
+ _setTransition: function( value ) {
+ if ( !this._currentTransition ) {
+ this._applyTransition( value );
+ }
+ },
+
+ _setTolerance: function( value ) {
+ var tol = { t: 5, r: 5, b: 5, l: 5 };
+
+ if ( value ) {
+ var ar = String( value ).split( "," );
+
+ $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } );
+
+ switch( ar.length ) {
+ // All values are to be the same
+ case 1:
+ if ( !isNaN( ar[ 0 ] ) ) {
+ tol.t = tol.r = tol.b = tol.l = ar[ 0 ];
+ }
+ break;
+
+ // The first value denotes top/bottom tolerance, and the second value denotes left/right tolerance
+ case 2:
+ if ( !isNaN( ar[ 0 ] ) ) {
+ tol.t = tol.b = ar[ 0 ];
+ }
+ if ( !isNaN( ar[ 1 ] ) ) {
+ tol.l = tol.r = ar[ 1 ];
+ }
+ break;
+
+ // The array contains values in the order top, right, bottom, left
+ case 4:
+ if ( !isNaN( ar[ 0 ] ) ) {
+ tol.t = ar[ 0 ];
+ }
+ if ( !isNaN( ar[ 1 ] ) ) {
+ tol.r = ar[ 1 ];
+ }
+ if ( !isNaN( ar[ 2 ] ) ) {
+ tol.b = ar[ 2 ];
+ }
+ if ( !isNaN( ar[ 3 ] ) ) {
+ tol.l = ar[ 3 ];
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ this._tolerance = tol;
+ },
+
+ _setOption: function( key, value ) {
+ var exclusions, setter = "_set" + key.charAt( 0 ).toUpperCase() + key.slice( 1 );
+
+ if ( this[ setter ] !== undefined ) {
+ this[ setter ]( value );
+ }
+
+ // TODO REMOVE FOR 1.2.1 by moving them out to a default options object
+ exclusions = [
+ "initSelector",
+ "closeLinkSelector",
+ "closeLinkEvents",
+ "navigateEvents",
+ "closeEvents",
+ "history",
+ "container"
+ ];
+
+ $.mobile.widget.prototype._setOption.apply( this, arguments );
+ if ( $.inArray( key, exclusions ) === -1 ) {
+ // Record the option change in the options and in the DOM data-* attributes
+ this.element.attr( "data-" + ( $.mobile.ns || "" ) + ( key.replace( /([A-Z])/, "-$1" ).toLowerCase() ), value );
+ }
+ },
+
+ // Try and center the overlay over the given coordinates
+ _placementCoords: function( desired ) {
+ // rectangle within which the popup must fit
+ var
+ winCoords = windowCoords(),
+ rc = {
+ x: this._tolerance.l,
+ y: winCoords.y + this._tolerance.t,
+ cx: winCoords.cx - this._tolerance.l - this._tolerance.r,
+ cy: winCoords.cy - this._tolerance.t - this._tolerance.b
+ },
+ menuSize, ret,
+ linkOffset = $(this.link).offset(),
+ positionOffsets = [],
+ correctionValue = [0,0],
+ arrayIdx;
+
+ // Clamp the width of the menu before grabbing its size
+ this._ui.container.css( "max-width", rc.cx );
+ menuSize = {
+ cx: this._ui.container.outerWidth( true ),
+ cy: this._ui.container.outerHeight( true )
+ };
+
+ // Center the menu over the desired coordinates, while not going outside
+ // the window tolerances. This will center wrt. the window if the popup is too large.
+ ret = {
+ x: fitSegmentInsideSegment( rc.cx, menuSize.cx, rc.x, desired.x ),
+ y: fitSegmentInsideSegment( rc.cy, menuSize.cy, rc.y, desired.y )
+ };
+
+ // Make sure the top of the menu is visible
+ ret.y = Math.max( 0, ret.y );
+
+ // If the height of the menu is smaller than the height of the document
+ // align the bottom with the bottom of the document
+
+ // fix for $( document ).height() bug in core 1.7.2.
+ var docEl = document.documentElement, docBody = document.body,
+ docHeight = Math.max( docEl.clientHeight, docBody.scrollHeight, docBody.offsetHeight, docEl.scrollHeight, docEl.offsetHeight );
+
+ ret.y -= Math.min( ret.y, Math.max( 0, ret.y + menuSize.cy - docHeight ) );
+
+ if ( this.positionTo !== "origin" ) {
+ return { left: ret.x, top: ret.y , arrowleft: 0 , arrowtop: 0};
+ } else if( this.options.isHardwarePopup ) {
+ return { left: this._tolerance.l, top: $(window).height() - menuSize.cy - this._tolerance.b, arrowleft: 0 , arrowtop: 0 };
+ }
+
+ positionOffsets = [ linkOffset.left,
+ linkOffset.top,
+ docEl.clientHeight - ( linkOffset.top + $(this.link).height() ),
+ docEl.clientWidth - ( linkOffset.left + $(this.link).width() )];
+ arrayIdx = positionOffsets.indexOf(Math.max.apply(window,positionOffsets));
+
+ switch( arrayIdx )
+ {
+ case 0:
+ correctionValue = [ -$(this.link).width() , 0];
+ arrowtop = ( linkOffset.top - ret.y ) + ( $(this.link).height() / 2 ) - parseInt( $(this._ui.arrow).css("border-width") ) ;
+ arrowleft = menuSize.cx;
+ $(this._ui.arrow).attr( "class", "" )
+ .addClass( "ui-arrow left" )
+ break;
+ case 1:
+ correctionValue = [ 0 , -(ret.y + menuSize.cy - linkOffset.top)];
+ arrowtop = menuSize.cy - 2;
+ arrowleft = (linkOffset.left - ret.x + correctionValue[0]) + ( $(this.link).width() / 2 ) - parseInt( $(this._ui.arrow).css("border-width") ) / 2;
+ $(this._ui.arrow).attr( "class", "" )
+ .addClass( "ui-arrow bottom" );
+ break;
+ case 2:
+ correctionValue = [ 0 , ( linkOffset.top + $(this.link).height() - ret.y ) ];
+ arrowtop = - parseInt( $(this._ui.arrow).css("border-width") ) * 2 + 1;
+ arrowleft = (linkOffset.left - ret.x + correctionValue[0]) + ( $(this.link).width() / 2 ) - parseInt( $(this._ui.arrow).css("border-width") ) / 2;
+ $(this._ui.arrow).attr( "class", "" )
+ .addClass("ui-arrow top");
+ break;
+ case 3:
+ correctionValue = [ ( menuSize.cx < $(this.link).width() ) ? ( $(this.link).width() / 2 ) + ( menuSize.cx / 2) : $(this.link).width() , 0];
+ arrowtop = ( linkOffset.top - ret.y ) + ( $(this.link).height() / 2 ) - parseInt( $(this._ui.arrow).css("border-width") ) ;
+ arrowleft = - parseInt( $(this._ui.arrow).css("border-width") ) * 2;
+ $(this._ui.arrow).attr( "class", "" )
+ .addClass("ui-arrow right");
+ break;
+ }
+
+ return { left: ret.x + correctionValue[0], top: ret.y + correctionValue[1] , arrowleft: arrowleft , arrowtop: arrowtop };
+ },
+
+ _createPrereqs: function( screenPrereq, containerPrereq, whenDone ) {
+ var self = this, prereqs;
+
+ // It is important to maintain both the local variable prereqs and self._prereqs. The local variable remains in
+ // the closure of the functions which call the callbacks passed in. The comparison between the local variable and
+ // self._prereqs is necessary, because once a function has been passed to .animationComplete() it will be called
+ // next time an animation completes, even if that's not the animation whose end the function was supposed to catch
+ // (for example, if an abort happens during the opening animation, the .animationComplete handler is not called for
+ // that animation anymore, but the handler remains attached, so it is called the next time the popup is opened
+ // - making it stale. Comparing the local variable prereqs to the widget-level variable self._prereqs ensures that
+ // callbacks triggered by a stale .animationComplete will be ignored.
+
+ prereqs = {
+ screen: $.Deferred(),
+ container: $.Deferred()
+ };
+
+ prereqs.screen.then( function() {
+ if ( prereqs === self._prereqs ) {
+ screenPrereq();
+ }
+ });
+
+ prereqs.container.then( function() {
+ if ( prereqs === self._prereqs ) {
+ containerPrereq();
+ }
+ });
+
+ $.when( prereqs.screen, prereqs.container ).done( function() {
+ if ( prereqs === self._prereqs ) {
+ self._prereqs = null;
+ whenDone();
+ }
+ });
+
+ self._prereqs = prereqs;
+ },
+
+ _animate: function( args ) {
+ // NOTE before removing the default animation of the screen
+ // this had an animate callback that would relove the deferred
+ // now the deferred is resolved immediately
+ // TODO remove the dependency on the screen deferred
+ this._ui.screen
+ .removeClass( args.classToRemove )
+ .addClass( args.screenClassToAdd );
+
+ args.prereqs.screen.resolve();
+
+ if ( args.transition && args.transition !== "none" ) {
+ if ( args.applyTransition ) {
+ this._applyTransition( args.transition );
+ }
+ this._ui.container
+ .animationComplete( $.proxy( args.prereqs.container, "resolve" ) )
+ .addClass( args.containerClassToAdd )
+ .removeClass( args.classToRemove );
+ } else {
+ this._ui.container.removeClass( args.classToRemove );
+ args.prereqs.container.resolve();
+ }
+ },
+
+ // The desired coordinates passed in will be returned untouched if no reference element can be identified via
+ // desiredPosition.positionTo. Nevertheless, this function ensures that its return value always contains valid
+ // x and y coordinates by specifying the center middle of the window if the coordinates are absent.
+ _desiredCoords: function( x, y, positionTo ) {
+ var dst = null, offset, winCoords = windowCoords();
+
+ self.positionTo = positionTo;
+
+ // Establish which element will serve as the reference
+ if ( positionTo && positionTo !== "origin" ) {
+ if ( positionTo === "window" ) {
+ x = winCoords.cx / 2 + winCoords.x;
+ y = winCoords.cy / 2 + winCoords.y;
+ } else {
+ try {
+ dst = $( positionTo );
+ } catch( e ) {
+ dst = null;
+ }
+ if ( dst ) {
+ dst.filter( ":visible" );
+ if ( dst.length === 0 ) {
+ dst = null;
+ }
+ }
+ }
+ }
+
+ // If an element was found, center over it
+ if ( dst ) {
+ offset = dst.offset();
+ x = offset.left + dst.outerWidth() / 2;
+ y = offset.top + dst.outerHeight() / 2;
+ }
+
+ // Make sure x and y are valid numbers - center over the window
+ if ( $.type( x ) !== "number" || isNaN( x ) ) {
+ x = winCoords.cx / 2 + winCoords.x;
+ }
+ if ( $.type( y ) !== "number" || isNaN( y ) ) {
+ y = winCoords.cy / 2 + winCoords.y;
+ }
+
+ return { x: x, y: y };
+ },
+
+ _reposition: function() {
+ var self = this,
+ coords;
+
+ if( self._isOpen
+ && self.link
+ && self.positionTo !== "window") {
+ coords = self._placementCoords( self._desiredCoords( $(self.link).offset().left + $(self.link).outerWidth() /2 , $(self.link).offset().top + $(self.link).outerHeight() /2 , self.positionTo || self.options.positionTo || "origin" ) );
+ self._ui.container
+ .offset( { top : coords.top } );
+ }
+ },
+
+ _openPrereqsComplete: function() {
+ var self = this;
+
+ self._ui.container.addClass( "ui-popup-active" );
+ self._isOpen = true;
+ self._resizeScreen();
+
+ // Android appears to trigger the animation complete before the popup
+ // is visible. Allowing the stack to unwind before applying focus prevents
+ // the "blue flash" of element focus in android 4.0
+ setTimeout(function(){
+ self._ui.container.attr( "tabindex", "0" ).focus();
+ self._trigger( "afteropen" );
+ self._reposition();
+ });
+ },
+
+ _open: function( options ) {
+ var coords, transition,
+ androidBlacklist = ( function() {
+ var w = window,
+ ua = navigator.userAgent,
+ // Rendering engine is Webkit, and capture major version
+ wkmatch = ua.match( /AppleWebKit\/([0-9\.]+)/ ),
+ wkversion = !!wkmatch && wkmatch[ 1 ],
+ androidmatch = ua.match( /Android (\d+(?:\.\d+))/ ),
+ andversion = !!androidmatch && androidmatch[ 1 ],
+ chromematch = ua.indexOf( "Chrome" ) > -1;
+
+ // Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and not Chrome.
+ if( androidmatch !== null && andversion === "4.0" && wkversion && wkversion > 534.13 && !chromematch ) {
+ return true;
+ }
+ return false;
+ }());
+
+ // Make sure options is defined
+ options = ( options || {} );
+
+ // Copy out the transition, because we may be overwriting it later and we don't want to pass that change back to the caller
+ transition = options.transition || this.options.transition;
+
+ // Give applications a chance to modify the contents of the container before it appears
+ this._trigger( "beforeposition" );
+
+ coords = this._placementCoords( this._desiredCoords( options.x, options.y, options.positionTo || this.options.positionTo || "origin" ) );
+
+ // Count down to triggering "popupafteropen" - we have two prerequisites:
+ // 1. The popup window animation completes (container())
+ // 2. The screen opacity animation completes (screen())
+ this._createPrereqs(
+ $.noop,
+ $.noop,
+ $.proxy( this, "_openPrereqsComplete" ) );
+
+ if ( transition ) {
+ this._currentTransition = transition;
+ this._applyTransition( transition );
+ } else {
+ transition = this.options.transition;
+ }
+
+ if ( !this.options.theme ) {
+ this._setTheme( this._page.jqmData( "theme" ) || $.mobile.getInheritedTheme( this._page, "c" ) );
+ }
+
+ this._ui.screen.removeClass( "ui-screen-hidden" );
+
+ this._ui.container
+ .removeClass( "ui-selectmenu-hidden" )
+ .offset( coords );
+ this._ui.arrow.css( { top : coords.arrowtop, left : coords.arrowleft } );
+ if ( this.options.overlayTheme && androidBlacklist ) {
+ /* TODO:
+ The native browser on Android 4.0.X ("Ice Cream Sandwich") suffers from an issue where the popup overlay appears to be z-indexed
+ above the popup itself when certain other styles exist on the same page -- namely, any element set to `position: fixed` and certain
+ types of input. These issues are reminiscent of previously uncovered bugs in older versions of Android's native browser:
+ https://github.com/scottjehl/Device-Bugs/issues/3
+
+ This fix closes the following bugs ( I use "closes" with reluctance, and stress that this issue should be revisited as soon as possible ):
+
+ https://github.com/jquery/jquery-mobile/issues/4816
+ https://github.com/jquery/jquery-mobile/issues/4844
+ https://github.com/jquery/jquery-mobile/issues/4874
+ */
+
+ // TODO sort out why this._page isn't working
+ this.element.closest( ".ui-page" ).addClass( "ui-popup-open" );
+ }
+ this._animate({
+ additionalCondition: true,
+ transition: transition,
+ classToRemove: "",
+ screenClassToAdd: "in",
+ containerClassToAdd: "in",
+ applyTransition: false,
+ prereqs: this._prereqs
+ });
+ },
+
+ _closePrereqScreen: function() {
+ this._ui.screen
+ .removeClass( "out" )
+ .addClass( "ui-screen-hidden" );
+ },
+
+ _closePrereqContainer: function() {
+ this._ui.container
+ .removeClass( "reverse out" )
+ .addClass( "ui-selectmenu-hidden" )
+ .removeAttr( "style" );
+ },
+
+ _closePrereqsDone: function() {
+ var self = this, opts = self.options;
+
+ self._ui.container.removeAttr( "tabindex" );
+
+ // remove nav bindings if they are still present
+ opts.container.unbind( opts.closeEvents );
+
+ // unbind click handlers added when history is disabled
+ self.element.undelegate( opts.closeLinkSelector, opts.closeLinkEvents );
+
+ // remove the global mutex for popups
+ $.mobile.popup.active = undefined;
+
+ // alert users that the popup is closed
+ self._trigger( "afterclose" );
+ },
+
+ _close: function( immediate ) {
+ this._ui.container.removeClass( "ui-popup-active" );
+ this._page.removeClass( "ui-popup-open" );
+
+ this._isOpen = false;
+
+ // IME hide when popup is closed
+ this.element.find("input").blur();
+
+ // Count down to triggering "popupafterclose" - we have two prerequisites:
+ // 1. The popup window reverse animation completes (container())
+ // 2. The screen opacity animation completes (screen())
+ this._createPrereqs(
+ $.proxy( this, "_closePrereqScreen" ),
+ $.proxy( this, "_closePrereqContainer" ),
+ $.proxy( this, "_closePrereqsDone" ) );
+
+ this._animate( {
+ additionalCondition: this._ui.screen.hasClass( "in" ),
+ transition: ( immediate ? "none" : ( this._currentTransition || this.options.transition ) ),
+ classToRemove: "in",
+ screenClassToAdd: "out",
+ containerClassToAdd: "reverse out",
+ applyTransition: true,
+ prereqs: this._prereqs
+ });
+ },
+
+ _destroy: function() {
+ var self = this;
+
+ // hide and remove bindings
+ self._close();
+
+ // Put the element back to where the placeholder was and remove the "ui-popup" class
+ self._setTheme( "none" );
+ self.element
+ .insertAfter( self._ui.placeholder )
+ .removeClass( "ui-popup ui-overlay-shadow ui-corner-all" );
+ self._ui.screen.remove();
+ self._ui.container.remove();
+ self._ui.placeholder.remove();
+
+ // Unbind handlers that were bound to elements outside self.element (the window, in self case)
+ // window history back is call "_destroy method"
+ // if this method is called, all kind of window resize event has been unbind
+ /*
+ $.each( self._globalHandlers, function( idx, oneSrc ) {
+ $.each( oneSrc.handler, function( eventType, handler ) {
+ oneSrc.src.unbind( eventType, handler );
+ });
+ });
+ */
+ },
+
+ _closePopup: function( e, data ) {
+ var parsedDst, toUrl;
+
+ if ( e.type === "pagebeforechange" && data ) {
+ if ( typeof data.toPage === "string" ) {
+ parsedDst = data.toPage;
+ } else {
+ parsedDst = data.toPage.jqmData( "url" );
+ }
+ parsedDst = $.mobile.path.parseUrl( parsedDst );
+ toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;
+
+ if ( this._myUrl !== toUrl ) {
+ this.options.container.unbind( this.options.closeEvents );
+ this._close( true );
+ } else {
+ this._close();
+ }
+ return; // skip normal close
+ }
+
+ this._close();
+ },
+
+ // any navigation event after a popup is opened should close the popup
+ // NOTE the pagebeforechange is bound to catch navigation events that don't
+ // alter the url (eg, dialogs from popups)
+ _bindContainerClose: function() {
+ var self = this;
+
+ self.options.container
+ .one( self.options.closeEvents, $.proxy( self, "_closePopup" ) );
+ },
+
+ // TODO no clear deliniation of what should be here and
+ // what should be in _open. Seems to be "visual" vs "history" for now
+ open: function( options ) {
+ var self = this, opts = this.options, url, hashkey, activePage, currentIsDialog, hasHash, urlHistory;
+ // self.link = ( $(event.target).attr('data-role') === 'button') ? event.target : $(event.target).closest('[data-role="button"]')[0];
+ // make sure open is idempotent
+ if( $.mobile.popup.active ) {
+ return;
+ }
+ // set the global popup mutex
+ $.mobile.popup.active = this;
+ if( !options ) {
+ options = [];
+ }
+
+ if ( !options.link ) {
+ if ( !event ) {
+ self.positionTo = "window";
+ } else {
+ self.link = ( $(event.target).closest('a')[0] || $(event.target).closest('div')[0] );
+ }
+ } else {
+ self.link = options.link;
+ }
+ if ( event ) {
+ self.positionTo = ( options != null && options.positionTo != null ) ? options.positionTo : "origin";
+ }
+
+ if ( $(self.link).jqmData("position-to") !== "window"
+ && self.positionTo !== "window" ) {
+
+ $(self.element).addClass("ui-ctxpopup");
+ $(self._ui.container).removeClass("ui-popup-container")
+ .addClass("ui-ctxpopup-container");
+
+ if( self.positionTo !== "origin" ) {
+ $(self._ui.arrow).hide();
+ } else {
+ $(self._ui.arrow).show();
+ }
+ } else {
+ $(self._ui.arrow).hide();
+ // apply opacity back screen
+ this._setOverlayTheme( "dim" );
+ }
+ if( !options.x
+ && self.positionTo === "origin"
+ && self.link ) {
+ options.x = $(self.link).offset().left + $(self.link).outerWidth() / 2;
+ }
+ if( !options.y
+ && self.positionTo === "origin"
+ && self.link ) {
+ options.y = $(self.link).offset().top + $(self.link).outerHeight() / 2;
+ }
+
+ // Hadeware key style popup
+ if( $(self.element).hasClass( "ui-ctxpopup-optionmenu" ) ){
+ self.options.isHardwarePopup = true;
+ $( self._ui.arrow).hide();
+ }
+
+ // if history alteration is disabled close on navigate events
+ // and leave the url as is
+ if( !( opts.history ) ) {
+ self._open( options );
+ self._bindContainerClose();
+
+ // When histoy is disabled we have to grab the data-rel
+ // back link clicks so we can close the popup instead of
+ // relying on history to do it for us
+ self.element
+ .delegate( opts.closeLinkSelector, opts.closeLinkEvents, function( e ) {
+ self._close();
+
+ // NOTE prevent the browser and navigation handlers from
+ // working with the link's rel=back. This may cause
+ // issues for developers expecting the event to bubble
+ return false;
+ });
+
+ return;
+ }
+
+ // cache some values for min/readability
+ hashkey = $.mobile.dialogHashKey;
+ activePage = $.mobile.activePage;
+ currentIsDialog = activePage.is( ".ui-dialog" );
+ // Set active page url
+ this._myUrl = url = urlHistory.getActive().url;
+ //
+ url = $.mobile.urlHistory.getActive().url;
+ hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog;
+ urlHistory = $.mobile.urlHistory;
+
+ if ( hasHash ) {
+ self._open( options );
+ self._bindContainerClose();
+ return;
+ }
+
+ // if the current url has no dialog hash key proceed as normal
+ // otherwise, if the page is a dialog simply tack on the hash key
+ if ( url.indexOf( hashkey ) === -1 && !currentIsDialog ){
+ url = url + hashkey;
+ } else {
+ url = $.mobile.path.parseLocation().hash + hashkey;
+ }
+
+ // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash
+ if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
+ url += hashkey;
+ }
+
+ // swallow the the initial navigation event, and bind for the next
+ opts.container.one( opts.navigateEvents, function( e ) {
+ e.preventDefault();
+ self._open( options );
+ self._bindContainerClose();
+ });
+
+ urlHistory.ignoreNextHashChange = currentIsDialog;
+
+ // Gotta love methods with 1mm args :(
+ urlHistory.addNew( url, undefined, undefined, undefined, "dialog" );
+
+ // set the new url with (or without) the new dialog hash key
+ $.mobile.path.set( url );
+ },
+
+ close: function() {
+ // make sure close is idempotent
+ if( !$.mobile.popup.active ){
+ return;
+ }
+
+ if( this.options.history ) {
+ $.mobile.back();
+ } else {
+ this._close();
+ }
+ }
+ });
+
+
+ // TODO this can be moved inside the widget
+ $.mobile.popup.handleLink = function( $link ) {
+ var closestPage = $link.closest( ":jqmData(role='page')" ),
+ scope = ( ( closestPage.length === 0 ) ? $( "body" ) : closestPage ),
+ // NOTE make sure to get only the hash, ie7 (wp7) return the absolute href
+ // in this case ruining the element selection
+ popup = $( $.mobile.path.parseUrl($link.attr( "href" )).hash, scope[0] ),
+ offset;
+
+ if ( popup.data( "popup" ) ) {
+ offset = $link.offset();
+ popup.popup( "open", {
+ x: offset.left + $link.outerWidth() / 2,
+ y: offset.top + $link.outerHeight() / 2,
+ transition: $.mobile.getAttrFixed( $link[0], "data-" + $.mobile.ns + "transition" ),
+ positionTo: $.mobile.getAttrFixed( $link[0], "data-" + $.mobile.ns + "position-to" ),
+ link: $link
+ });
+ }
+
+ //remove after delay
+ setTimeout( function() {
+ $link.removeClass( $.mobile.activeBtnClass );
+ }, 300 );
+ };
+
+ // TODO move inside _create
+ $.mobile.$document.bind( "pagebeforechange", function( e, data ) {
+ if ( data.options.role === "popup" ) {
+ $.mobile.popup.handleLink( data.options.link );
+ e.preventDefault();
+ }
+ });
+
+ //delegate self-init widgets
+ $.delegateSelfInitWithSingleSelector( $.mobile.popup, true );
+
+})( jQuery );
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+});
+//>>excludeEnd("jqmBuildExclude");