@@ -110,8 +110,10 @@ define( [ "jquery", "../jquery.mobile.vmouse", "../jquery.mobile.support.touch"

// ONLY trigger a 'tap' event if the start target is
// the same as the stop target.
if ( $.event.special.tap.emitTapOnTaphold && !isTaphold && origTarget === event.target ) {
if ( !isTaphold && origTarget === event.target ) {
triggerCustomEvent( thisObject, "tap", event );
} else if ( isTaphold ) {
event.stopPropagation();
}
}

@@ -120,7 +122,9 @@ define( [ "jquery", "../jquery.mobile.vmouse", "../jquery.mobile.support.touch"
$document.bind( "vmousecancel", clearTapHandlers );

timer = setTimeout( function() {
isTaphold = true;
if( $.event.special.tap.emitTapOnTaphold ) {
isTaphold = true;
}
triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
}, $.event.special.tap.tapholdThreshold );
});
@@ -17,7 +17,7 @@ define( [ "jquery", "./jquery.mobile.ns", "json!../package.json" ], function( jQ
jqmDataRE = /:jqmData\(([^)]*)\)/g;

// jQuery.mobile configurable options
$.mobile = $.extend($.mobile, {
$.extend($.mobile, {

// Version of the jQuery Mobile Framework
version: __version__,
@@ -97,40 +97,23 @@ define( [ "jquery", "./jquery.mobile.ns", "json!../package.json" ], function( jQ
window: $( window ),
document: $( document ),

// TODO might be useful upstream in jquery itself ?
keyCode: {
ALT: 18,
BACKSPACE: 8,
CAPS_LOCK: 20,
COMMA: 188,
COMMAND: 91,
COMMAND_LEFT: 91, // COMMAND
COMMAND_RIGHT: 93,
CONTROL: 17,
DELETE: 46,
DOWN: 40,
END: 35,
ENTER: 13,
ESCAPE: 27,
HOME: 36,
INSERT: 45,
LEFT: 37,
MENU: 93, // COMMAND_RIGHT
NUMPAD_ADD: 107,
NUMPAD_DECIMAL: 110,
NUMPAD_DIVIDE: 111,
NUMPAD_ENTER: 108,
NUMPAD_MULTIPLY: 106,
NUMPAD_SUBTRACT: 109,
PAGE_DOWN: 34,
PAGE_UP: 33,
PERIOD: 190,
RIGHT: 39,
SHIFT: 16,
SPACE: 32,
TAB: 9,
UP: 38,
WINDOWS: 91 // COMMAND
UP: 38
},

// Place to store various widget extensions
@@ -177,10 +160,6 @@ define( [ "jquery", "./jquery.mobile.ns", "json!../package.json" ], function( jQ
// and then camel case the attribute string. Add the result
// to our nsNormalizeDict so we don't have to do this again.
nsNormalize: function( prop ) {
if ( !prop ) {
return;
}

return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) );
},

@@ -271,7 +250,7 @@ define( [ "jquery", "./jquery.mobile.ns", "json!../package.json" ], function( jQ
// jQuery version is here as a normalized fallback for platforms like Symbian
return window.innerHeight || $.mobile.window.height();
}
}, $.mobile );
});

// Mobile version of data and removeData and hasData methods
// ensures all data is set and retrieved using jQuery Mobile's data namespace
@@ -321,20 +300,21 @@ define( [ "jquery", "./jquery.mobile.ns", "json!../package.json" ], function( jQ
};

$.fn.addDependents = function( newDependents ) {
$.addDependents( $( this ), newDependents );
$.addDependents( this , newDependents );
};

$.addDependents = function( elem, newDependents ) {
var dependents = $( elem ).jqmData( "dependents" ) || $();
var $elem = $( elem ),
dependents = $elem.jqmData( "dependents" ) || $();

$( elem ).jqmData( "dependents", $.merge( dependents, newDependents ) );
$elem.jqmData( "dependents", $( dependents ).add( newDependents ) );
};

// note that this helper doesn't attempt to handle the callback
// or setting of an html element's text, its only purpose is
// to return the html encoded version of the text in all cases. (thus the name)
$.fn.getEncodedText = function() {
return $( "<div/>" ).text( $( this ).text() ).html();
return $( "<a>" ).text( $( this ).text() ).html();
};

// fluent helper function for the mobile namespaced equivalent
@@ -12,8 +12,7 @@ define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.buttonMarkup",
//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 = {},
getAttr = $.mobile.getAttribute;
var getAttr = $.mobile.getAttribute;

$.widget( "mobile.listview", $.mobile.widget, $.extend( {

@@ -98,7 +97,6 @@ $.widget( "mobile.listview", $.mobile.widget, $.extend( {

refresh: function( create ) {
this.parentPage = this.element.closest( ".ui-page" );
this._createSubPages();

var o = this.options,
$list = this.element,
@@ -287,95 +285,6 @@ $.widget( "mobile.listview", $.mobile.widget, $.extend( {
//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 = getAttr( parentPage[ 0 ], "url", true ),
parentId = parentUrl || parentPage[ 0 ][ $.expando ],
parentListId = parentList.attr( "id" ),
o = this.options,
dns = "data-" + $.mobile.ns,
self = this,
persistentFooter = parentPage.find( ":jqmData(role='footer')" ),
persistentFooterID = ( persistentFooter.length > 0 ? getAttr( persistentFooter[ 0 ], "id", true ) : undefined ),
hasSubPages,
newRemove = function( e, ui ) {
var nextPage = ui.nextPage, npURL,
prEvent = new $.Event( "pageremove" );

if ( ui.nextPage ) {
npURL = getAttr( nextPage[ 0 ], "url", true );
if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
self.childPages().remove();
parentPage.trigger( prEvent );
if ( !prEvent.isDefaultPrevented() ) {
parentPage.removeWithDependents();
}
}
}
};

if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
listCountPerPage[ parentId ] = -1;
}

parentListId = parentListId || ++listCountPerPage[ parentId ];

$( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
var 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 = getAttr( list[ 0 ], "theme", true ) || o.theme,
countTheme = getAttr( list[ 0 ], "counttheme", true ) || getAttr( parentList[ 0 ], "counttheme", true ) || 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( "mobile-page" ).options.domCache === false ) {

// unbind the original page remove and replace with our specialized version
parentPage
.unbind( "pagehide.remove" )
.bind( "pagehide.remove", newRemove);
}
},

// 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 + "')" );
}
}, $.mobile.behaviors.addFirstLastClasses ) );

@@ -855,137 +855,6 @@

module( "Cached Linked List" );

var findNestedPages = function(selector){
return $( selector + " #topmost" ).listview( 'childPages' );
};

asyncTest( "nested pages are removed from the dom by default", function(){
$.testHelper.pageSequence([
function(){
//reset for relative url refs
$.mobile.changePage( home );
},

function(){
$.mobile.changePage( "cache-tests/uncached-nested.html" );
},

function(){
ok( findNestedPages( "#uncached-nested-list" ).length > 0, "verify that there are nested pages" );
$.mobile.changePage( home );
},

function() {
$.mobile.changePage( "cache-tests/clear.html" );
},

function(){
deepEqual( findNestedPages( "#uncached-nested-list" ).length, 0 );
start();
}
]);
});

asyncTest( "nested pages preserved when parent page is cached", function(){

$.testHelper.pageSequence([
function(){
//reset for relative url refs
$.mobile.changePage( home );
},

function(){
$.mobile.changePage( "cache-tests/cached-nested.html" );
},

function(){
ok( findNestedPages( "#cached-nested-list" ).length > 0, "verify that there are nested pages" );
$.mobile.changePage( home );
},

function() {
$.mobile.changePage( "cache-tests/clear.html" );
},

function(){
ok( findNestedPages( "#cached-nested-list" ).length > 0, "nested pages remain" );
start();
}
]);
});

asyncTest( "parent page is not removed when visiting a sub page", function(){
$.testHelper.pageSequence([
function(){
//reset for relative url refs
$.mobile.changePage( home );
},

function(){
$.mobile.changePage( "cache-tests/cached-nested.html" );
},

function(){
deepEqual( $("#cached-nested-list").length, 1 );
$.mobile.changePage( home );
},

function() {
$.mobile.changePage( "cache-tests/clear.html" );
},

function(){
deepEqual( $("#cached-nested-list").length, 1 );
start();
}
]);
});

asyncTest( "nested pages hash key is always in the hash (replaceState)", function(){
$.testHelper.pageSequence([
function(){
//reset for relative url refs
$.mobile.changePage( home );
},

function(){
// https://github.com/jquery/jquery-mobile/issues/1617
$.mobile.changePage("#nested-lists-test");
},

function(){
// Click on the link of the third li element
$('.ui-page-active li:eq(2) a:eq(0)').click();
},

function(){
ok( location.hash.search($.mobile.subPageUrlKey) >= 0 );
start();
}
]);
});

asyncTest( "embedded listview page with nested pages is not removed from the dom", function() {
$.testHelper.pageSequence([
function() {
// open the nested list page
deepEqual( $("div#nested-list-test").length, 1 );
$( "a#nested-list-test-anchor" ).click();
},

function() {
// go back to the origin page
window.history.back();
},

function() {
// make sure the page is still in place
deepEqual( $("div#nested-list-test").length, 1 );
start();
}
]);
});


asyncTest( "list inherits theme from parent", function() {
$.testHelper.pageSequence([