Skip to content
This repository was archived by the owner on Oct 8, 2021. It is now read-only.

Commit 958569a

Browse files
author
Gabriel Schulhof
committed
Navigation: Add demo showing how to copy script references and widgets
Fixes gh-7362 Fixes gh-7087 Closes gh-7370
1 parent f1ba6a0 commit 958569a

File tree

16 files changed

+465
-361
lines changed

16 files changed

+465
-361
lines changed

demos/_assets/js/view-source.js

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,50 @@ function getSnippet( type, selector, source ) {
4848

4949
// First, try to grab a tag in this document
5050
if ( !$.mobile.path.isPath( selector ) ) {
51-
el = source.find( type + selector );
51+
el = source.find( ( "markup" === type ? "" : type ) + selector );
5252
// If this is not an embedded style, try a stylesheet reference
5353
if ( el.length === 0 && type === "style" ) {
5454
el = source.find( "link[rel='stylesheet']" + selector );
5555
}
56-
text = $( "<div></div>" ).append( el.contents().clone() ).html();
56+
57+
// Stringify each element and cache the string representation on the element. This helps us
58+
// avoid re-stringifying the element later. This, in turn, prevents us from re-stringifying
59+
// already enhanced elements, such as shared widgets outside the page, when the View Source
60+
// button is in the page, and the elements have already been enhanced when the View Source
61+
// button is created. This assumes, of course, that the first time we stringify an element
62+
// the element is not yet enhanced.
63+
el.each( function( index, singleElement ) {
64+
var whitespace,
65+
single = $( this ),
66+
singleText = single.jqmData( "viewSourceCachedData" );
67+
68+
if ( !singleText ) {
69+
singleText = $( "<div></div>" )
70+
.append( ( "markup" === type ? single : single.contents() ).clone() )
71+
.html();
72+
73+
// If we're dealing with markup, retrieve the initial indentation of the element so
74+
// we get proper indentation in the source view
75+
if ( type === "markup" ) {
76+
if ( this.previousSibling && this.previousSibling.nodeType === 3 ) {
77+
whitespace = $( "<div>" )
78+
.append( $( this.previousSibling ).clone() )
79+
.html()
80+
.match( /\n(\s*)$/ );
81+
if ( whitespace && whitespace.length > 1 ) {
82+
singleText = whitespace[ 1 ] + singleText;
83+
}
84+
}
85+
}
86+
single.jqmData( "viewSourceCachedData", singleText );
87+
}
88+
89+
text = text +
90+
91+
// Separate the text for multiple elements with a newline
92+
( index > 0 ? "\n" : "" ) +
93+
singleText;
94+
});
5795
if ( !text ) {
5896
text = "";
5997
selector = el.attr( "href" ) || el.attr( "src" ) || "";
@@ -91,7 +129,7 @@ $( document ).bind( "pagebeforechange", function( e, data ) {
91129

92130
attachPopupHandler( popup, sources );
93131
popup
94-
.appendTo( $.mobile.activePage )
132+
.appendTo( "body" )
95133
.popup()
96134
.bind( "popupafterclose", function() {
97135
popup.remove();
@@ -136,7 +174,7 @@ $.fn.viewSourceCode = function() {
136174
if ( self.attr( "data-demo-html" ) === "true" ) {
137175
data = self.html();
138176
} else {
139-
data = $( "<div></div>" ).append( $( self.attr( "data-demo-html" ) ).clone() ).html();
177+
data = getSnippet( "markup", self.attr( "data-demo-html" ), $( document ) );
140178
}
141179
sources.push( { title: "HTML", theme: "c", brush: "xml", data: fixData( data ) } );
142180
}
@@ -174,22 +212,12 @@ $( document ).on( "pagebeforecreate", "[data-role='page']", function() {
174212
SyntaxHighlighter.defaults['auto-links'] = false;
175213
});
176214

177-
$( document ).on( "pagecreate", function( e ) {
178-
// prevent page scroll while scrolling source code
179-
$( document ).on( "mousewheel", ".jqm-view-source .ui-collapsible-content", function( event, delta ) {
180-
if ( delta > 0 && $( this ).scrollTop() === 0 ) {
181-
event.preventDefault();
182-
} else if ( delta < 0 && $( this ).scrollTop() === $( this ).get( 0 ).scrollHeight - $( this ).innerHeight() ) {
183-
event.preventDefault();
184-
}
185-
});
186-
215+
$( document )
187216
// reposition when switching between html / js / css
188-
$( e.target ).delegate( ".jqm-view-source .ui-collapsible", "expand", function() {
217+
.on( "collapsibleexpand", ".jqm-view-source .ui-collapsible", function() {
189218
$( this ).parents( ":mobile-popup" ).popup( "reposition", { positionTo: "window" } );
190-
});
191-
192-
$( e.target ).delegate( ".jqm-view-source", "popupbeforeposition", function() {
219+
})
220+
.on( "popupbeforeposition", ".jqm-view-source", function() {
193221
// max height: screen height - tolerance (2*30px) - 42px for each collapsible heading
194222
var x = $( this ).find( ".ui-collapsible" ).length,
195223
maxHeight = $.mobile.getScreenHeight() - 60 - ( x * 42 );
@@ -209,8 +237,17 @@ $( document ).on( "pagecreate", function( e ) {
209237
$( line ).height( height );
210238
}
211239
});
240+
})
241+
.on( "pagecreate", function( e ) {
242+
// prevent page scroll while scrolling source code
243+
$( document ).on( "mousewheel", ".jqm-view-source .ui-collapsible-content", function( event, delta ) {
244+
if ( delta > 0 && $( this ).scrollTop() === 0 ) {
245+
event.preventDefault();
246+
} else if ( delta < 0 && $( this ).scrollTop() === $( this ).get( 0 ).scrollHeight - $( this ).innerHeight() ) {
247+
event.preventDefault();
248+
}
249+
});
212250
});
213-
});
214251

215252
/**
216253
* SyntaxHighlighter

demos/external-widgets/index.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>External Widgets - jQuery Mobile Demos</title>
7+
<link rel="stylesheet" href="../../css/themes/default/jquery.mobile.css">
8+
<link rel="stylesheet" href="../_assets/css/jqm-demos.css">
9+
<link rel="shortcut icon" href="../favicon.ico">
10+
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:300,400,700">
11+
<script src="../../external/jquery/jquery.js"></script>
12+
<script src="../_assets/js/"></script>
13+
<script src="../../js/"></script>
14+
</head>
15+
<body>
16+
<div data-role="page" class="jqm-demos" data-quicklinks="true">
17+
18+
<div data-role="header" class="jqm-header">
19+
<h2><a href="../" title="jQuery Mobile Demos home"><img src="../_assets/img/jquery-logo.png" alt="jQuery Mobile"></a></h2>
20+
<p><span class="jqm-version"></span> Demos</p>
21+
<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>
22+
<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>
23+
</div><!-- /header -->
24+
25+
<div role="main" class="ui-content jqm-content">
26+
<h1>External Widgets and Ajax Navigation</h1>
27+
<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>
28+
<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>
29+
<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>
30+
<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>
31+
<pre><code>
32+
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
33+
// Markup inside the body of this if-statement is only sent with HTTP requests
34+
</code></pre>
35+
<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>
36+
<div data-demo-html="./info.php?source=true" data-demo-js="./shared-widget-init.js"></div>
37+
</div><!-- /content -->
38+
39+
<?php include( '../jqm-navmenu.php' ); ?>
40+
41+
<div data-role="footer" data-position="fixed" data-tap-toggle="false" class="jqm-footer">
42+
<p>jQuery Mobile Demos version <span class="jqm-version"></span></p>
43+
<p>Copyright 2014 The jQuery Foundation</p>
44+
</div><!-- /footer -->
45+
46+
<?php include( '../jqm-search.php' ); ?>
47+
48+
</div><!-- /page -->
49+
50+
</body>
51+
</html>

demos/external-widgets/info.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest' || isset( $_GET['source'])) { ?>
2+
<!DOCTYPE html>
3+
<html>
4+
<!-- This is an example of an HTML document equipped to handle the startup of an application. It
5+
contains a <head> section common to all documents of the application, and its body contains
6+
the markup for all widgets that will be shared across pages. The body for each document of the
7+
application contains such markup. -->
8+
<head>
9+
10+
<!-- The various documents reachable from within your navigation system must all have the
11+
necessary header information to be able to launch your application. Nevertheless, the
12+
server only needs to send this header information with the first request by the user. On
13+
Ajax requests by the application, this information can be discarded server-side in order
14+
to save bandwidth and to improve the time it takes to display a page. -->
15+
<meta charset="utf-8">
16+
<meta name="viewport" content="width=device-width, initial-scale=1">
17+
<title>Ajax optimized persistent toolbars - jQuery Mobile Demos</title>
18+
<link rel="shortcut icon" href="../favicon.ico">
19+
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:300,400,700">
20+
<link rel="stylesheet" href="../../css/themes/default/jquery.mobile.css">
21+
<link rel="stylesheet" href="../_assets/css/jqm-demos.css">
22+
<script src="../../external/jquery/jquery.js"></script>
23+
<script src="../_assets/js/"></script>
24+
<script src="../../js/"></script>
25+
26+
<!-- This script contains the code that is shared between all the documents of your
27+
application. It is responsible for enhancing the shared widgets during your application's
28+
startup. -->
29+
<script id="shared-widget-init" src="shared-widget-init.js"></script>
30+
</head>
31+
<body>
32+
<!-- The following panel is shared across all pages of the application, and must therefore be
33+
copied to all the documents containing the application's pages. It will only be loaded
34+
once with the first page. On subsequent page loads the existing widget will be reused. -->
35+
<div id="shared-panel" data-role="panel" data-theme="a" data-position="right">
36+
<ul id="nav-menu-links" data-role="listview">
37+
<li data-icon="lock"><a href="#nav-menu" data-rel="popup">Login</a></li>
38+
<li><a href="info.php" data-prefetch="true" data-transition="none">Info</a></li>
39+
<li><a href="page-b.php" data-prefetch="true" data-transition="flip">Friends</a></li>
40+
<li><a href="page-c.php" data-prefetch="true" data-transition="turn">Albums</a></li>
41+
<li><a href="page-d.php" data-prefetch="true" data-transition="slide">Emails</a></li>
42+
</ul>
43+
</div>
44+
<div id="shared-header" data-role="header" data-position="fixed" data-theme="a">
45+
<!-- Shared header markup must be added to all documents of the application to ensure any
46+
of them can serve as the start page. The server can be instructed to omit sending
47+
this portion of the data whenever the request for the document is made via Ajax. -->
48+
<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>
49+
<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>
50+
<div data-role="popup" id="nav-menu" data-theme="a">
51+
<form class="ui-content">
52+
<label for="login-field">Login:</label>
53+
<input id="login-field" name="login">
54+
<label for="password-field">Password:</label>
55+
<input type="password" id="password-field" name="password">
56+
</form>
57+
</div>
58+
<h1>Fixed external header</h1>
59+
</div><!-- /header -->
60+
<?php } ?>
61+
<!-- This is the actual page. It will always be sent to the client. -->
62+
<div data-role="page" class="jqm-demos">
63+
64+
<div data-role="panel" id="local-panel" data-position="right">
65+
<p>This is an example of a panel that is not shared across pages.</p>
66+
</div>
67+
68+
<div class="ui-content jqm-content jqm-fullwidth" role="main">
69+
70+
<h1>External Widgets Demo</h1>
71+
<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>
72+
<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>
73+
<p>There is a <a href="#local-panel">second panel</a> on this page which is not shared across pages.</p>
74+
75+
</div><!-- /content -->
76+
77+
</div><!-- /page -->
78+
79+
<?php if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest' || isset( $_GET['source'])) { ?>
80+
<div id="shared-navbar" data-role="footer" data-position="fixed" data-theme="a">
81+
<!-- Shared navbar markup must be added to all documents of the application to ensure any
82+
of them can serve as the start page. The server can be instructed to omit sending
83+
this portion of the data whenever the request for the document is made via Ajax. -->
84+
<div data-role="navbar">
85+
<ul>
86+
<li><a href="info.php" data-prefetch="true" data-transition="none">Info</a></li>
87+
<li><a href="page-b.php" data-prefetch="true" data-transition="flip">Friends</a></li>
88+
<li><a href="page-c.php" data-prefetch="true" data-transition="turn">Albums</a></li>
89+
<li><a href="page-d.php" data-prefetch="true" data-transition="slide">Emails</a></li>
90+
</ul>
91+
</div><!-- /navbar -->
92+
</div><!-- /footer -->
93+
94+
</body>
95+
</html>
96+
<?php } ?>

0 commit comments

Comments
 (0)