cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pplaque...@apache.org
Subject [21/52] [abbrv] [cordova-tizen] tizen SDK 2.2 support
Date Mon, 26 Aug 2013 15:33:55 GMT
http://git-wip-us.apache.org/repos/asf/cordova-tizen/blob/e21e0780/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/listview.js
----------------------------------------------------------------------
diff --git a/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/listview.js b/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/listview.js
new file mode 100644
index 0000000..9a56d4b
--- /dev/null
+++ b/templates/CordovaTizenWebUIFrameworkTemplate/project/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/e21e0780/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/loader.js
----------------------------------------------------------------------
diff --git a/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/loader.js b/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/loader.js
new file mode 100644
index 0000000..ba5c190
--- /dev/null
+++ b/templates/CordovaTizenWebUIFrameworkTemplate/project/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/e21e0780/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/navbar.js
----------------------------------------------------------------------
diff --git a/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/navbar.js b/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/navbar.js
new file mode 100644
index 0000000..f4842f8
--- /dev/null
+++ b/templates/CordovaTizenWebUIFrameworkTemplate/project/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/e21e0780/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.js
----------------------------------------------------------------------
diff --git a/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.js b/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.js
new file mode 100644
index 0000000..cd7efa6
--- /dev/null
+++ b/templates/CordovaTizenWebUIFrameworkTemplate/project/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/e21e0780/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.sections.js
----------------------------------------------------------------------
diff --git a/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.sections.js b/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/page.sections.js
new file mode 100644
index 0000000..352355e
--- /dev/null
+++ b/templates/CordovaTizenWebUIFrameworkTemplate/project/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/e21e0780/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/popup.js
----------------------------------------------------------------------
diff --git a/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/popup.js b/templates/CordovaTizenWebUIFrameworkTemplate/project/tizen-web-ui-fw/latest/js/modules/jqm/widgets/popup.js
new file mode 100644
index 0000000..136686c
--- /dev/null
+++ b/templates/CordovaTizenWebUIFrameworkTemplate/project/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");


Mime
View raw message