Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Вынесен polyfill для HTML5 History API #171

Merged
merged 3 commits into from Oct 22, 2013
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions doc/ns.history.md
@@ -0,0 +1,35 @@
# ns.History

В noscript для смены URL в адресной строке используется HTML5 History API, который не поддерживается [в IE раньше 10](http://caniuse.com/#feat=history).

## Polyfill для IE

В качестве полифилла можно использовать [devote/HTML5-History-API](https://github.com/devote/HTML5-History-API). Скрипт предоставляет стандартизированное API и будет использовать смену хеш-фрагмента URL для навигации.

/notes/141 -> /#/notes/141

Кроме подключения самого скрипта на страницу нужно проделать небольшую работу:

1. Организовать редирект до старта приложения:

```js
if (window.history.emulate) {
// Тут может произойти смена URL и перезагрузка, поэтому какие-нибудь
// ручки до редиректа запрашивать бессмысленно.
window.history.redirect();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.redirect() полезно использовать и в новых браузерах, что бы было автоматическое преобразование из ссылок вида: /#/notes/141 в ссылки вида: /notes/141

Но вам виднее конечно.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

О, спасибо за комментарий, это правда полезнее. Да и вообще, спасибо за библиотеку.


ns.init();
```

2. Переопределить вычисление текущего URL приложения:

```js
var history = window.history;

if (history.emulate) {
ns.page.getCurrentUrl = function() {
return history.location.pathname + history.location.search;
};
}
```
4 changes: 2 additions & 2 deletions src/ns.action.js
Expand Up @@ -198,8 +198,8 @@ ns.action._process = function(e) {
return true;
}

// если host ссылки не равен нашему хосту
if (target.host != window.location.host) {
// если hostname ссылки не равен нашему хосту
if (target.hostname != window.location.hostname) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут поправил ошибку в IE9, там target.host возвращается с портом, а в window.location.host нет порта для 80 и 443.

return true;
}

Expand Down
128 changes: 34 additions & 94 deletions src/ns.history.js
@@ -1,104 +1,44 @@
(function() {

/**
* Объект для работы с историей и адресной строкой браузера.
*
* Методы:
*
* `ns.history.pushState(url)`
* Меняет URL в адресной строке, запоминая его в истории браузера.
* `ns.history.replaceState(url)`
* Меняет URL в адресной строке, заменяя текущую запись в истории браузера.
* `ns.history.adapt()`
* Адаптирует хешовые URL для браузеров, поддерживающих HTML5 History API,
* выполняя трансформации типа `/#/message/123/` в `/message/123/`.
* `ns.history.legacy`
* {Boolean}
* Флаг использования HTML5 History API или отката на `hashchange`.
* @namespace
*/
ns.history = (function(window, ns) {

var history = window.history;
var loc = window.location;

var legacy = !(history.pushState && history.replaceState);

// API для браузеров с поддержкой HTML5 History API.
if (!legacy) {
return {
pushState: function(url, title) {
history.pushState(null, title || ns.page.title(url), url);
},
replaceState: function(url, title) {
history.replaceState(null, title || ns.page.title(url), url);
},

// Редирект с хешового урла на его полноценный аналог.
adapt: function() {
var hash = loc.hash.substr(1);

if (hash.length) {

var hashRoute = ns.router(hash);
if (hashRoute.page !== ns.R.NOT_FOUND && hashRoute.page !== ns.R.REDIRECT) {

// TODO: При добавлении способа задания корневого урла заменить
// слеш на нужное свойство/метод.
if (loc.pathname === '/' || ns.router(loc.pathname).page === ns.R.NOT_FOUND) {
// Если в хеше уже есть какие-то query параметры,
// текущие надо к ним прибавить.
var search = hash.indexOf('?') !== -1 ? loc.search.replace(/^\?/, '&') : loc.search;
this.replaceState(hash + search);
}
}
}
ns.history = {};

window.addEventListener('popstate', function(e) {
// прибиваем событие, чтобы не дергалась адресная строка
e.preventDefault();
e.stopPropagation();
ns.history.init = function() {
$(window).on('popstate', function(e) {
// прибиваем событие, чтобы не дергалась адресная строка
e.preventDefault();
e.stopPropagation();

ns.page.go('', true);
}, false);
ns.history.onpopstate(e);
});
};

// Здесь `ns.page.go` можно не вызывать, потому что после `ns.init`
// `ns.page.go` все равно вызывается.
},
legacy: false
};

// Откат на `hashchange`.
} else {
// Редирект с текущего полноценного урла на его хешовый аналог.
//
// TODO: При добавлении способа задания корневого урла заменить
// слеш на нужное свойство/метод.
if ((loc.pathname + loc.search) !== '/') {
return loc.replace('/#' + loc.pathname + loc.search);
}

// Заставляет `hashchange` игнорировать смену URL через вызов legacy
// `pushState` или `replaceState`.
var ignore = false;
ns.history.pushState = function(url, title) {
if (isFunction(window.history.pushState)) {
window.history.pushState(null, title || ns.page.title(url), url);
}
};

window.addEventListener('hashchange', function() {
if (!ignore) {
ns.page.go();
}
ns.history.replaceState = function(url, title) {
if (isFunction(window.history.replaceState)) {
window.history.replaceState(null, title || ns.page.title(url), url);
}
};

ignore = false;
}, false);
ns.history.onpopstate = function() {
ns.page.go('', true);
};

return {
pushState: function(url) {
ignore = true;
loc.hash = url;
},
replaceState: function(url) {
ignore = true;
loc.replace(loc.pathname + loc.search + '#' + url);
},
adapt: no.nop,
legacy: true
};
}
/**
* Проверяет, является ли переданный объект функцией.
* @param {Function} fn
* @return {Boolean}
*/
function isFunction(fn) {
return 'function' == typeof fn;
}

}(window, ns));
})();
3 changes: 1 addition & 2 deletions src/ns.js
Expand Up @@ -112,9 +112,8 @@ ns.tmpl = function(json, mode, module) {
ns.init = function() {
ns.action.init();
ns.router.init();
ns.history.init();
ns.initMainView();

ns.history.adapt();
};

/**
Expand Down
13 changes: 9 additions & 4 deletions src/ns.page.js
Expand Up @@ -18,7 +18,7 @@ ns.page._lastUrl = '';

/**
* Осуществляем переход по ссылке.
* @param {String} [url=location.pathname + location.search]
* @param {String} [url=ns.page.getCurrentUrl()]
* @param {Boolean} [preventAddingToHistory=false] Не добавлять урл в историю браузера.
* @return {no.Promise}
*/
Expand All @@ -29,9 +29,7 @@ ns.page.go = function(url, preventAddingToHistory) {
return no.Promise.rejected('transaction');
}

var loc = window.location;

url = url || (ns.history.legacy ? loc.hash.substr(1) : (loc.pathname + loc.search));
url = url || ns.page.getCurrentUrl();

// возможность заблокировать переход
if (!ns.page.block.check(url)) {
Expand Down Expand Up @@ -165,6 +163,13 @@ ns.page.getDefaultUrl = function() {
return ns.router.url('/');
};

/**
* Calculates current application url, fed as default value for `ns.page.go`.
*/
ns.page.getCurrentUrl = function() {
return window.location.pathname + window.location.search;
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно просто написать:

ns.page.getCurrentUrl = function() {
    var location = window.history.location || window.location;
    return location.pathname + location.search;
}

и не нужно будет писать вот этого кода:

if (history.emulate) {
    ns.page.getCurrentUrl = function() {
        return history.location.pathname + history.location.search;
    };
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Дело в том, что мы не включаем HTML5-History-API в noscript по умолчанию: страница ns.history.md — это как раз краткое руководство по подключению.

window.location.history появляется, насколько я понял, при подключении polyfill'а, значит код window.history.location || window.location должен лежать как минимум в приложении. Ну а там, как мне кажется, яснее один раз определиться, как мы считаем текущий URL.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не вижу ничего ужасного в добавлении условия window.history.location || window.location в ваш код, это не 500 строк кода, а лишь одна. Не мешающая работе, но дающая возможность другим не задумываться о том какие нужно делать манипуляции при подключении полифила HTML5-History-API.

Но это лишь мое мнение, решать конечно автору приложения о котором тут ведется речь)


/**
* Object to work with application history.
* @namespace
Expand Down
7 changes: 3 additions & 4 deletions test/index.html
Expand Up @@ -39,17 +39,17 @@
<script src="../src/ns.consts.events.js"></script>
<script src="../src/ns.dom.js"></script>
<script src="../src/ns.entityify.js"></script>
<script src="../src/ns.history.js"></script>
<script src="../src/ns.http.js"></script>
<script src="../src/ns.log.js"></script>
<script src="../src/ns.object.js"></script>

<script src="../src/ns.action.js"></script>
<script src="../src/ns.box.js"></script>
<script src="../src/ns.layout.js"></script>
<script src="../src/ns.model.js"></script>
<script src="../src/ns.modelCollection.js"></script>
<script src="../src/ns.layout.js"></script>
<script src="../src/ns.log.js"></script>
<script src="../src/ns.page.js"></script>
<script src="../src/ns.history.js"></script>
<script src="../src/ns.request.js"></script>
<script src="../src/ns.router.js"></script>
<script src="../src/ns.update.js"></script>
Expand All @@ -69,7 +69,6 @@
<script src="spec/ns.action.js"></script>
<script src="spec/ns.box.js"></script>
<script src="spec/ns.entityify.js"></script>
<script src="spec/ns.history.js"></script>
<script src="spec/ns.layout.js"></script>
<script src="spec/ns.model.js"></script>
<script src="spec/ns.modelCollection.js"></script>
Expand Down