Skip to content

Instantly share code, notes, and snippets.

@devote
Last active December 12, 2015 04:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devote/4717631 to your computer and use it in GitHub Desktop.
Save devote/4717631 to your computer and use it in GitHub Desktop.
The plugin includes support for older non-HTML5 browsers for the library: https://github.com/visionmedia/page.js
<!DOCTYPE html>
<html>
<head>
<title>Basic</title>
<script src="/history.js"></script>
<script src="/page.js"></script>
<script src="/history-plugin.js"></script>
</head>
<body>
<script>
if (!window.history.replaceState) {
alert('missing replaceState');
}
</script>
<h1>Basic</h1>
<p></p>
<ul>
<li><a href="./">/</a></li>
<li><a href="#whoop">#whoop</a></li>
<li><a href="./about">/about</a></li>
<li><a href="./contact">/contact</a></li>
<li><a href="./not-found?foo=bar">/not-found</a></li>
</ul>
<script>
// the "notfound" implements a catch-all
// with page('*', notfound). Here we have
// no catch-all, so page.js will redirect
// to the location of paths which do not
// match any of the following routes
page.base('/basic');
page('/', index);
page('/about', about);
page('/contact', contact);
page('*', notfound);
page();
function index() {
document.getElementsByTagName('p')[0]
.innerHTML = 'viewing index'; // IE unsupport textContent
}
function about() {
document.getElementsByTagName('p')[0]
.innerHTML = 'viewing about';
}
function contact() {
document.getElementsByTagName('p')[0]
.innerHTML = 'viewing contact';
}
function notfound() {
document.getElementsByTagName('p')[0]
.innerHTML = 'not found';
}
</script>
</body>
</html>
/**
* The plugin includes support for older non-HTML5 browsers for the library: https://github.com/visionmedia/page.js
*
* Documentation:
* Connect on your site library: https://github.com/devote/HTML5-History-API
* Next, connect the library: https://github.com/visionmedia/page.js
* After them, connect this the plugin.
*
* Good luck!
*
* Copyright 2013, Dmitriy Pakhtinov ( spb.piksel@gmail.com )
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Update: 06-02-2013
*/
(function(window, undefined) {
// get the version IE
var msie = +(((window.eval && eval("/*@cc_on 1;@*/") && /msie (\d+)/i.exec(navigator.userAgent)) || [])[1] || 0),
pageStart = page.start,
pageStop = page.stop,
pageDispatch = page.dispatch,
pageReplace = page.replace,
pageBase = page.base,
// overload 'page.dispatch' method had because of this line
location = history.location || window.location;
// IE incorrectly generates property '.pathname' in links
if (msie && (msie === 8 || msie === 9)) {
// fix for this
var descriptor = Object.getOwnPropertyDescriptor(HTMLAnchorElement.prototype, 'pathname');
Object.defineProperty(HTMLAnchorElement.prototype, "pathname", {
get: function() {
var pathname = descriptor.get.call(this);
return pathname.indexOf( "/" ) === 0 ? pathname : "/" + pathname;
},
set: descriptor.set
});
}
page.base = function() {
if (arguments.length) {
history.redirect(undefined, arguments[0].replace(/\/?$/, '/'));
}
pageBase.apply(page, arguments);
}
page.start = function(options) {
// temporarily replace method
page.replace = pReplace;
// check the availability of the event model w3c
if (!window.addEventListener) {
// temporarily add a method
window.addEventListener = function(type, callback) {
var context = type === 'click' ? document : window;
context.attachEvent("on" + type, callback.proxy = function(e){
var event = e || window.event;
event.defaultPrevented = event.returnValue === false;
var el = event.target = event.srcElement;
while (el && 'A' != el.nodeName) el = el.parentNode;
if (el && 'A' == el.nodeName) {
// emulate event object for work in IE7
event = {
which: 1,
target: {
nodeName: 'A',
href: el.href,
pathname: el.pathname.indexOf('/') === 0 ? el.pathname : '/' + el.pathname,
search: el.search,
hash: el.hash,
getAttribute: function() {return el.getAttribute('href')}
},
preventDefault: function() {window.event.returnValue = false;}
}
}
callback.call(window, event);
});
}
pageStart.apply(page, arguments);
window.addEventListener = undefined;
} else {
pageStart.apply(page, arguments);
}
page.replace = pageReplace;
}
page.stop = function() {
// check the availability of the event model w3c
if (!window.removeEventListener) {
// temporarily add a method
window.removeEventListener = function(type, callback) {
var context = type === 'click' ? document : window;
context.detachEvent("on" + type, callback.proxy);
delete callback.proxy;
}
pageStop.apply(page, arguments);
window.addEventListener = undefined;
} else {
pageStop.apply(page, arguments);
}
}
page.dispatch = function(ctx) {
var i = 0;
function next() {
var fn = page.callbacks[i++];
if (!fn) return unhandled(ctx);
fn(ctx, next);
}
next();
}
function unhandled(ctx) {
if (location.pathname + location.search == ctx.canonicalPath) return;
page.stop();
ctx.unhandled = true;
window.location = ctx.canonicalPath;
}
function pReplace() {
arguments[0] = location.pathname + location.search;
return pageReplace.apply(page, arguments);
}
})(window);
@alanning
Copy link

