drupal 7 and history.js
DESCRIPTION
Drupal 7 and History.js Presentation, for Drupal Camp Kiev 2013TRANSCRIPT
Зачем ?
• Иметь статичную зону контента.
• Иметь возможность навигации Back или Forward в истории browser-a
• Избежать несоответствий при использовании родного window.History
Составляющие
1. Ajaxify Pages
2. History.js
3. Libraries
History.js (Введение)
• Придерживается HTML5 History API насколько это возможно;
• Кросбраузерность для всех HTML5 браузеров;
• Поддержка HTML4 браузеров;• Адаптация link-ов в зависимости от
HTML5 или HTML4;• Поддержка javaScript framerork-ов как
"jQuery", "MooTools", "Prototype", "Zepto".
History.js (Где это работает ?)
HTML5 Browsers
HTML4 Browsers
HTML5 BrowsersFirefox 4+ Safari 5.0+
Chrome 8+ Safari iOS 4.3+
Opera 11.5
IE 6, 7, 8, 9 Safari 4
Firefox 3 Safari iOS 4.2, 4.1, 4.0, 3.2
Opera 10, 11.0
History.js (Как это работает ?)
(function(window,undefined){ var History = window.History; // Bind to StateChange Event History.Adapter.bind(window,'statechange',function(){ // Note: We are using statechange instead of popstate
var State = History.getState(); // Note: We are using History.getState() instead of event.state
History.log(State.data, State.title, State.url); }); // Change our States History.pushState({state:1}, "State 1", "?state=1"); // logs {state:1}, "State 1", "?state=1"
History.pushState({state:2}, "State 2", "?state=2"); // logs {state:2}, "State 2", "?state=2"
History.replaceState({state:3}, "State 3", "?state=3"); // logs {state:3}, "State 3", "?state=3"
History.pushState(null, null, "?state=4"); // logs {}, '', "?state=4"
History.back(); // logs {state:3}, "State 3", "?state=3"
History.back(); // logs {state:1}, "State 1", "?state=1"
History.back(); // logs {}, "Home Page", "?"
History.go(2); // logs {state:3}, "State 3", "?state=3"
})(window);
History.js (Разбор полётов)
History.Adapter.bind(window,'statechange',function(){}); - Привязка к событию
(Срабатывает, когда состояние страницы изменилось).
History.getState() - Получает текущее состояние браузера, возвращает
объект с data, title и url.
History.pushState({state:1}, "State 1", "?state=1") - Вталкивает новое "состояния"
в браузер; data может быть нулевыми или объектом, title может быть
нулевым или строкой, URL должен быть строкой.
History.js (Разбор полётов)
History.replaceState({state:3}, "State 3", "?state=3") - Заменяет существующее "состояние" новым; (В случае замены, будет утеряно "состояние" предшествующее нынешнему)
History.back() - Назад в историю на 1 шаг;
History.go(2) - Вперед в историю на 2 шага;
History.log(...) - Вывод сообщения в консоли;
History.js (Что еще?)
События :
1. 'statechange' - Срабатывает, когда состояние страницы изменилось;
2. 'onanchrorchange' - Срабатывает, когда "якорь" страницы изменился;
History.replaceState(data,title,url) - Заменяет текущее состояние страницы новым.
History.getHash() - Возвращает значение URL после # (hash); (HTML 4)
History.Adapter.trigger(element,event) - Триггер события;
History.Adapter.onDomLoad(callback) - onDomLoad связующее;
History.forward() - шаг впреред в историю браузера;
History.debug(...) - тоже что и History.log(...), но работает только если History.debug.enable === true
History.js в Drupal-e
History.js и Drupal (Привязка к URL)
1. Drupal.behaviors.ajaxify_pages = {};2. Drupal.behaviors.ajaxify_pages.attach = function(context, settings) {3. $(Drupal.settings.ajaxify_pages.links_selector, context).each(function() {4. var to_avoid = Drupal.settings.ajaxify_pages.links_to_avoid;5. var links = $(this).attr("href");6. var links_obj = $(this);7. if (typeof links !== 'undefined') {8. $.each(to_avoid, function(index, value) {9. if (links.indexOf(value) < 0 && links !== '/') {10. var href = links_obj.addClass('ajaxify-pages-processed');11. href = decodeURIComponent(href);12. if (href.indexOf("<script>") > 0) {13. return false;14. }15. if (typeof href !== 'undefined') {16. $(this).click(function() {17. History.pushState(null, null, href);18. return false;19. }); }} }); } });
History.js и Drupal (Привязка к URL)
1. if (typeof Drupal.settings.ajaxify_pages.processed !== 'undefined') {2. return false;3. }4. Drupal.settings.ajaxify_pages.processed = 'processed';5. Drupal.ajaxify_pages = {cache: {}};6. History.Adapter.bind(window, 'statechange', function() {7. loadPage(History.getState().url);8. });9. Drupal.ajaxify_pages.cache[History.getState().url] = {10. title: $('title').html(),11. content: $(Drupal.settings.ajaxify_pages.content_selector)12. };13. };
History.js и Drupal (Привязка к URL) Разбор полётов
Что, и зачем мы сделали ?
1. Указываем в админке части URL, при нахождении которых, мы игнорируем URL. (Избегаем внешних ссылок, картинок и т.д);
2. Заменяем каждую escape-последовательность в закодированном URI символом, который она представляет;
3. Вталкивает новое "состояния" в браузер;
4. Привязываемся к событию смены состояния страницы, и вызываемся функцию, которая будет получать новый контент;
5. Сохраняем новые значения; (При возврате на URI, на котором мы уже были, можно вытащить уже сохранённые значения, что бы не делать лишний AJAX запрос)
History.js и Drupal Получаем новый контент
Для получения контента, делаем обычный AJAX запрос, НО ! :
1. Drupal, генерирует BODY классы в зависимости от страницы, так что не забываем их заменять на "новой" странице;
2. Разные страницы могут иметь разные CSS файлы, их тоже нужно добавить;
3. Разные страницы могут иметь разные JS файлы, и их :) ;
4. Меняем title страницы;
5. Не забываем про Trobber, потому как AJAX запрос может быть разной продолжительности;
History.js и Drupal Получаем новый контент (немного кода)
1. Drupal.ajaxify_pages.cache[url] = {2. title: new_page.title,3. content: new_page.content,4. scripts: new_page.new_script,5. css: new_page.new_css,6. body_class: new_page.body_class,7. admin_menu: new_page.admin_menu8. };9. processPage(Drupal.ajaxify_pages.cache[url], url);
Не забываем сохранить страницу в "кэш", что бы мы в следующий раз,
при возвращении Back в браузере, ну или новым кликом на ссылку
этой страницы, не делали новый AJAX запрос, а использовали
существующие данные.
History.js и Drupal Заменяем контент
1. $('head script').each(function() {2. if ($(this)[0].src.length > 0) {3. to_replace.page_script.push($(this)[0].src.match(/([^<]+)(.js)/)[0]);4. }5. if ($(this).text().length > 0) {6. $(this).remove();7. }8. });9. 10. if (typeof to_replace.new_script !== 'undefined') {11. $.grep(to_replace.new_script, function(el) {12. if ($.inArray(el, to_replace.page_script) === -1)13. to_replace.diff_script.push(el);14. i++;15. });16. }
History.js и Drupal Заменяем контент
•Нет нужды заменять все JS файлы, потому что при этом, будут
перезапущены все скрипты.
•Не стоит пугаться что JS или CSS файл не появился в вашем DOM-e.
•Мы только добавляем новые файлы.
•В определённый момент, у нас соберутся в кучу все JS и CSS файлы
сайты, но на мой взгляд, это не смертельно.
•Так же, не стоит забывать, что Drupal делает агрегацию файлов, то есть
добавляет символы в конец имени файла. Это надо учесть при получении
и сравнении новых файлов.
History.js и Drupal Views, как быть ?
1. Drupal.settings.views = {2. ajaxViews: {},3. ajax_path: "/views/ajax"4. };5. $("[class^=view]").ready(function() {6. $(".view").each(function() {7. if ($("[class*=view-]").length > 0 && $("[class*=view-dom-id]").length > 0
&& $("[class*=view-display-id]").length > 0) {8. var view_classes_array = $("[class*=view-]").attr('class').split(' '),9. view_name = view_classes_array[1].replace(/view-/g, ""),10. view_display_id =
view_classes_array[3].replace(/view-display-id-/g, ""),11. view_dom_id = view_classes_array[4].replace(/view-dom-id-/g, ""),12. view_path = '';13. view_path = location.pathname.substring(1,
location.pathname.length);14. view_name = view_name.replace("-", "_");
History.js и Drupal Views, как быть ?
1. Drupal.settings.views.ajaxViews['views_dom_id:'+view_display_id] = {2. pager_element: "0",3. view_args: "",4. view_base_path: null,5. view_display_id: view_display_id,6. view_dom_id: view_dom_id,7. view_name: view_name,8. view_path: view_path9. };10. }11. });12. });
History.js и Drupal Views, как быть? (Разбор полётов)Так как мы получаем контент по AJAX, все JS обьекты, мы строим рукчами :(
pager_element - Элемент страничной навигации;
view_args - Аргумент передаваеммый во Views;
view_base_path - Родной адресс Views;
view_display_id - ID "отображения" Views;
view_dom_id - Рандомный хэш, который присваивается как класс контейнеру
Views;
view_name - Машинное имя Views;
view_path - URL на котором вызывается Views. При совпадении с
view_base_path, можно указывать только view_path, а view_base_path оставить
как NULL.
• Drupal.settings.views = {• ajaxViews: {},• ajax_path: "/views/ajax"• };
Views обьект в которов будет список всех Views на странице.Каждая Views идентифицируется с помощью ключа "'views_dom_id:'+view_display_id"
History.js и Drupal Views, как быть? (Разбор полётов)
Приведенный пример, должен использоваться не только по отношению ко Views, но также и в случае AJAX форм, Ctools modals, Omega layouts ... в общем ко всему что могло бы быть динамично добавлено при генерации страницы.
Спасибо за Внимание !