diff --git a/js/push.js b/js/push.js index f72282c7d..9fed8ccd1 100644 --- a/js/push.js +++ b/js/push.js @@ -29,13 +29,6 @@ fade : 'fade' }; - var bars = { - bartab : '.bar-tab', - barnav : '.bar-nav', - barfooter : '.bar-footer', - barheadersecondary : '.bar-header-secondary' - }; - var cacheReplace = function (data, updates) { PUSH.id = data.id; if (updates) { @@ -174,27 +167,11 @@ }); } - if (transitionFromObj.transition) { - activeObj = extendWithDom(activeObj, '.content', activeDom.cloneNode(true)); - for (key in bars) { - if (bars.hasOwnProperty(key)) { - barElement = document.querySelector(bars[key]); - if (activeObj[key]) { - swapContent(activeObj[key], barElement); - } else if (barElement) { - barElement.parentNode.removeChild(barElement); - } - } - } - } - - swapContent( - (activeObj.contents || activeDom).cloneNode(true), - document.querySelector('.content'), - transition, function() { - triggerStateChange(); - } - ); + TRANSITION(activeDom.cloneNode(true), + transitionFromObj.transition, + function() { + triggerStateChange(); + }); PUSH.id = id; @@ -209,14 +186,6 @@ var key; var xhr = PUSH.xhr; - options.container = options.container || options.transition ? document.querySelector('.content') : document.body; - - for (key in bars) { - if (bars.hasOwnProperty(key)) { - options[key] = options[key] || document.querySelector(bars[key]); - } - } - if (xhr && xhr.readyState < 4) { xhr.onreadystatechange = noop; xhr.abort(); @@ -279,20 +248,7 @@ document.title = data.title; } - if (options.transition) { - for (key in bars) { - if (bars.hasOwnProperty(key)) { - barElement = document.querySelector(bars[key]); - if (data[key]) { - swapContent(data[key], barElement); - } else if (barElement) { - barElement.parentNode.removeChild(barElement); - } - } - } - } - - swapContent(data.contents, options.container, options.transition, function () { + TRANSITION(data.contents, options.transition, function () { cacheReplace({ id : options.id || +new Date(), url : data.url, @@ -303,6 +259,7 @@ triggerStateChange(); }); + if (!options.ignorePush && window._gaq) { _gaq.push(['_trackPageview']); // google analytics } @@ -319,78 +276,6 @@ // PUSH helpers // ============ - var swapContent = function (swap, container, transition, complete) { - var enter; - var containerDirection; - var swapDirection; - - if (!transition) { - if (container) { - container.innerHTML = swap.innerHTML; - } else if (swap.classList.contains('content')) { - document.body.appendChild(swap); - } else { - document.body.insertBefore(swap, document.querySelector('.content')); - } - } else { - enter = /in$/.test(transition); - - if (transition === 'fade') { - container.classList.add('in'); - container.classList.add('fade'); - swap.classList.add('fade'); - } - - if (/slide/.test(transition)) { - swap.classList.add('sliding-in', enter ? 'right' : 'left'); - swap.classList.add('sliding'); - container.classList.add('sliding'); - } - - container.parentNode.insertBefore(swap, container); - } - - if (!transition) { - complete && complete(); - } - - if (transition === 'fade') { - container.offsetWidth; // force reflow - container.classList.remove('in'); - var fadeContainerEnd = function () { - container.removeEventListener('webkitTransitionEnd', fadeContainerEnd); - swap.classList.add('in'); - swap.addEventListener('webkitTransitionEnd', fadeSwapEnd); - }; - var fadeSwapEnd = function () { - swap.removeEventListener('webkitTransitionEnd', fadeSwapEnd); - container.parentNode.removeChild(container); - swap.classList.remove('fade'); - swap.classList.remove('in'); - complete && complete(); - }; - container.addEventListener('webkitTransitionEnd', fadeContainerEnd); - - } - - if (/slide/.test(transition)) { - var slideEnd = function () { - swap.removeEventListener('webkitTransitionEnd', slideEnd); - swap.classList.remove('sliding', 'sliding-in'); - swap.classList.remove(swapDirection); - container.parentNode.removeChild(container); - complete && complete(); - }; - - container.offsetWidth; // force reflow - swapDirection = enter ? 'right' : 'left'; - containerDirection = enter ? 'left' : 'right'; - container.classList.add(containerDirection); - swap.classList.remove(swapDirection); - swap.addEventListener('webkitTransitionEnd', slideEnd); - } - }; - var triggerStateChange = function () { var e = new CustomEvent('push', { detail: { state: getCached(PUSH.id) }, @@ -419,29 +304,6 @@ window.location.replace(url); }; - var extendWithDom = function (obj, fragment, dom) { - var i; - var result = {}; - - for (i in obj) { - if (obj.hasOwnProperty(i)) { - result[i] = obj[i]; - } - } - - Object.keys(bars).forEach(function (key) { - var el = dom.querySelector(bars[key]); - if (el) { - el.parentNode.removeChild(el); - } - result[key] = el; - }); - - result.contents = dom.querySelector(fragment); - - return result; - }; - var parseXHR = function (xhr, options) { var head; var body; @@ -468,11 +330,7 @@ var text = 'innerText' in data.title ? 'innerText' : 'textContent'; data.title = data.title && data.title[text].trim(); - if (options.transition) { - data = extendWithDom(data, '.content', body); - } else { - data.contents = body; - } + data.contents = body; return data; }; diff --git a/js/transitions.js b/js/transitions.js new file mode 100644 index 000000000..eef8f83e2 --- /dev/null +++ b/js/transitions.js @@ -0,0 +1,125 @@ +!(function () { + 'use strict'; + + // Ratchet's layout includes fixed position headers & footers that should always + // appear before the main .content div within + // + // These fixed bars will have new content swapped in, ignoring any + // transitions (slide-in, slide-out & fade). + // + // These following selectors define which elements are transitioned with + // simple DOM replacement and are always immediate children of + var barSelectors = [ + '.bar-tab', + '.bar-nav', + '.bar-footer', + '.bar-header-secondary' + ]; + + // Other than any fixed bars, '.content' should be the only other child of body + var contentSelector = '.content'; + + // For any bar elements in `newMarkup`, either: + // * replace an existing bar elements with new content + // * add new bar elements when an existing one isn't present + // * remove any bar elements not found in `newMarkup` + var updateBars = function (newMarkup) { + for (var i=0; i < barSelectors.length; i++ ) { + var selector = barSelectors[i]; + var newBar = newMarkup.querySelector(selector); + var existingBar = document.querySelector(selector); + + if (newBar) { + displayBar(newBar, existingBar); + } else if (existingBar) { + existingBar.parentNode.removeChild(existingBar); + } + } + }; + + var displayBar = function (bar, container) { + if (container) { + container.innerHTML = bar.innerHTML; + } else { + // per Ratchet's CSS, bar elements must be the first thing in + // here we assume `.content` is an immediate child of + document.body.insertBefore(bar, document.querySelector(contentSelector)); + } + }; + + var transitionContent = function (swap, container, transition, complete) { + var enter; + var containerDirection; + var swapDirection; + + enter = /in$/.test(transition); + + if (transition === 'fade') { + container.classList.add('in'); + container.classList.add('fade'); + swap.classList.add('fade'); + } + + if (/slide/.test(transition)) { + swap.classList.add('sliding-in', enter ? 'right' : 'left'); + swap.classList.add('sliding'); + container.classList.add('sliding'); + } + + container.parentNode.insertBefore(swap, container); + complete && complete(); + + if (transition === 'fade') { + container.offsetWidth; // force reflow + container.classList.remove('in'); + var fadeContainerEnd = function () { + container.removeEventListener('webkitTransitionEnd', fadeContainerEnd); + swap.classList.add('in'); + swap.addEventListener('webkitTransitionEnd', fadeSwapEnd); + }; + var fadeSwapEnd = function () { + swap.removeEventListener('webkitTransitionEnd', fadeSwapEnd); + container.parentNode.removeChild(container); + swap.classList.remove('fade'); + swap.classList.remove('in'); + complete && complete(); + }; + container.addEventListener('webkitTransitionEnd', fadeContainerEnd); + } + + if (/slide/.test(transition)) { + var slideEnd = function () { + swap.removeEventListener('webkitTransitionEnd', slideEnd); + swap.classList.remove('sliding', 'sliding-in'); + swap.classList.remove(swapDirection); + container.parentNode.removeChild(container); + complete && complete(); + }; + + container.offsetWidth; // force reflow + swapDirection = enter ? 'right' : 'left'; + containerDirection = enter ? 'left' : 'right'; + container.classList.add(containerDirection); + swap.classList.remove(swapDirection); + swap.addEventListener('webkitTransitionEnd', slideEnd); + } + }; + + // `contents` must include an element with the class 'content' and can + // optionally include a number of Ratchet bar elements (see `barSelectors`) + var TRANSITION = function (contents, transition, complete) { + if (transition) { + updateBars(contents); + + var existingContentDiv = document.querySelector(contentSelector); + var newContentDiv = contents.querySelector(contentSelector); + transitionContent(newContentDiv, existingContentDiv, + transition, complete); + } else { + document.body.innerHTML = contents.innerHTML; + complete && complete(); + } + }; + + window.TRANSITION = TRANSITION; +}());