alanning commented Feb 5, 2013

Nice work!!

Thanks for writing this and for letting us know about it in the meteor-router IE Support issue thread [1].

I wanted to point out a few things:

  • The page.js basic example application seems to run as expected in IE9 when using this plugin ('viewing' works) however it still throws an error related to replaceState.
    I believe this is because the history polyfill sees that most of history is already supported so it doesn't load itself. But IE9 doesn't actually implement a 'replaceState' so technically the polyfill should check for that individually.

SCRIPT438: Object doesn't support property or method 'replaceState'
page.js, line 228 character 5

  • The forEach polyfill isn't used anywhere and, while useful, should probably not be part of the plugin. :-)

[1] tmeasday/meteor-router#33

@alanning
Copy link

alanning commented Feb 5, 2013

Hmm, it also doesn't seem to work in IE8. I'm using IE9's Browser Mode emulation so maybe that is the cause?
Browser Mode: IE8 Document Mode: IE8 Standards

Missing replaceState and throws this error:

SCRIPT438: Object doesn't support property or method 'addEventListener'
page.js, line 95 character 37

Can you have a try and see if it works for you, please?

Here is the modified basic example page that I am testing with:

<!DOCTYPE html>
<html>
  <head>
    <title>Basic</title>
    <script src="/history.js"></script>
    <script src="/page.js"></script>
    <script src="/history-plugin.js"></script>
  </head>
  <body>
    <script>
      if (!window.history.replaceState) {
        alert('missing replaceState');
      }
    </script>
    <h1>Basic</h1>
    <p></p>
    <ul>
      <li><a href="./">/</a></li>
      <li><a href="#whoop">#whoop</a></li>
      <li><a href="./about">/about</a></li>
      <li><a href="./contact">/contact</a></li>
      <li><a href="./not-found?foo=bar">/not-found</a></li>
    </ul>

    <script>
      // the "notfound" implements a catch-all
      // with page('*', notfound). Here we have
      // no catch-all, so page.js will redirect
      // to the location of paths which do not
      // match any of the following routes
      page.base('/basic');
      page('/', index);
      page('/about', about);
      page('/contact', contact);
      page();

      function index() {
        document.querySelector('p')
          .textContent = 'viewing index';
      }

      function about() {
        document.querySelector('p')
          .textContent = 'viewing about';
      }

      function contact() {
        document.querySelector('p')
          .textContent = 'viewing contact';
      }
    </script>
  </body>
</html>

@alanning
Copy link

alanning commented Feb 6, 2013

I'm wondering if the issues I'm seeing are due to the express server setup preventing access to the 'history' js files... Behavior matches what I would expect if your history polyfill isn't being loaded at all. But I'm not seeing any errors in the IE9 console...

@devote
Copy link
Author

devote commented Feb 6, 2013

@alanning: Hi,

Yes there were mistakes and imperfections, which I have corrected. Everything should work well in IE 7-8-9

Please test! Thank you!

@alanning
Copy link

alanning commented Feb 8, 2013

@devote:

Thanks for the update. Unfortunately, the page.js 'basic' test is still not working for me in IE9 or IE8.

There are no errors thrown in IE9 that I can see.

The steps to reproduce and the expected behavior are included below:

  1. git clone https://github.com/visionmedia/page.js.git

  2. cd page.js

  3. npm install

  4. copy 'history.js' and 'history-plugin.js' files to the 'examples/' directory
  5. include the code snippet below [1] in the 'examples/index.js' file between the 'Get page.js' and 'Get list of examples.' sections
  6. include the code snippet below [2] in the 'examples/basic/index.html' file, replacing the existing script include for page.js
  7. node examples

  8. open http://localhost:3000 in IE9
  9. click 'basic'. See 'viewing index'
  10. click '/about'. See 'viewing about'
  11. click '/contact'. See 'viewing contact'
  12. click browser's Back button. Should see 'viewing about' but still see 'viewing contact'

[1] examples/index.js

/**
 * GET history files
 */

app.get('/history.js', function(req, res){
  res.sendfile(join(__dirname, '.', 'history.js'));
});
app.get('/history-plugin.js', function(req, res){
  res.sendfile(join(__dirname, '.', 'history-plugin.js'));
});

[2] examples/basic/index.html

    <!--[if gte IE 7]>
    <script src="/history.js"></script>
    <![endif]-->
    <script src="/page.js"></script>
    <!--[if gte IE 7]>
    <script src="/history-plugin.js"></script>
    <![endif]-->

@devote
Copy link
Author

devote commented Feb 9, 2013

If your browser go to the address: http://localhost:3000/history.js you see the contents of the file history.js? If so, check what Content-Type the server sends along with the files. Because IE9 does not like bad Content-Type, for him to send Content-Type: application/javascript for JavaScript file(s)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment