mirror of
https://github.com/Hopiu/jquery-mobile.git
synced 2026-03-24 09:50:26 +00:00
- Fixed header/footer code now listens for a custom event "contentmodified" on the document to figure out if it should be repositioned or not. Modified collapsible and listview to fire off contentmodified whenever they modify content. Developers can also fire off this event to trigger position updates for fixed headers/footers, so this could be used to address issue #2042 and the mention of collapsible in #2596.
315 lines
9.3 KiB
JavaScript
315 lines
9.3 KiB
JavaScript
/*
|
|
* jQuery Mobile Framework : "listview" plugin
|
|
* Copyright (c) jQuery Project
|
|
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
* http://jquery.org/license
|
|
*/
|
|
|
|
(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: "c",
|
|
countTheme: "c",
|
|
headerTheme: "b",
|
|
dividerTheme: "b",
|
|
splitIcon: "arrow-r",
|
|
splitTheme: "b",
|
|
inset: false,
|
|
initSelector: ":jqmData(role='listview')"
|
|
},
|
|
|
|
_create: function() {
|
|
var t = this;
|
|
|
|
// create listview markup
|
|
t.element.addClass(function( i, orig ) {
|
|
return orig + " ui-listview " + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
|
|
});
|
|
|
|
t.refresh( true );
|
|
},
|
|
|
|
_itemApply: function( $list, item ) {
|
|
var $countli = item.find( ".ui-li-count" );
|
|
if ( $countli.length ) {
|
|
item.addClass( "ui-li-has-count" );
|
|
}
|
|
$countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
|
|
|
|
// TODO class has to be defined in markup
|
|
item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
|
|
.find( "p, dl" ).addClass( "ui-li-desc" ).end()
|
|
.find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() {
|
|
item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
|
|
}).end()
|
|
.find( ".ui-li-aside" ).each(function() {
|
|
var $this = $(this);
|
|
$this.prependTo( $this.parent() ); //shift aside to front for css float
|
|
});
|
|
},
|
|
|
|
_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;
|
|
|
|
if ( this.options.inset ) {
|
|
$li = this.element.children( "li" );
|
|
// at create time the li are not visible yet so we need to rely on .ui-screen-hidden
|
|
$visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
|
|
|
|
this._removeCorners( $li );
|
|
|
|
// Select the first visible li element
|
|
$topli = $visibleli.first()
|
|
.addClass( "ui-corner-top" );
|
|
|
|
$topli.add( $topli.find( ".ui-btn-inner" ) )
|
|
.find( ".ui-li-link-alt" )
|
|
.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" );
|
|
|
|
$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" );
|
|
}
|
|
if ( !create ) {
|
|
this.element.trigger( "contentmodified" );
|
|
}
|
|
},
|
|
|
|
refresh: function( create ) {
|
|
this.parentPage = this.element.closest( ".ui-page" );
|
|
this._createSubPages();
|
|
|
|
var o = this.options,
|
|
$list = this.element,
|
|
self = this,
|
|
dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
|
|
listsplittheme = $list.jqmData( "splittheme" ),
|
|
listspliticon = $list.jqmData( "spliticon" ),
|
|
li = $list.children( "li" ),
|
|
counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
|
|
item, itemClass, itemTheme,
|
|
a, last, splittheme, countParent, icon;
|
|
|
|
if ( counter ) {
|
|
$list.find( ".ui-li-dec" ).remove();
|
|
}
|
|
|
|
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 = item.jqmData("theme") || o.theme;
|
|
a = item.children( "a" );
|
|
|
|
if ( a.length ) {
|
|
icon = item.jqmData("icon");
|
|
|
|
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().addClass( "ui-link-inherit" );
|
|
|
|
if ( a.length > 1 ) {
|
|
itemClass += " ui-li-has-alt";
|
|
|
|
last = a.last();
|
|
splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
|
|
|
|
last.appendTo(item)
|
|
.attr( "title", last.getEncodedText() )
|
|
.addClass( "ui-li-link-alt" )
|
|
.empty()
|
|
.buttonMarkup({
|
|
shadow: false,
|
|
corners: false,
|
|
theme: itemTheme,
|
|
icon: false,
|
|
iconpos: false
|
|
})
|
|
.find( ".ui-btn-inner" )
|
|
.append(
|
|
$( "<span />" ).buttonMarkup({
|
|
shadow: true,
|
|
corners: true,
|
|
theme: splittheme,
|
|
iconpos: "notext",
|
|
icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
|
|
})
|
|
);
|
|
}
|
|
} else if ( item.jqmData( "role" ) === "list-divider" ) {
|
|
|
|
itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
|
|
item.attr( "role", "heading" );
|
|
|
|
//reset counter when a divider heading is encountered
|
|
if ( counter ) {
|
|
counter = 1;
|
|
}
|
|
|
|
} else {
|
|
itemClass += " ui-li-static ui-body-" + itemTheme;
|
|
}
|
|
}
|
|
|
|
if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
|
|
countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
|
|
|
|
countParent.addClass( "ui-li-jsnumbering" )
|
|
.prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
|
|
}
|
|
|
|
item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
|
|
|
|
self._itemApply( $list, item );
|
|
}
|
|
|
|
this._refreshCorners( create );
|
|
},
|
|
|
|
//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(),
|
|
nodeEls = $( list.prevAll().toArray().reverse() ),
|
|
nodeEls = nodeEls.length ? nodeEls : $( "<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 = list.jqmData( "theme" ) || o.theme,
|
|
countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "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;
|
|
|
|
if( ui.nextPage ){
|
|
npURL = nextPage.jqmData( "url" );
|
|
if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){
|
|
self.childPages().remove();
|
|
parentPage.remove();
|
|
}
|
|
}
|
|
};
|
|
|
|
// 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 +"')");
|
|
}
|
|
});
|
|
|
|
//auto self-init widgets
|
|
$( document ).bind( "pagecreate create", function( e ){
|
|
$( $.mobile.listview.prototype.options.initSelector, e.target ).listview();
|
|
});
|
|
|
|
})( jQuery );
|