
По хорошему, полноэкранный режим доступен только для полностью динамических AJAX веб-приложений, которые не перегружают экран целиком и не переходят по ссылкам даже в рамках своего домена. Если же адаптируется обычный сайт, где навигация происходит при смене URL и перегрузке страниц, то и fullscreen не доступен и куки не исчезают.
Рецепт для простого сайта
Решение это известно, я приведу его только потому, что ниже дополню его отличиями для веб-приложения.
Тут документация по дополнительным мета-тегам Safari, а вот нужные нам мета-теги для вставки в head:
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<link rel="apple-touch-icon-precomposed" href="/favicon.png">
Картинка /favicon.png станет иконкой на рабочем столе. Про размеры иконок подробнее написано в документации, их может быть несколько, например.
Полноэкранный режим
Для перехода приложения в полноэкранный режим, присовокупим к тем двум тегам еще два:
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
Для обычного сайта они имеют спорную пользу, но вот для веб-приложения — другое дело.
Решение для WebApplication
Как я вскользь упоминал, для веб-приложений нужно выдержать дополнительные условия: приложение не должно переходить по ссылкам a href, это приводит к открытию ссылок в браузере и выходу из фулскрин режима. Но вот делать
window.location.reload(true);
можно и даже window.location = "/demo/path";
вполне разрешен из JavaScript. При этих переходах кукизы не теряются и все хорошо.Следующий код позволит сохранить сессионный cookie в localStorage, и когда кукиз будет потерян при переходе между приложениями в iOS, то этот же код восстановить из кукиз и перегрузит страницу, чтобы сервер отдал ее в том виде, как должен получить залогиненый пользователь.
function PersistCookie(SessionCookieName) {
if (localStorage && (
navigator.userAgent.match(/iPhone/i) ||
navigator.userAgent.match(/iPod/i) ||
navigator.userAgent.match(/iPad/i)
)) {
var CookieSession = document.cookie.match(new RegExp(SessionCookieName + "=[^;]+"));
var LocalSession = localStorage.getItem(SessionCookieName);
if (CookieSession) {
CookieSession = CookieSession[0].replace(SessionCookieName + "=", "");
if (LocalSession!=CookieSession) {
localStorage.setItem(SessionCookieName, CookieSession);
}
} else if (LocalSession && LocalSession!=CookieSession) {
document.cookie = SessionCookieName + "=" + LocalSession + "; path=/";
window.location.reload(true);
}
}
}
Как видно из кода, у нас есть два места хранения сессионной переменной: document.cookie и localStorage, мы читаем из обоих, а пишем туда, где кода небыло. В случае, если код есть в обоих местах, то предпочтение отдается document.cookie, т.к. может так случиться, что сервер заменит сессионную переменную и нам ее нужно записать поверх той, что уже есть в localStorage. Пример вызова: PersistCookie(«SID»); В параметрах передается имя сессионной куки. Вызов нужно делать при загрузке страницы, но оборачивать в событие «onload» или в jQuery.ready() не обязательно. Для PHP имя сессионной куки «PHPSESSID», для ASP.NET «ASP.NET_SessionId» и т.д. но может меняться в настройках сервера или программно. При отлогинивании пользователя нужно не забыть сделать
if (localStorage) localStorage.clear();
чтобы кукиз не вернулся. Еще можно отключить проверку navigator.userAgent, чтобы код работал не только в iOS, но я не исследовал, будет ли это полезно или вредно.P.S. Вообще, я нашел описание проблемы с кукизами в англоязычных форумах, и один робкий совет: устанавливать время жизни сессионного куки со стороны сервера. Не знаю, почему такое советовали, я пробовал это делать, совет не работает, возможно, он работал на каких-то старых версиях iOS или человеку только показалось, что этот метод сработал. Вообще, сессионные куки не должны иметь время жизни, т.е. поля Expires по спецификации у них нет, иначе они перестают быть сессионными.
UPD: Обнаружилась приятная особенность, localStorage сквозной для всего домена, то есть, залогинившись в Safari, сессия распространяется на установленное «псевдо-приложение» и наоборот, если в приложении залогиниться, то потом в сафари кукиз добавляется, если страницу обновить.
UPD2: Есть и неприятная особенность, каждый раз после возвращения к «псевдо-приложению», кроме сброса кукизов еще и страница перегружается, то есть, если выдать форму и пользователь ее начинает заполнять, потом переключился куда-то, вернулся и все пропало, и форма и все, что ввел. Так что, еще до поста нужно все сохранять в localStorage. Скорее всего, нужно сделать универсальное решение для сохранения форм с любыми полями, и вообще, сохранения «состояния» приложения на момент переключения, чтобы восстановить что там было. Состояние же может содержать состояние навигации внутри приложения, состояние контролов (например закладок, списков), состояние прокрутки, состояние динамических изменений html и css на странице. Все же сбрасывается на пол пути. Кстати, страница при сбросе не перегружается с сервера, а берется из кеша.
UPD3: Для тех, кто хочет минимальными усилиями сделать обычный сайт псевдо-приложением в фулскрин режиме, но не имеет желания переписывать все на AJAX, можно перехватывать все ссылки и делать переходы между страницами через window.location. При этом, как уже говорилось, Safari не выбьет вас в режим браузера, если только ссылка не будет вести на другой домен. Вот решение на jQuery:
$('a').live('click', function(e) { e.preventDefault(); window.location = $(this).attr('href'); });
Но остается проблема со сбросом сайта на первую страницу при переключении между приложениями. Это лечится так же, как и с кукизами — сохраняем в localStorage. Конечно же, нужно определаять, куда ведут ссылки и сохранять в localStorage только ссылки в пределах нашего домена, все другие и так будут открываться в Safari. Вот все наработки собраны в расширение для jQuery:
(function($) {
$.platform = {
iPhone: navigator.userAgent.match(/iPhone/i),
iPod: navigator.userAgent.match(/iPod/i),
iPad: navigator.userAgent.match(/iPad/i),
Android: navigator.userAgent.match(/Android/i)
};
$.platform.iOS = $.platform.iPhone || $.platform.iPod || $.platform.iPad;
$.platform.Mobile = $.platform.iOS || $.platform.Android;
$.extend({
fixLinks: function(persist) {
if ($.platform.iOS) {
if (persist == null) persist = true;
persist = persist && localStorage;
if (persist) {
var CurrentLocation = window.location.pathname + window.location.search;
var StoredLocation = localStorage.getItem("location");
if (StoredLocation && StoredLocation !== CurrentLocation) {
window.location = StoredLocation;
}
}
$('a').live('click',function(e) {
e.preventDefault();
if (persist && this.host === window.location.host)
localStorage.setItem("location", this.pathname + this.search);
window.location = this.href;
});
}
},
fixCookie: function (SessionCookieName) {
if (localStorage && $.platform.iOS) {
var CookieSession = document.cookie.match(new RegExp(SessionCookieName + "=[^;]+"));
var LocalSession = localStorage.getItem(SessionCookieName);
if (CookieSession) {
CookieSession = CookieSession[0].replace(SessionCookieName + "=", "");
if (LocalSession!=CookieSession) {
localStorage.setItem(SessionCookieName,CookieSession);
}
} else if (LocalSession && LocalSession !== CookieSession) {
document.cookie = SessionCookieName + "=" + LocalSession + "; path=/";
window.location.reload(true);
}
}
}
});
})( jQuery );
Использовать расширение очень просто, подключите js файл с библиотекой и при загрузке страницы вставьте вызовы:
- Исправить проблему с нежелательным выходом в Safari по ссылкам для iOS полноэкранного приложения и сохранять/восстанавливать текущий URL в пределах домена в localStorage: $.fixLinks();
- Исправить проблему с выходом в Safari для iOS, но не запоминать URL в localStorage: $.fixLinks(false);
- Исправить проблему со сбросом сессионных Cookie: $.fixCookie(«SID»); где «SID» имя сессионной куки.