Skip to content

Commit

Permalink
Navigation: Add demo showing how to copy script references and widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Schulhof committed Jun 4, 2015
1 parent f1ba6a0 commit 958569a
Show file tree
Hide file tree
Showing 16 changed files with 465 additions and 361 deletions.
75 changes: 56 additions & 19 deletions demos/_assets/js/view-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,50 @@ function getSnippet( type, selector, source ) {

// First, try to grab a tag in this document
if ( !$.mobile.path.isPath( selector ) ) {
el = source.find( type + selector );
el = source.find( ( "markup" === type ? "" : type ) + selector );
// If this is not an embedded style, try a stylesheet reference
if ( el.length === 0 && type === "style" ) {
el = source.find( "link[rel='stylesheet']" + selector );
}
text = $( "<div></div>" ).append( el.contents().clone() ).html();

// Stringify each element and cache the string representation on the element. This helps us
// avoid re-stringifying the element later. This, in turn, prevents us from re-stringifying
// already enhanced elements, such as shared widgets outside the page, when the View Source
// button is in the page, and the elements have already been enhanced when the View Source
// button is created. This assumes, of course, that the first time we stringify an element
// the element is not yet enhanced.
el.each( function( index, singleElement ) {
var whitespace,
single = $( this ),
singleText = single.jqmData( "viewSourceCachedData" );

if ( !singleText ) {
singleText = $( "<div></div>" )
.append( ( "markup" === type ? single : single.contents() ).clone() )
.html();

// If we're dealing with markup, retrieve the initial indentation of the element so
// we get proper indentation in the source view
if ( type === "markup" ) {
if ( this.previousSibling && this.previousSibling.nodeType === 3 ) {
whitespace = $( "<div>" )
.append( $( this.previousSibling ).clone() )
.html()
.match( /\n(\s*)$/ );
if ( whitespace && whitespace.length > 1 ) {
singleText = whitespace[ 1 ] + singleText;
}
}
}
single.jqmData( "viewSourceCachedData", singleText );
}

text = text +

// Separate the text for multiple elements with a newline
( index > 0 ? "\n" : "" ) +
singleText;
});
if ( !text ) {
text = "";
selector = el.attr( "href" ) || el.attr( "src" ) || "";
Expand Down Expand Up @@ -91,7 +129,7 @@ $( document ).bind( "pagebeforechange", function( e, data ) {

attachPopupHandler( popup, sources );
popup
.appendTo( $.mobile.activePage )
.appendTo( "body" )
.popup()
.bind( "popupafterclose", function() {
popup.remove();
Expand Down Expand Up @@ -136,7 +174,7 @@ $.fn.viewSourceCode = function() {
if ( self.attr( "data-demo-html" ) === "true" ) {
data = self.html();
} else {
data = $( "<div></div>" ).append( $( self.attr( "data-demo-html" ) ).clone() ).html();
data = getSnippet( "markup", self.attr( "data-demo-html" ), $( document ) );
}
sources.push( { title: "HTML", theme: "c", brush: "xml", data: fixData( data ) } );
}
Expand Down Expand Up @@ -174,22 +212,12 @@ $( document ).on( "pagebeforecreate", "[data-role='page']", function() {
SyntaxHighlighter.defaults['auto-links'] = false;
});

$( document ).on( "pagecreate", function( e ) {
// prevent page scroll while scrolling source code
$( document ).on( "mousewheel", ".jqm-view-source .ui-collapsible-content", function( event, delta ) {
if ( delta > 0 && $( this ).scrollTop() === 0 ) {
event.preventDefault();
} else if ( delta < 0 && $( this ).scrollTop() === $( this ).get( 0 ).scrollHeight - $( this ).innerHeight() ) {
event.preventDefault();
}
});

$( document )
// reposition when switching between html / js / css
$( e.target ).delegate( ".jqm-view-source .ui-collapsible", "expand", function() {
.on( "collapsibleexpand", ".jqm-view-source .ui-collapsible", function() {
$( this ).parents( ":mobile-popup" ).popup( "reposition", { positionTo: "window" } );
});

$( e.target ).delegate( ".jqm-view-source", "popupbeforeposition", function() {
})
.on( "popupbeforeposition", ".jqm-view-source", function() {
// max height: screen height - tolerance (2*30px) - 42px for each collapsible heading
var x = $( this ).find( ".ui-collapsible" ).length,
maxHeight = $.mobile.getScreenHeight() - 60 - ( x * 42 );
Expand All @@ -209,8 +237,17 @@ $( document ).on( "pagecreate", function( e ) {
$( line ).height( height );
}
});
})
.on( "pagecreate", function( e ) {
// prevent page scroll while scrolling source code
$( document ).on( "mousewheel", ".jqm-view-source .ui-collapsible-content", function( event, delta ) {
if ( delta > 0 && $( this ).scrollTop() === 0 ) {
event.preventDefault();
} else if ( delta < 0 && $( this ).scrollTop() === $( this ).get( 0 ).scrollHeight - $( this ).innerHeight() ) {
event.preventDefault();
}
});
});
});

/**
* SyntaxHighlighter
Expand Down
51 changes: 51 additions & 0 deletions demos/external-widgets/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>External Widgets - jQuery Mobile Demos</title>
<link rel="stylesheet" href="../../css/themes/default/jquery.mobile.css">
<link rel="stylesheet" href="../_assets/css/jqm-demos.css">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:300,400,700">
<script src="../../external/jquery/jquery.js"></script>
<script src="../_assets/js/"></script>
<script src="../../js/"></script>
</head>
<body>
<div data-role="page" class="jqm-demos" data-quicklinks="true">

<div data-role="header" class="jqm-header">
<h2><a href="../" title="jQuery Mobile Demos home"><img src="../_assets/img/jquery-logo.png" alt="jQuery Mobile"></a></h2>
<p><span class="jqm-version"></span> Demos</p>
<a href="#" class="jqm-navmenu-link ui-btn ui-btn-icon-notext ui-corner-all ui-icon-bars ui-nodisc-icon ui-alt-icon ui-btn-left">Menu</a>
<a href="#" class="jqm-search-link ui-btn ui-btn-icon-notext ui-corner-all ui-icon-search ui-nodisc-icon ui-alt-icon ui-btn-right">Search</a>
</div><!-- /header -->

<div role="main" class="ui-content jqm-content">
<h1>External Widgets and Ajax Navigation</h1>
<p>Toolbars such as headers and footers, popups, panels, and many other widgets can be placed outside jQuery Mobile pages. When you use them outside pages, you should keep in mind that you must structure your application such that the widgets work together with the Ajax navigation.</p>
<p>When Ajax navigation is used in combination with the pushState plugin, the location bar is updated to show the URL of the file that has been loaded via Ajax. This implies that the user may copy that URL and launch your application with a starting URL that is different from the one you had intended. For example, if your application contains two or more documents and if the pages inside the documents contain links to one another, then both documents must be equipped to handle your application's startup. This means that you have to copy script references into the <code>&lt;head&gt;</code> section of each document, and you must also copy external shared widgets (widgets that are not inside the page, but which are seen and/or used from both pages) to the <code>&lt;body&gt;</code> section of both documents.</p>
<p>Since navigation from one page to the other involves retrieving the other page via Ajax, and since jQuery Mobile discards everything received in an Ajax call except for the first page, you may wish to optimize all your pages using server-side scripting to instruct the server to send the full document with headers, shared widgets, and page, whenever it is retrieved via an HTTP request, but to only send the page when the document is accessed via an Ajax request. This will save bandwidth for the user and reduce load times for your application.</p>
<p>You can use and if-statement similar to the following to wrap external widgets and the document's <code>&lt;head&gt;</code> section, causing them to be included in a HTTP request, but be excluded from an Ajax request:</p>
<pre><code>
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
// Markup inside the body of this if-statement is only sent with HTTP requests
</code></pre>
<a href="info.php" class="ui-btn ui-corner-all ui-shadow ui-btn-inline ui-icon-carat-r ui-btn-icon-right" data-ajax="false">Open Demo</a>
<div data-demo-html="./info.php?source=true" data-demo-js="./shared-widget-init.js"></div>
</div><!-- /content -->

<?php include( '../jqm-navmenu.php' ); ?>

<div data-role="footer" data-position="fixed" data-tap-toggle="false" class="jqm-footer">
<p>jQuery Mobile Demos version <span class="jqm-version"></span></p>
<p>Copyright 2014 The jQuery Foundation</p>
</div><!-- /footer -->

<?php include( '../jqm-search.php' ); ?>

</div><!-- /page -->

</body>
</html>
96 changes: 96 additions & 0 deletions demos/external-widgets/info.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest' || isset( $_GET['source'])) { ?>
<!DOCTYPE html>
<html>
<!-- This is an example of an HTML document equipped to handle the startup of an application. It
contains a <head> section common to all documents of the application, and its body contains
the markup for all widgets that will be shared across pages. The body for each document of the
application contains such markup. -->
<head>

<!-- The various documents reachable from within your navigation system must all have the
necessary header information to be able to launch your application. Nevertheless, the
server only needs to send this header information with the first request by the user. On
Ajax requests by the application, this information can be discarded server-side in order
to save bandwidth and to improve the time it takes to display a page. -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ajax optimized persistent toolbars - jQuery Mobile Demos</title>
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:300,400,700">
<link rel="stylesheet" href="../../css/themes/default/jquery.mobile.css">
<link rel="stylesheet" href="../_assets/css/jqm-demos.css">
<script src="../../external/jquery/jquery.js"></script>
<script src="../_assets/js/"></script>
<script src="../../js/"></script>

<!-- This script contains the code that is shared between all the documents of your
application. It is responsible for enhancing the shared widgets during your application's
startup. -->
<script id="shared-widget-init" src="shared-widget-init.js"></script>
</head>
<body>
<!-- The following panel is shared across all pages of the application, and must therefore be
copied to all the documents containing the application's pages. It will only be loaded
once with the first page. On subsequent page loads the existing widget will be reused. -->
<div id="shared-panel" data-role="panel" data-theme="a" data-position="right">
<ul id="nav-menu-links" data-role="listview">
<li data-icon="lock"><a href="#nav-menu" data-rel="popup">Login</a></li>
<li><a href="info.php" data-prefetch="true" data-transition="none">Info</a></li>
<li><a href="page-b.php" data-prefetch="true" data-transition="flip">Friends</a></li>
<li><a href="page-c.php" data-prefetch="true" data-transition="turn">Albums</a></li>
<li><a href="page-d.php" data-prefetch="true" data-transition="slide">Emails</a></li>
</ul>
</div>
<div id="shared-header" data-role="header" data-position="fixed" data-theme="a">
<!-- Shared header markup must be added to all documents of the application to ensure any
of them can serve as the start page. The server can be instructed to omit sending
this portion of the data whenever the request for the document is made via Ajax. -->
<a href="../toolbar/" data-rel="back" class="ui-btn ui-btn-left ui-alt-icon ui-nodisc-icon ui-corner-all ui-btn-icon-notext ui-icon-carat-l">Back</a>
<a href="#shared-panel" data-rel="panel" class="ui-btn ui-btn-right ui-alt-icon ui-nodisc-icon ui-corner-all ui-btn-icon-right ui-icon-navigation">Navigation</a>
<div data-role="popup" id="nav-menu" data-theme="a">
<form class="ui-content">
<label for="login-field">Login:</label>
<input id="login-field" name="login">
<label for="password-field">Password:</label>
<input type="password" id="password-field" name="password">
</form>
</div>
<h1>Fixed external header</h1>
</div><!-- /header -->
<?php } ?>
<!-- This is the actual page. It will always be sent to the client. -->
<div data-role="page" class="jqm-demos">

<div data-role="panel" id="local-panel" data-position="right">
<p>This is an example of a panel that is not shared across pages.</p>
</div>

<div class="ui-content jqm-content jqm-fullwidth" role="main">

<h1>External Widgets Demo</h1>
<p>This demo illustrates the use of widgets outside the page in an application consisting of multiple documents linked to each other via Ajax.</p>
<p>As you navigate from page to page using the navbar below or the popup opening via the button at the top right, the various pages of the demo are retrieved and displayed via Ajax, but the navigational elements which are outside the page, such as the header, the footer, the login panel, and the popup remain in the DOM.</p>
<p>There is a <a href="#local-panel">second panel</a> on this page which is not shared across pages.</p>

</div><!-- /content -->

</div><!-- /page -->

<?php if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest' || isset( $_GET['source'])) { ?>
<div id="shared-navbar" data-role="footer" data-position="fixed" data-theme="a">
<!-- Shared navbar markup must be added to all documents of the application to ensure any
of them can serve as the start page. The server can be instructed to omit sending
this portion of the data whenever the request for the document is made via Ajax. -->
<div data-role="navbar">
<ul>
<li><a href="info.php" data-prefetch="true" data-transition="none">Info</a></li>
<li><a href="page-b.php" data-prefetch="true" data-transition="flip">Friends</a></li>
<li><a href="page-c.php" data-prefetch="true" data-transition="turn">Albums</a></li>
<li><a href="page-d.php" data-prefetch="true" data-transition="slide">Emails</a></li>
</ul>
</div><!-- /navbar -->
</div><!-- /footer -->

</body>
</html>
<?php } ?>
Loading

0 comments on commit 958569a

Please sign in to comment.