Опубликован: 02.04.2013 | Уровень: для всех | Доступ: свободно
Лекция 4:

Анатомия приложения и навигация по страницам

Аннотация: Данная лекция содержит описание внутреннего устройства приложения, особенности разработки страниц, которые функционируют в локальном контексте и в веб-контексте, подробно рассмотрен жизненный цикл приложения и особенности организации системы навигации в приложении. Особое внимание здесь уделено использованию отложенных операций в применении к различным вопросам разработки
Ключевые слова: принятия решений, сборка, работ, приложение, ПО, цикла, место, HTML, css, visual, Basic, app, host, environment, Windows, интеграция, очередь, домашняя страница, поле, start page, интерфейс, application, контекст, атрибут, контент, доступ, тег, браузер, навигация, функция, URI, navigation, буфер, default, location, пользователь, поиск, content, манифест, запрос, диалоговое окно, исключение, permission denied, continue, запуск, объект, операции, API, Lisp, объединение, исполнение, точка останова, опыт, focus, visibility, активность, DOM, State, change, макет, команда, диспетчер, Приостановка (suspending), Возобновление (resuming), Завершение (terminating), значение, SUSPEND, AND, shutdown, путь, файл, множества, индикатор, XML, item, форматирование, компромисс, поведение приложения, пункт, resume, переменная, Состояние сеанса, отметка времени, механизмы, SQL, server, производительность, AJAX, область видимости, выражение, пространство, set, память, делегаты, handler, result, параметр, error, контейнер, Дополнение, dispatcher, Web, worker, AS, инкапсуляция

Файлы к данной лекции Вы можете скачать здесь.

На ранних стадиях написания курса, я, кроме того, плотно работал с подрядчиком на постройке дома для моей семьи. Даже когда я не был на месте строительства, прилагая все усилия, чтобы оно продвигалось, я был, конечно, вовлечен в процесс принятия решений о различных фазах строительства и иногда участвовал в самом строительстве.

В предгорьях Сьерра-Невады в Калифорнии, где я живу, каркас дома построен из местного дерева, и вся сантехника и проводка должны быть в стенах до того, как будет установлена изоляция и древесные плиты (гипсокартон). Меня поразило, сколько времени потребовалось для того, чтобы завершить монтаж этой инфраструктуры. Строители потратили много времени на добавление маленьких брусков дерева, то тут, то там, чтобы упростить себе отделочную работу позже (вроде развешивания шкафов), и много времени заняла правильная сборка сантехники и проводки. Всё это стало совершенно невидимым для глаз, когда панели были закреплены и была готова отделка.

Представьте себе, на что будет похож дом, построенный без такого внимания к его деталям. Представьте, что некоторые выключателя просто не работают или управляют не теми светильниками. Представьте себе, что трубы в стенах протекают. Представьте, как шкафы и отделка падают со стен через пару недель после того, как они попали в дом. Даже если дому удалось пройти окончательную проверку, подобные недостатки сделают его почти непригодным для жизни, и неважно, каким красивым он может показаться на первый взгляд. Это походило бы на несколько работ знаменитого архитектора Фрэнка Ллойда Райта: очень интересно с архитектурной точки зрения, но совершенно неудобно для того, чтобы жить там.

С приложениями - та же история. На самом деле, я удивляюсь, как много похожего между этими двумя начинаниями! Это значит, что приложение может замечательно выглядеть, даже ошеломляюще, но как только вы начнете по-настоящему пользоваться им, день за днем, недостатки внимания к фундаментальным основам станет болезненно очевидным. В результате, ваши клиенты, вероятнее всего, начнут искать себе новый дом, то есть - приложение какого-нибудь другого разработчика.

Эта лекция посвящена тем самым основам: ключевым глубинным конструкциям приложения, на основе которых можно построить нечто, выглядящее красиво и работающее по-настоящему хорошо. Мы сначала доведем до полного понимания окружение хост-процесса приложения и посмотрим на активацию (то, как приложение запускается) и на переходы между стадиями жизненного цикла приложения. Затем мы посмотрим на навигацию по страницам внутри приложения и разберем еще некоторые важные попутные вопросы, такие, как работа с несколькими асинхронными операциями.

Позвольте мне предупредить вас о том, что эта лекция гораздо больше и сложнее, чем многие, следующие за ней, так как она имеет дело с программными эквивалентами создания каркаса дома, построения сантехнических коммуникаций и электропроводки. В случае с нашим домом, я могу с полной уверенностью говорить о том, что установка прекрасных светильников, которые выбрала моя жена, принесло больше удовольствия, чем сам процесс строительства, которым я занимался месяцами ранее. Но сейчас, по-настоящему живя в доме, я чувствую глубокую признательность за всю ту менее привлекательную работу, благодаря которой дом был построен. Это место, где я хочу быть, место, в котором я и моя семья будем счастливы провести большую часть нашей жизни. Вы ведь хотите, чтобы клиенты чувствовали то же самое по отношению к вашим приложениям? Абсолютно точно! Зная о том удовольствии, которое хорошо спроектированные приложения способны принести вашим клиентам, займёмся нашим делом и ощутим удовольствие от исследования деталей!

Локальный и веб-контексты внутри хост-процесса приложения

Как было описано в "Жизненный путь приложений для Магазина Windows: Характеристики платформы Windows 8" , приложения, написанные на HTML, CSS и JavaScript не являются исполняемыми, как их скомпилированные аналоги, написанные на C#, Visual Basic или C++. В пакетах приложений нет EXE-файлов, там есть лишь .html, .css и .js-файлы (и ресурсы тоже, конечно), которые не содержат ничего, кроме обычного текста. В итоге, что-то должно превратить эти тексты, определяющие приложение, во что-то, что запускается и работает в памяти. Это "кое-что" - хост-процесс приложения (app host), wwahost.exe, который создаёт то, что мы называем управляемой средой (hosted environment) приложений для Магазина Windows.

Рассмотрим то, что мы уже узнали в "Жизненный путь приложений для Магазина Windows: Характеристики платформы Windows 8" и "Быстрый старт" о характеристиках управляемой среды.

  • Хост-процесс приложения (и приложения в нем) получают доступ к критически важным ресурсам через брокера.
  • Хотя хост-процесс предоставляет среду, очень похожую на Internet Explorer 10, существует некоторое количество различий в DOM API, отраженных в документах "Список изменений API для модели DOM и HTML", (http://msdn.microsoft.com/library/windows/apps/hh700404.aspx), "Возможности и различия HTML, CSS и JavaScript", (http://msdn.microsoft.com/library/windows/apps/hh465380.aspx). Кроме того, дополнительные сведения можно найти в материале "Сравнение приложений для Магазина Windows на языке JavaScript с традиционными веб-приложениями", (http://msdn.microsoft.com/library/windows/apps/hh465408.aspx).
  • HTML-контент в пакете приложения может быть загружен в локальный контекст или в веб-контекст. в зависимости от схемы (ms-appx:/// или ms-appx-web:///), использованной для обращения к контенту (третий / означает "пакет приложения"). Удалённый контент (обращение к нему ведется с помощью http[s]://) всегда исполняется в веб-контексте.
  • Локальный контекст имеет доступ к API WinRT, помимо других возможностей, в то время, как веб-контекст может загружать и исполнять удалённые скрипты, но не имеет доступа к WinRT.
  • Использование подключаемых элементов управления ActiveX не разрешено ни в том, ни в другом контексте.
  • Функция HTML5 postMessage может быть использована для организации межконтекстного взаимодействия между iframe и средой, которая её содержит. Это может быть полезным для исполнения удаленного скрипта в веб-контексте и передачи результатов в локальный контекст; скрипт из веб-контекста не нужно перемещать в локальный контекст и исполнять там. (Правила Магазина Windows запрещают это и приложения, отправленные в Магазин, анализируются на предмет применения подобных методов работы).
  • Дополнительные сведения вы можете найти в материале "Функции и ограничения в зависимости от контекста" (http://msdn.microsoft.com/library/windows/apps/hh465373.aspx), включая то, какие части WinRT не используют WinRT, и, таким образом, могут быть использованы в веб-контексте. (WinJS, кстати, нельзя использовать на веб-страницах за пределами приложения).

В этой лекции мы больше будем заниматься не самими этими характеристиками, а тем, как они воздействуют на структуру приложения. (Для того, чтобы посмотреть характеристики, обратитесь к примеру "Интеграция содержимого и элементов управления с веб-сервисов", (http://code.msdn.microsoft.com/windowsapps/Mashup-Sample-10689f5b).)

В первую очередь, и прежде всего, отметим, что домашняя страница приложения, та, которую вы указываете в манифесте, в поле Начальная страница (Start page), на закладке Интерфейс приложения (Application UI)1В манифесте это называется "Начальная страница", но я предпочитаю термин "домашняя страница" для того, чтобы не возникло путаницы с Начальным экраном Windows, всегда выполняется в локальном контексте. И любая страница, к которой она может обратиться напрямую (посредством <href> или команды document.location) так же должна принадлежать локальному контексту.

Далее, страница локального контекста может содержать элемент iframe, имеющий локальный или веб-контекст, предоставляя ему атрибут src, ссылающийся на контент в пакете приложения (и, кстати, программный доступ только для чтения к содержимому вашего пакета, который можно получить посредством Windows.ApplicationMode.Package.Current.InstalledLocation). Ссылки на любые другие местоположения (http[s]:// или другие протоколы) всегда помещаются в iframe, который исполняется в веб-контексте.

<!-- iframe в локальном контексте, с источником из пакета приложения  -->	
<!-- подобное разрешено лишь из локального контекста -->
<iframe src="/frame-local.html"></iframe>	
<iframe src="ms-appx:///frame-local.html"></iframe>	

<!-- iframe в веб-контексте с источником в пакете приложения -->
<iframe src="ms-appx-web:///frame-web.html"></iframe>

<!-- iframe с внешним источником автоматически присваивается веб-контекст -->
<iframe src="http://www.bing.com"></iframe>

Кроме того, если вы используете тег <a href="..." target="..."> с target, указывающем на iframe, схема в href определяет контекст.

Страница в веб-контексте, в свою очередь, может содержать только iframe, который так же имеет веб-контекст. Например, выше можно использовать последние два элемента iframe, в то время как два первых элемента - нет. Кроме того, вы можете использовать ms-appx-web:/// в веб-контексте для того, чтобы ссылаться на другое содержимое в пакете приложения, например, на изображения.

Хотя это не вполне справедливо внутри приложений для Магазина Windows, по причинам, которые мы рассмотрим ниже в этой лекции, похожие правила применимы к навигации между страницами с использованием <href> или document.location. Так как всё то, о чём мы здесь говорили, похоже сейчас на кашу из разных сведений, точное поведение для этих вариаций и iframe приведено в следующей таблице:

Таблица 3.1.
Цель Результат на странице в локальном контексте Результат на странице в веб-контексте
<iframe src="ms-appx:///"> iframe в локальном контексте Не разрешено
<iframe src="ms-appx-web:///"> iframe в веб-контексте iframe в веб-контексте
<iframe src="http[s]:// "> или другая схема iframe в веб-контексте iframe в веб-контексте
<a href="[uri]" target="myFrame"> <iframe name="myFrame"> iframe в локальном или веб-контексте, в зависимости от [uri] iframe в веб-контексте; [uri] не может начинаться с ms-appx.
<a href="ms-appx:///"> Ссылка на страницу в локальном контексте Не разрешено, если только не задано явно (смотрите ниже)
<a href="ms-appx-web:///"> Не разрешено Ссылка на страницу в веб-контексте
<a href="[uri]"> с любым другим протоколом, включая http[s] Открывает браузер по умолчанию с [uri] Открывает браузер по умолчанию с [uri]

Когда iframe работает в веб-контексте, обратите внимание на то, что страница может иметь ms-appx-web-ссылки на ресурсы пакета приложения, даже если страница загружена с удалённого источника (http[s]). Подобная страница, конечно, не будет работать в браузере.

Последние два элемента в таблице, на самом деле, имеют в виду то, что приложение не может осуществить переход со своей страницы верхнего уровня (в локальном контексте) прямо к странице любого вида в веб-контексте (удалённой или локальной) и при этом отображать её в приложении. Вместо приложения будет запущен браузер. Такова жизнь приложения в хост-процессе! Подобный контент нужно размещать в iframe.

Аналогично, навигация со страницы веб-контекста к страницам локального контекста по умолчанию не разрешена, но вы можете включить это, вызвав сверхсекретную функцию MSApp.addPublicLocalApplicationUri (http://msdn.microsoft.com/library/windows/apps/hh465759.aspx) из кода на локальной странице (на самом деле, функция хорошо документирована) для каждого конкретного URI, который вам нужен:

//Это должно быть вызвано из локального контекста
MSApp.addPublicLocalApplicationUri("ms-appx:///frame-local.html");

Упражнение для этой лекции, "Direct Navigation", содержит демонстрацию этой возможности (как в Сценарии 6, в примере "Интеграция содержимого и элементов управления с веб-сервисов", (http://code.msdn.microsoft.com/windowsapps/Mashup-Sample-10689f5b). Будьте осторожны, когда URI содержит параметры запроса. Например, вы можете не захотеть позволять вебсайту переходить на что-то вроде: ms-appx:///delete.html?file=superimportant.doc , выполняя операцию по удалению очень важных данных!

Здесь возникает еще один вопрос, о возможности предоставить странице в веб-контексте доступ к специфическим функциям, наподобие геолокации, записи данных в буфер обмена, доступ к кэшу приложения, к IndexedDB - к тому, чем обычно пользуются веб-страницы. По умолчанию веб-контекст в приложениях для Магазина Windows не имеют доступа к подобным возможностям уровня операционной системы. Например, создайте новое приложение по шаблону Пустое приложение в Visual Studio с этой единственной HTML-строкой в теле страницы default.html:

<iframe src="http://maps.bing.com" style="width:1366px; height: 768px"></iframe>

Затем, включите возможность Расположение (Location) в манифесте (я об этом забыл, когда проводил данный эксперимент!) и запустите приложение. Вы, как и ожидалось, увидите страницу Bing2Если цветовое оформление кажется вам странным, это потому, что iframe берет стили из таблицы стилей по умолчанию ui-dark.css в WinJS. Попытайтесь поменять эту таблицу стилей на ui-light.css для того, чтобы то, что вы видите, приобрело бы более традиционный вид.. Однако, попытка использовать возможности по определению местоположения на данной странице - щелчок по значку локатора в левой части, у элемента "World", например - приведет к возникновению ошибки, вроде той, которая показана на Рис. 3.1.

Использование возможности, доступ к которой контролируется брокером, наподобие геолокации, внутри веб-контекста, приводит к появлению ошибки

увеличить изображение
Рис. 3.1. Использование возможности, доступ к которой контролируется брокером, наподобие геолокации, внутри веб-контекста, приводит к появлению ошибки

Подобные возможности заблокированы, так как веб-контент, загруженный в iframe, может легко осуществить переход на любые другие страницы. Со страницы карт Bing, изображенной выше, пользователь может перейти на домашнюю страницу поисковой системы Bing, выполнить поиск, и затем перейти на любое количество непроверенных, потенциально опасных, страниц. В любом случае, эти страницы могут запрашивать доступ к критически важным ресурсам, и если это приведет к показу обычного вопроса для пользователя, который выводит приложение, пользователи могут быть обмануты при предоставлении подобного доступа.

К счастью, если вы хорошо попросите, Windows позволит вам включить эти возможности для веб-страниц, о которых знает приложение. Всё, что для этого нужно - письменные показания, под присягой, подписанные вами и шестнадцатью свидетелями… Ладно, я шучу! Вам просто нужно добавить то, что называется правила URI содержимого приложения (application content URI rules) в ваш манифест. Каждое правило заявляет о том, что контент, доступный по некоторому URI, известен приложению, и оно доверяет ему. Таким образом, этот контент может действовать от имени приложения. Вы так же можете исключать URI, что обычно используется для исключения определенных страниц, которые могли бы быть включены в другое правило.

Подобные правила создаются на закладке URI содержимого (Content URI) редактора манифеста в Visual Studio, как показано на Рис. 3.2. Каждое правило должно содержать точный URI, по которому может быть сделан запрос, такой, как http://www.bing.com/maps/ . Как только мы добавим это правило (как в полном упражнении для этой лекции, "ContentURI"), картам Bing разрешено будет использовать геолокацию. Когда они попытаются это сделать, отобразится диалоговое окно (Рис. 3.3), как тогда, когда приложение само пытается выполнить подобное действие. (Примечание. При выполнении в отладчике, пример "ContentURI" может показать исключение Отсутствие прав доступа (Permission Denied) при запуске. Если это произошло, нажмите Продолжить (Continue) в Visual Studio, так как это не влияет на запуск приложения вне отладчика).

Добавление URI содержимого в манифест приложения; содержимое текстовых полей сохраняется при сохранении манифеста. Кнопка Добавить новый URI (Add New URI) создаёт набор элементов управления, с помощью которых можно добавлять дополнительные правила

Рис. 3.2. Добавление URI содержимого в манифест приложения; содержимое текстовых полей сохраняется при сохранении манифеста. Кнопка Добавить новый URI (Add New URI) создаёт набор элементов управления, с помощью которых можно добавлять дополнительные правила
Когда правило URI содержимого должным образом настроено, веб-контент в  iframe действует так же, как приложение, что говорит о том, почему правила URI содержимого необходимы для защиты пользователя от страниц, неизвестных приложению, которые, в противном случае могли бы обмануть пользователя, получая доступ к критически важным ресурсам

увеличить изображение
Рис. 3.3. Когда правило URI содержимого должным образом настроено, веб-контент в iframe действует так же, как приложение, что говорит о том, почему правила URI содержимого необходимы для защиты пользователя от страниц, неизвестных приложению, которые, в противном случае могли бы обмануть пользователя, получая доступ к критически важным ресурсам

Врезка: Несколько советов и предостережений по iframe

Так как мы говорим здесь об iframe, вот несколько дополнительных советов, которые вы можете счесть полезными при работе с этим элементом. Во-первых, чтобы предотвратить выделение, настройте стиль элемента с помощью -ms-user-select: none (http://msdn.microsoft.com/library/windows/apps/hh779846.aspx) или установите его свойство style.msUserSelect в значение none с помощью JavaScript. Во-вторых, некоторые веб-страницы содержат код, который препятствует их загрузке в iframe, в таком случае, страница будет открыта в стандартном браузере, а не в iframe. Если эта страница важна для вашего приложения, вам нужно поработать с владельцем страницы для создания альтернативной страницы, которая предназначена специально для вас. В-третьих, так как плагины в приложениях для Магазина Windows не поддерживаются, они так же не будут загружены для страницы, которая загружена в iframe. В итоге, загружать в приложение веб-содержимое, которое не принадлежит приложению - дело рисковое.

Далее, поддержка iframe не предназначена для того, чтобы позволить вам строить приложение исключительно из удалённых веб-страниц. Раздел 2.4. "Сертификационных требований к приложениям для Windows 8" (http://msdn.microsoft.com/library/windows/apps/hh694083.aspx), фактически, прямо запрещает приложения, которые являются веб-сайтами - основная функциональность приложения должна содержаться внутри приложения, то есть, подразумевается, что она не должна реализовываться средствами веб-сайта, загруженного в элемент iframe. Несколько ключевых причин для этого - то, что веб-сайты обычно не очень хорошо настроены для сенсорного взаимодействия (что нарушает требование пункта 3.5.) и часто не работают нормально в прикрепленном режиме (нарушение требования 3.6.). В итоге, чрезмерное использование веб-контента означает, что приложение не пройдёт сертификацию в Магазине Windows.

Обращение к содержимому из данных приложения: ms-appdata

Как мы уже видели, схема ms-appx[-web]:/// позволяет приложению ссылаться в элементах iframe на страницы, которые существуют внутри пакета приложения или в веб. Встаёт вопрос: может ли приложение сослаться на содержимое в локальной файловой системе, которое существует за пределами пакета, такое, как динамически создаваемый файл в папке данных приложения? Быть может, приложение использует протокол file:// для адресации содержимого или доступа к нему?

Как бы не хотелось мне сказать вам, что это просто работает, ответ несколько неоднозначен. Во-первых, протокол file:// полностью заблокирован при проектировании системы по различным причинам, связанным с безопасностью, даже для папок с данными приложения, к которым вы обладаете полным доступом. (Другие обычные протоколы так же не поддерживаются в URI iframe src.) К счастью, имеется заменитель, ms-appdata:///, который удовлетворяет часть потребностей. Внутри локального контекста приложения, ms-appdata:/// это - короткое имя для папки с данными приложения, где существуют папки локальных, перемещаемых, временных данных. Итак, если вы создали изображение, названное image65.png в папке локальных данных приложения, вы можете сослаться на него, используя команду ms-appdata:///local/image65.png, и похожим образом - для roaming и temp, где URI может быть включено в CSS, как, например, background.

К несчастью, есть оговорка, как это обычно бывает с контейнером приложения. Она заключается в том, что ms-appdata может быть использована только для ресурсов, а именно, это может быть атрибут src для элементов img (изображение), video (видео) и audio (аудио). Данный подход нельзя использовать для загрузки HTML-страниц, таблиц стилей CSS или JavaScript, не подходит он и для целей навигации (iframe, гиперссылки и так далее). Это так, потому что не представляется возможным создания суб-изолированной среды для подобных страниц, а без этого страница, загруженная с помощью ms-appdata://, получит полный доступ к приложению.

Можете ли вы выполнять динамическую генерацию страниц? Да: вам нужно загрузить содержимое файла и обработать его вручную, вставив в DOM посредством свойства innerHTML или чего-то подобного. Вы можете получить доступ к папкам с данными приложения, воспользовавшись API Windows.Storage.ApplicationData. Для загрузки и рендеринга полной HTML-страницы, нужно, чтобы вы обработали все внешние ссылки, разобрались бы со скриптами, но сделать это можно - если вы действительно этого хотите.

Похожий вопрос касается возможности динамической генерации и исполнения скриптов. Ответ снова подразумевает ограничения. Да, вы можете взять строку JavaScript и передать её в функцию eval или exeScript. Однако, учтите, что требования сертификации Магазина Windows прямо запрещают выполнять это со скриптами, полученными из удалённых ресурсов в локальном контексте (раздел 3.9. требований). Другое предостережение заключается в том, что для подобного кода применяется автоматическая фильтрация, которая предотвращает внедрение скриптов (и другого опасного кода) в DOM через свойства вроде innerHTML и outerHTML, и методы, такие, как document.write и DOMParser.parseFromString. Но есть ситуации, когда вы, как разработчик точно знаете, что делаете, наслаждаясь жонглированием бензопилами и горящими кинжалами, и, таким образом, хотите обойти подобные ограничения, особенно - используя библиотеки сторонних разработчиков (смотрите врезку ниже). Понимая это, Microsoft предоставляет механизм для того, чтобы сознательно всё это обойти: MSApp.execUnsafeLocalFunction. Подробности об этом вы можете найти в материале "Разработка безопасных приложений", (http://msdn.microsoft.com/library/windows/apps/hh849625.aspx), который, помимо этой темы, содержит информацию еще по некоторым вещам, которые я сюда не включал. Один из подразделов этого документа - касается различных вариаций атрибута sandbox для iframe. Так же, для того, чтобы лучше разобраться в этом, можете посмотреть пример "JavaScript iframe sandbox attribute sample" (http://code.msdn.microsoft.com/windowsapps/JavaScript-iframe-sandbox-0f077ece)

Как ни странно, WinJS, на самом деле, упрощает жонглирование опасными предметами! WinJS.Utilities.setInnerHTMLUnsafe, setOuterHTMLUnsafe, и insertAdjacentHTMLUnsafe- это "обёртки" для вызова методов DOM, которые могут отфильтровать опасное содержимое.

Рассказав всё это (вы ведь любите углубляться в детали?), давайте рассмотрим пример использования ms-appdata. Скорее всего, этот механизм вы будете широко использовать в своих приложениях.

Врезка: Библиотеки сторонних разработчиков в управляемой среде

В целом, приложения для Магазина Windows могут использовать библиотеки наподобие jQuery, Prototype, Dojo и так далее, как сказано в "Жизненный путь приложений для Магазина Windows: Характеристики платформы Windows 8" . Тем не менее, существуют некоторые ограничения и оговорки.

Во-первых, так как страницы локального контекста не могут загружать скрипты из удалённых источников, приложение обычно нуждается во включении подобных библиотек в свой пакет, если не планируется использовать эти возможности только в веб-контексте. WinJS, заметьте, не нужно встраивать в приложение, так как он предоставляется Магазином Windows, но подобные "пакеты фреймворков" не поддерживаются для других библиотек в Windows 8.

Во-вторых, изменения в DOM API и ограничения пакета приложения могут повлиять на библиотеку. Например, библиотечные функции, использующие window.alert, работать не будут. Библиотека, кроме того, не может загружать другую библиотеку из удалённого источника в локальный контекст. Важно отметить, что всё в библиотеке, что подразумевает более высокий уровень доверия, чем обеспечивает контейнер приложения (например, открытый доступ к файловой системе) будет сопряжено с проблемами.

Самая распространённая проблема возникает, когда библиотека пытается добавить элементы или скрипты в DOM (как с помощью innerHTML), это - широко распространённый подход для веб-приложений, который, как правило, не разрешен внутри контейнера приложения. Например, попытка создать виджет для выбора даты jQuery ($("myCalendar").datepicker()) выдаст такого рода ошибку. Вы можете обойти эту проблему на уровне приложения, заключив вышеприведенный код в MSApp.execUnsafeLocalFunction, но это не решит проблем с внедрением кода, которое происходит из более глубокого уровня библиотеки. В случае с примером с jQuery, который здесь показан, элемент управления может быть создан, но щелчок по нему выдаст другую ошибку.

В итоге, вы можете использовать библиотеки сторонних разработчиков, понимая, что обычно они написаны, исходя из предположений, не всегда применимых к контейнеру приложения. Со временем, конечно, появятся версии библиотек, полностью совместимые с Windows 8.

"Here My Am!" с ms-appdata

Отлично! Пережив семь страниц эзотерики, давайте поиграем с настоящим кодом и вернемся к приложению "Here My Am!", которое мы написали в "Быстрый старт" . Эта программа использует удобный метод URL.createObjectURL для показа изображения, полученного с камеры в элементе img:

captureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo)
.done(function (capturedFile) {	
if (capturedFile) {	
that.src = URL.createObjectURL(capturedFile);	
}	
});	

Всё это замечательно: мы просто берем адрес, полагая, что изображение хранится где-то в то время когда мы сформировали URI. На самом деле, фотографии (и видео), захваченные с помощью соответствующего API камеры, просто хранятся во временном файле. Если вы установите точку останова в отладчике и посмотрите на capturedFile, вы увидите, что там находится уродливый путь к файлу, наподобие C:\Users\kraigb\AppData\Local\Packages\ ProgrammingWin8-JS-CH3- HereMyAm3a_5xchamk3agtd6\TempState\picture001.png. Ничего себе! Не выглядит дружественно, да и типичный пользователь вряд ли захочет видеть подобное.

В случае с приложением, подобным данному, скопируем данный временный файл в более подходящее место, для того, чтобы разрешить пользователю, например, выбирать одну из ранее сделанных фотографий (так, как мы сделаем в "Быстрый старт" курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript". Мы создадим копию файла в папке локальных данных приложения и используем ms-appdata для установки img src на данное расположение файла. Начнём с вызова captureUI.captureFileAsync, как и ранее.

//Для использования в promise-вызовах, 
// объединенных в цепочку 
var capturedFile = null;

captureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo)	
.then(function (capturedFileTemp) {	
//Убедитесь в том, что проверили правильность полученных данных; 
//это может быть null, если пользователь отменит операцию.
if (!capturedFileTemp) { throw ("no file captured"); }

Обратите внимание на то, что вместо вызова done для получения promise-результатов, мы используем then. Это происходит потому, что нам нужно построить цепочку из асинхронных операций и then позволяет информации об ошибках распространяться по цепочке, как мы увидим в следующем разделе. В любом случае, как только мы получим результат в captureFileTemp (файл расположен в подобной, странно выглядящей, папке), затем мы откроем или создадим папку "HereMyAm" внутри папки с локальными данными приложения. Это может быть сделано с помощью Windows.Storage.ApplicationData.current.localFolder, что позволяет нам получить объект Windows.Storage.StorageFolder, который даёт нам метод createFolderAsync:

//Для демонстрации использования ms-appdata usage, скопируем StorageFile в папку HereMyAm
//распложенную в папке appdata/local, и используем ms-appdata для того, чтобы 
//обратиться к ней. 
var local = Windows.Storage.ApplicationData.current.localFolder; capturedFile = capturedFileTemp;
return local.createFolderAsync("HereMyAm", Windows.Storage.CreationCollisionOption.openIfExists);
})
.then(function (myFolder) {
//Снова, проверяем правильность результата операции
if (!myFolder) { throw ("could not create local appdata folder"); }

Предполагая, что папка создана успешно, myFolder будет содержать другой объект StorageFolder. Затем мы используем его в качестве целевого параметра для файлового метода copyAsync, который, в качестве второго параметра, принимает имя файла. Для этого имени мы просто используем исходное имя, добавив к нему дату и время (заменив двоеточия на дефисы, чтобы имя файла было корректным):

//Присоединяем время создания файла (следует избегать коллизий, 
//но нужно заменить двоеточия)	
var newName = capturedFile.displayName + " - "	
+ capturedFile.dateCreated.toString().replace(/:/g, "-") + capturedFile.fileType;
return capturedFile.copyAsync(myFolder, newName);	
})	
.done(function (newFile) {	
if (!newFile) { throw ("could not copy file"); }

Так как это - последняя асинхронная операция в цепочке, мы используем метод promise-объекта done по причинам, о которых скоро поговорим. В любом случае, если копирование удалось, переменная newFile содержит объект StorageFile для копии, и мы можем сослаться на данный файл, используя URI ms-appdata:

lastCapture = newFile; 
//Сохраним для целей общего доступа	
that.src = "ms-appdata:///local/HereMyAm/" + newFile.name;
},	
function (error) {	
console.log(error.message);	
});	

Полный код вы можете найти в упражнении HereMyAm3a.

Конечно, мы можем использовать URL.createObjectURL с newFile, как ранее (убедившись в установке параметра { oneTimeOnly=true } для того, чтобы избежать утечек памяти). Несмотря на то, что это расходится с целями данного упражнения, работает это отлично (и нагрузка на память, в общем, та же самая, при использовании этих двух методов). На самом деле, нам нужно использовать этот подход, если мы скопируем изображения в библиотеку изображений пользователя. Для того, чтобы это сделать, просто замените Windows.Storage.ApplicationData.current.localFolder на Windows.Storage.KnownFolders.picturesLibrary и объявите возможность Библиотека изображений (Pictures Library) в манифесте. Оба API возвращают StorageFolder, поэтому оставшийся код выглядит так же, за исключением того, что мы используем URL.createObjectURL, так как ни ms-appdata://, ни file:// мы не можем использовать для доступа к библиотеке изображений. Упражнение HereMyAm3a содержит данный код в комментариях.

Юрий Макушин
Юрий Макушин
Россия, Москва, РЭА им. Плеханова, 2004