Skip to content

Instantly share code, notes, and snippets.

@frequent
Created July 1, 2014 07:51
Show Gist options
  • Save frequent/cdefb7e9712137863ea5 to your computer and use it in GitHub Desktop.
Save frequent/cdefb7e9712137863ea5 to your computer and use it in GitHub Desktop.
JQM navigation gadget, which allows deeplinking and (uri encoded) query parameters
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global console, window, document, rJS, RSVP, $ */
(function (window, document, rJS) {
"use strict";
rJS(window)
/**
* Override navigation of jQuery Mobile and allow deeplinking. Missing
* pages will now be loaded and added to the DOM as new page-gadgets -
* and removed again once the user leaves them.
* @method render
* @param {Object} gadget Gadget object
*/
.declareMethod('render', function () {
var gadget = this;
// helper: render new gadget
function renderNewGadget(my_gadget, my_page_name, query) {
var page_list, page, data_url;
// render = create dynamic contents of page if applicable
if (my_gadget.render) {
my_gadget.render().fail(console.log);
}
// active page is last page
page_list = document.querySelectorAll("[data-role='page']");
page = page_list[page_list.length - 1];
// encode query parameters (Hello JQM!)
if (query) {
data_url = window.encodeURIComponent(my_page_name + "?" + query);
}
// go to the new page, overwrite page id with correct data_url
// so that pages can be based on a single "template" but have
// different urls
$.mobile.changePage("#" + page.id, {"dataUrl": data_url || null});
page.setAttribute("data-external-page", true);
page.setAttribute("data-url", data_url);
}
// helper: create new page gadget and integrate it in application
// this will fetch an HTML file whose body contains the page
// template.
function createNewGadget(my_target_name, query) {
return gadget.declareGadget(
"./" + my_target_name + ".html",
{"element": gadget.__element, "scope": my_target_name}
)
.then(function (new_gadget) {
return renderNewGadget(new_gadget, my_target_name, query);
})
.fail(console.log);
}
// helper: select page via data-url
function getElementByDataUrl(str) {
return document.querySelector("[data-url='" + str + "']");
}
// helper: override default JQM navigation, allow deeplinks
// NOTE: a deeplink is a transition to a page that is already in the
// DOM. If it is not in the DOM, the transition will be halted, the
// page will be fetched/generated and inserted before the transition
// is triggered again with correct parameters (data_url).
// NOTE: in case of a deeplink, the initial page stays un-enhanced
// and the user starts on the deeplinked page. However the initial
// page is still set as firstPage!
function navigationHandler(e, data) {
var target, has_query, clean_url, parsed_url, to_page, decoded, page,
go_home, dist, encoded, data_url, deeplink;
if (e && data) {
dist = $.mobile.navigate.history.initialDst;
if (typeof data.toPage === "string") {
to_page = data.toPage.split("#")[1];
decoded = decodeURIComponent(to_page || "documents").split("?");
// NOTE: hmhm....
encoded = window.location.hash.replace("#", "").replace("?", "%3F");
target = decoded[0];
has_query = decoded[1];
// transition to first page (unenhanced if coming from deeplink)
go_home = $.mobile.firstPage[0].id === to_page;
page = document.getElementById(target) ||
getElementByDataUrl(target) ||
getElementByDataUrl(to_page);
// deeplink
} else if (dist && window.location.hash !== "") {
// enable deeplinking
clean_url = window.location.href.split("#")[0];
parsed_url = $.mobile.path.parseUrl(clean_url);
decoded = decodeURIComponent(window.location.href).split("?");
target = decoded[0].split("#")[1];
has_query = decoded[1];
page = document.getElementById(target) ||
getElementByDataUrl(target);
deeplink = true;
// remove initialDist, otherwise closing popups
// will trigger double backward transition.
delete $.mobile.navigate.history.initialDst;
// correctly set index as first page in JQM history
$.mobile.navigate.history.stack[0].hash = "";
$.mobile.navigate.history.stack[0].url = clean_url;
$.mobile.path.documentUrl = parsed_url;
$.mobile.path.documentBase = parsed_url;
}
}
if (target) {
// page does not exist > make it. Load underlying document, set
// url to encoded string including query param #foo?baz=bam and
// hierarchy (#foo/baz/bam)
if (!page) {
e.preventDefault();
return createNewGadget(target, has_query)
.fail(console.log);
}
// page exists
if (has_query || go_home) {
if (deeplink) {
return;
}
if (has_query && to_page !== encoded) {
window.location.hash = to_page;
return;
}
if ((has_query && ($.mobile.activePage &&
$.mobile.activePage[0].id !== target))) {
data_url = window.encodeURIComponent(target + "?" + has_query);
$.mobile.changePage("#" + target, {"dataUrl": data_url});
} else {
return gadget.my_triggerEvent(page, "render")
.fail(console.log);
}
}
}
}
// helper: remove page and gadget when user leaves
function cleanupHandler(e) {
var page, is_enhanced, clean_scope, data_url;
page = e.target;
is_enhanced = $(page).data("mobilePage") || $(page).page("instance");
data_url = page.getAttribute("data-url");
clean_scope = window.decodeURIComponent(data_url).split("?")[0];
if (page.getAttribute('data-external-page')) {
if (is_enhanced) {
$(this).page('destroy').remove();
}
return gadget.dropGadget(clean_scope)
.fail(console.log);
}
}
// START:
$(document)
.on('pagebeforechange', navigationHandler)
.on('pagehide', 'div.ui-page', cleanupHandler);
})
/* ======================== METHODS TO EXPOSE ========================= */
/* ========================= METHODS NEEDED =========================== */
.declareAcquiredMethod("my_triggerEvent", "util_triggerEvent");
}(window, document, rJS));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment