如何自訂 Visual Studio 範本資料 (HTML)
[ 本文的目標對象是撰寫 Windows 執行階段 App 的 Windows 8.x 和 Windows Phone 8.x 開發人員。如果您正在開發適用於 Windows 10 的 App,請參閱 最新文件 ]
在「中樞/樞紐」、「中樞」、「樞紐」、「格線」及「分割」範本中,取得應用程式所需資料的程式碼位於 data.js 檔案中。 這個檔案代表應用程式的範例資料來源。data.js 檔案包含通常需以動態資料取代的靜態資料。例如,如果您的應用程式發出一個 xhr 要求來取得 RSS 或 JSON 資料,您可能想在 data.js 中包含這個程式碼。在該處包含程式碼可讓您易於使用自己的資料,而不用變更範本中的資料模型。
秘訣 「中樞/樞紐」、「中樞」及「樞紐」範本也可以從支援全球化的 .resjson 檔案擷取靜態資料。如需詳細資訊,請參閱將資料繫結至中樞/樞紐、中樞及樞紐範本 UI 的範例。
將您自己的資料新增至應用程式時,您需要注意幾件事情:
- 群組與項目在本質上是連結的。應用程式的項目資料必須組織為群組。您可以在自己的實作取消兩者的連結,但是需要修改程式碼才能讓實作運作。本主題顯示範本資料模型中群組的用法。
- 當您在 data.js 中實作應用程式的自訂資料時,需要確定自訂資料的固有屬性名稱對應到範本使用的屬性名稱。您可以變更範本使用的名稱,但是需要修改更多程式碼。本主題將說明做法範例。
項目和群組
範本資料是儲存在 WinJS.Binding.List 中。這個程式碼顯示 data.js 檔案中的清單宣告。
var list = new WinJS.Binding.List();
項目資料陣列 (此範例中為 sampleItems
) 是透過 push 函式傳送至 WinJS.Binding.List,如這裡所示:
generateSampleData.forEach(function (item) {
list.push(item);
});
WinJS.Binding.List 包含用來處理資料群組的內部邏輯。sampleItems
陣列包含 group
屬性,用來識別項目所屬的群組 (在範例資料中,群組指定於 sampleGroups
陣列中)。這裡是 generateSampleData
函式中的項目資料陣列:
function generateSampleData() {
// . . .
var sampleGroups = [
{ key: "group1", title: "Group Title: 1", // . . .
// . . .
];
var sampleItems = [
{ group: sampleGroups[0], title: "Item Title: 1", // . . .
// . . .
];
return sampleItems;
}
當您為自訂資料修改應用程式時,可能需要按照資料群組的相同模式。若為較小的資料集,建議您為 ListView 使用 WinJS.Binding.List。如果您沒有將項目群組,仍然可以使用 WinJS.Binding.List,但是如果範本需要尋找以群組為基礎的資料時,還是需要修改範本的程式碼。
秘訣 WinJS.Binding.List 是使用 JavaScript 陣列的同步資料來源。若為非常龐大的資料集 (可能包含數千個項目),則需要使用非同步資料來源。如需詳細資訊,請參閱使用 ListView。
WinJS.Binding.List 的 createGrouped 函式指定如何使用群組機碼和項目群組值來群組項目。此函式是在 data.js 中呼叫。key
和 group
都是在範例資料陣列中指定的屬性名稱。
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);
當範本應用程式需要項目清單時,它會呼叫 getItemsFromGroup
,而這會傳回 WinJS.Binding.List,其中僅包含屬於指定群組的項目。
function getItemsFromGroup(group) {
return list.createFiltered(function (item) {
return item.group.key === group.key;
});
}
秘訣 getItemsFromGroup
這類函式 (會呼叫 createFiltered) 會建立新的 WinJS.Binding.List 投影,如果您離開頁面,可能就需要處置傳回的物件。若要處置物件,請呼叫 WinJS.Binding.List.dispose 方法。
適用於 JavaScript 的 Windows Library define 函式會透過指定名為 Data
的命名空間搭配一組公開成員函式,公開要在應用程式中使用的資料。
WinJS.Namespace.define("Data", {
items: groupedItems,
groups: groupedItems.groups,
getItemReference: getItemReference,
getItemsFromGroup: getItemsFromGroup,
resolveGroupReference: resolveGroupReference,
resolveItemReference: resolveItemReference
});
如果您要針對應用程式的每個頁面定義不同的資料來源,或不同的資料模型,就必須在 JavaScript 程式碼中取代對這些成員的所有呼叫。
將群組和項目繫結至 UI
下列程式碼顯示 ListView 控制項的標記範例。ListView 的資料來源是在 itemDataSource 屬性指定,如下所示。這個範例來自「分割」範本中的 split.html。
<div class="itemlist win-selectionstylefilled" aria-label="List of this group's items" data-win-control="WinJS.UI.ListView" data-win-options="{
layout: {type: WinJS.UI.ListLayout},
currentItem: {type: WinJS.UI.ObjectType.item, index: 0, hasFocus: true},
selectionMode: 'single',
swipeBehavior: 'none',
tapBehavior: 'toggleSelect',
itemDataSource: select('.pagecontrol').winControl.itemDataSource,
itemTemplate: select('.itemtemplate'),
onselectionchanged: select('.pagecontrol').winControl.selectionChanged
}">
</div>
在上述程式碼中,與頁面關聯的 itemDataSource
屬性會指派給 ListView 控制項的 itemDataSource 屬性。
在範本中,資料通常是繫結至 init 函式或 ready 函式 (定義於和每個 HTML 網頁相關聯的 .js 檔案) 中的 UI。下列程式碼包含在 split.html 的 init 函式中。在這段程式碼中,應用程式會取得群組參考,然後呼叫在 data.js 中實作的 getItemsFromGroup
。如前所述,getItemsFromGroup
會傳回 WinJS.Binding.List,其中只包含指定群組中的項目。
this._group = Data.resolveGroupReference(options.groupKey);
this._items = Data.getItemsFromGroup(this._group);
我們接著將 getItemsFromGroup 傳回的清單繫結至頁面的 itemDataSource
屬性,這會將資料繫結至 ListView,我們還會指定項目選取的處理常式 (_selectionChanged
)。
this.itemDataSource = this._items.dataSource;
this.selectionChanged = ui.eventHandler(this._selectionChanged.bind(this));
為了在 ListView 中顯示每個項目,應用程式會讓範本與 ListView 產生關聯,如下所示。這段程式碼會顯示在 ListView 控制項的標記中,並使用 itemTemplate 屬性指定類別名稱為 itemtemplate
的 DIV 元素。
itemTemplate: select('.itemtemplate')
以 WinJS.Binding.Template 為基礎的 WinJS 範本會用來格式化和顯示資料的多個例項。「格線」和「分割」範本中最常使用的範本,是用來顯示 ListView 中項目的項目範本。就像每個 WinJS 範本物件,您透過新增 data-win-control 屬性並將該屬性設定為 WinJS.Binding.Template 來宣告它。這是 split.html 中 itemtemplate
的 HTML 程式碼:
<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
<div class="item">
<img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
<div class="item-info">
<h3 class="item-title win-type-ellipsis"
data-win-bind="textContent: title"></h3>
<h6 class="item-subtitle win-type-ellipsis"
data-win-bind="textContent: author"></h6>
</div>
</div>
</div>
itemtemplate
是用於一個任意 ListView 項目。視內容而定,ListView 項目可能是群組或個別資料項目。例如,在 items.html 中,ListView 項目是群組。
重要 您使用 WinJS.Binding.Template 建立的範本與 Visual Studio 專案和項目範本沒有關係,例如「格線」和「分割」範本。
專案範本在資料中必須出現特定屬性,而且 HTML 中要明確命名這些屬性。在 itemtemplate
的上述 HTML 程式碼中,您會找到 title
與 subtitle
之類的屬性。如果您的自訂應用程式資料不使用這些屬性名稱,則需要執行下列其中一項工作:
- 將您的資料對應至這些屬性名稱 (通常在 data.js 中);或者
- 在範本程式碼中修正這些屬性的所有 HTML 和 .js 程式碼參考,讓它們符合您資料中使用的屬性名稱。範本中使用的屬性包含:
title
、subtitle
、description
以及backgroundImage
(群組和項目屬性)group
和content
(項目屬性)key
(群組屬性)
按照相同的 WinJS 範本模式,「格線」應用程式範本也會在它的部分 HTML 網頁中使用 headerTemplate
。
將資料繫結至「中樞/樞紐」、「中樞」及「樞紐」範本 UI 的範例
Visual Studio 的「中樞/樞紐」、「中樞」及「樞紐」專案範本示範如何實作兩種不同的資料來源:
- 儲存在 .resjson 資源檔中的全球化靜態資料。這個資料會用在一些應用程式區段 (PivotItem 或 HubSection 控制項) 中。
- data.js 中的範例資料 (代表資料模型)。這個檔案與「格線」和「分割」範本中的相同。這個範例資料會用在其中一個應用程式區段的 ListView 控制項中。
HTML 中的宣告式函式最初是用來取得範例資料,資料模型預設是同步的。自訂範本以便在所有區段中使用動態資料,需要對 hub.html、hub.js 及其他檔案進行一些變更。下列範例應用程式示範如何自訂「中樞/樞紐」和「樞紐」範本以支援非同步資料:
因為 .resjson 檔案中的全球化資料很容易被取代,所以範例應用程式不會修改這個資源檔。在範例應用程式中,會以非同步方式擷取 <img> 元素資料和「中樞/樞紐」區段中的 ListView 控制項。
如需在 .resjson 檔案中提供全球化資料的詳細資訊,請參閱快速入門:翻譯 UI 資源
若要支援將非同步資料繫結至「中樞/樞紐」的 ListView 控制項,請先取代 hub.js 中這些呼叫資料模型的全域變數。
var section3Group = Data.resolveGroupReference("group4");
var section3Items = Data.getItemsFromGroup(section3Group);
取代為這些變數宣告:
var section3Group = "group2";
var section3Items;
您還必須修改 hub.js 中宣告式函式的實作。在預設範本實作中,這些函式取決於已可使用的資料 (例如對於 section3Items.dataSource
的呼叫)。請將這個程式碼:
section3DataSource: section3Items.dataSource,
section3HeaderNavigate: util.markSupportedForProcessing(function (args) {
nav.navigate("/pages/section/section.html", { title: args.detail.section.header,
groupKey: section3Group.key });
}),
section3ItemNavigate: util.markSupportedForProcessing(function (args) {
var item = Data.getItemReference(section3Items.getAt(args.detail.itemIndex));
nav.navigate("/pages/item/item.html", { item: item });
}),
取代為:
section3DataSource: null,
section3HeaderNavigate: util.markSupportedForProcessing(function (args) {
nav.navigate("/pages/section/section.html", { title: args.detail.section.header,
groupKey: section3Group });
}),
section3ItemNavigate: util.markSupportedForProcessing(function (args) {
var itemSet = section3Items;
var itemObj = itemSet.getAt(args.detail.itemIndex);
var item = [itemObj.group.key, itemObj.title, itemObj.backgroundImage];
nav.navigate("/pages/item/item.html", { item: item });
}),
這個程式碼會將 section3DataSource
函式設定為 null,以避免嘗試繫結尚未就緒的資料。我們稍後會在資料繫結函式 (_bindData
或 bindListView
,取決於範例應用程式而定) 中設定資料來源。
資料可用時,就會呼叫資料繫結函式。為了啟用此功能,我們為資料模型的 dataReady
事件新增了接聽程式 (在 data.js 中的範例應用程式定義)。
this._observer = Data.getObservable();
this._observer.addEventListener('dataReady', this.onDataCompleted.bind(this));
應用程式會從 onDataCompleted
事件處理常式 (未顯示) 呼叫資料繫結函式。「中樞」範本範例 _bindData
函式的程式碼如下所示。在這段程式碼中,我們設定 ListView 的 itemDataSource 屬性。
_bindData: function (context, grp1Items, grp2Items) {
var self = context;
// . . .
self._items = grp2Items;
section3Items = self._items;
self._section3lv.itemDataSource = self._items.dataSource;
// . . .
},
如果使用 [上一步] 按鈕瀏覽頁面,因為不需要等候新資料,所以會直接從頁面的初始化函式呼叫資料繫結函式。
秘訣 在「中樞」範本程式碼中,為了查詢 ListView 元素的 DOM (儲存於 _section3lv
),應用程式會從 Hub 控制項的 loadingstatechanged
事件處理常式呼叫 _hubReady
函式。只在中樞頁面載入完成時,才會啟動這個事件。使用這個事件處理常式,我們就能夠查詢 DOM,以取得與 ListView 關聯的巢狀 DIV 元素。
如需讓非同步資料能夠在「中樞/樞紐」和「中樞」範本中運作的完整程式碼,請參閱使用中樞/樞紐範本的 JSON Web 讀取程式和使用中樞範本的 JSON Web 讀取程式。除了此處所述的自訂之外,我們還對範例應用程式進行了下列變更:
- 在資料模型 (data.js) 中加入程式碼,以使用 xhr 要求擷取資料並剖析 JSON 資料 (來自 Flickr)。
- 在資料模型中加入程式碼,以處理多重資料要求並在資料傳回時啟動
dataReady
事件。 - 在 UI 中加入輸入方塊,以要求新資料。
- 加入進度列,以顯示資料要求的狀態。
- 在輸入方塊與進度列新增 CSS 樣式。
- 加入函式,在 Hub 控制項完全載入之後初始化頁面 (如
_hubReady
或_hubReadyPhone
)。 - 修改「中樞/樞紐」或「中樞」的 <img> 元素,以支援 Click 事件 (修改的檔案專用於這個範本)。
- 修改檔案,將非同步資料繫結至「中樞/樞紐」或「中樞」的 <img> 元素 (修改的檔案專用於這個範本)。
- 修改 hub.js,支援從「中樞」的 <img> 元素瀏覽至影像 (修改的檔案專用於這個範本)。
- 修改了 item.html 與 item.js,以支援檢視單一影像。
將資料繫結至 UI 的範例 (格線與分割)
本節說明如何在「格線」與「分割」專案範本中實作您自己的資料來源。這裡的程式碼範例使用 xhr 要求來產生 RSS 資料。
重要 若要在「中樞」範本中實作非同步資料,請參閱將資料繫結至中樞範本中的 UI。
更新 data.js
在 Visual Studio 中建立新專案。使用分割應用程式或格線應用程式專案範本。
在 data.js 中,將下列變數新增到檔案開頭附近的
use strict
陳述式之後:var lightGray = "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY7h4+cp/AAhpA3h+ANDKAAAAAElFTkSuQmCC"; var mediumGray = "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY5g8dcZ/AAY/AsAlWFQ+AAAAAElFTkSuQmCC";
在 data.js 中,移除包含下列陣列的
generateSampleData
函式:sampleGroups
與sampleItems
。我們將以 RSS 資料取代此資料。我們不會使用大多數的預留位置變數 (例如
groupDescription
),但是會重複使用預留位置影像 (lightGray
和mediumGray
),以讓新的程式碼運作。在移除
generateSampleData
的同一個位置,將下列程式碼新增到 data.js:function getFeeds() { // Create an object for each feed. blogs = [ { key: "blog1", url: 'https://windowsteamblog.com/windows/b/developers/atom.aspx', title: 'tbd', subtitle: 'subtitle', updated: 'tbd', backgroundImage: lightGray, acquireSyndication: acquireSyndication, dataPromise: null }, { key: "blog2", url: 'https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx', title: 'tbd', subtitle: 'subtitle', updated: 'tbd', backgroundImage: lightGray, acquireSyndication: acquireSyndication, dataPromise: null }] // Get the content for each feed in the blog's array. blogs.forEach(function (feed) { feed.dataPromise = feed.acquireSyndication(feed.url); dataPromises.push(feed.dataPromise); }); // Return when all asynchronous operations are complete return WinJS.Promise.join(dataPromises).then(function () { return blogs; }); }; function acquireSyndication(url) { return WinJS.xhr( { url: url, headers: { "If-Modified-Since": "Mon, 27 Mar 1972 00:00:00 GMT" } }); } function getBlogPosts() { getFeeds().then(function () { // Process each blog. blogs.forEach(function (feed) { feed.dataPromise.then(function (articlesResponse) { var articleSyndication = articlesResponse.responseXML; // Get the blog title and last updated date. if (articleSyndication) { // Get the blog title and last updated date. feed.title = articleSyndication.querySelector( "feed > title").textContent; var ds = articleSyndication.querySelector( "feed > updated").textContent; var date = ds.substring(5, 7) + "-" + ds.substring(8, 10) + "-" + ds.substring(0, 4); feed.updated = "Last updated " + date; // Process the blog posts. getItemsFromXml(articleSyndication, blogPosts, feed); } else { // There was an error loading the blog. feed.title = "Error loading blog"; feed.updated = "Error"; blogPosts.push({ group: feed, key: "Error loading blog", title: feed.url, author: "Unknown", month: "?", day: "?", year: "?", content: "Unable to load the blog at " + feed.url }); } }); }); }); return blogPosts; } function getItemsFromXml(articleSyndication, blogPosts, feed) { var posts = articleSyndication.querySelectorAll("entry"); // Process each blog post. for (var postIndex = 0; postIndex < posts.length; postIndex++) { var post = posts[postIndex]; // Get the title, author, and date published. var postTitle = post.querySelector("title").textContent; var postAuthor = post.querySelector("author > name").textContent; var pds = post.querySelector("published").textContent; var postDate = pds.substring(5, 7) + "-" + pds.substring(8, 10) + "-" + pds.substring(0, 4); // Process the content so that it displays nicely. var staticContent = toStaticHTML(post.querySelector( "content").textContent); // Store the post info we care about in the array. blogPosts.push({ group: feed, key: feed.title, title: postTitle, author: postAuthor, pubDate: postDate, backgroundImage: mediumGray, content: staticContent }); } }
在 data.js 中,取代這個程式碼:
var list = new WinJS.Binding.List(); var groupedItems = list.createGrouped( function groupKeySelector(item) { return item.group.key; }, function groupDataSelector(item) { return item.group; } ); // TODO: Replace the data with your real data. // You can add data from asynchronous sources whenever it becomes available. generateSampleData.forEach(function (item) { list.push(item); });
取代為:
var dataPromises = []; var blogs; var blogPosts = new WinJS.Binding.List(); var list = getBlogPosts(); var groupedItems = list.createGrouped( function groupKeySelector(item) { return item.group.key; }, function groupDataSelector(item) { return item.group; } );
我們將重複使用 createGrouped 中可指定群組的程式碼—groupKeySelector 和 groupDataSelector 函式。
因為我們已經變更範本所使用的某些屬性名稱,所以將需要更新 HTML 網頁。特別是對於參考從
subtitle
變更為author
的項目 (非群組) 的任何subtitle
屬性。對於參考項目的任何description
屬性,需要將description
變更為pubDate
。若要在 UI 中實作這些變更,請參閱下列其中一個小節:
- 將範例資料繫結至分割範本中的 UI
- 將範例資料繫結至格線範本中的 UI
將範例資料繫結至分割範本中的 UI
若要在分割範本中使用範例程式碼,請開啟 split.html。
在 split.html 中,我們需要變更 DIV 元素中的幾行程式碼,這個元素具有
itemtemplate
的類別名稱。將這行:<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
變更為:
<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: author"></h6>
此外在 split.html 中,文章區段 (
articlesection
) 有我們需要更新的標頭資訊。將這行:<h4 class="article-subtitle" data-win-bind="textContent: subtitle"></h4>
變更為:
<h4 class="article-subtitle" data-win-bind="textContent: author"></h4>
開啟 items.html。
HTML 程式碼中定義的 WinJS 項目範本包含任意 ListView 項目。在 items.html 中,範本是用來顯示群組 (部落格)。我們在這裡唯一需要變更的群組屬性是
subtitle
。<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
將
subtitle
屬性變更為updated
,如這裡所示:<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: updated"></h6>
儲存專案,然後按 F5 來偵錯應用程式。
您會立刻看到網頁標題,但是在抓取摘要資料時會有短暫延遲。完成所有應完成的事項之後,您會在首頁中看到每個部落格。按一下其中一個部落格,在主控制項/詳細資料控制項檢視中查看部落格文章。
將範例資料繫結至格線範本中的 UI
進行這些步驟之前,請更新 data.js 專案檔,如將資料繫結至 UI 的範例所述。
若要在格線範本中使用 RSS 範例程式碼,請開啟 groupDetail.html。
此頁面顯示單一群組 (一個部落格) 以及該群組中的個別項目 (部落格文章)。
在 groupDetail.html 中,我們需要變更 DIV 元素中的幾行程式碼,這個元素具有
item-info
的類別名稱。將這幾行:<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6> <h4 class="item-description" data-win-bind="textContent: description"></h4>
變更為:
<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: author"></h6> <h4 class="item-description" data-win-bind="textContent: pubDate"></h4>
在 groupDetail.html 中,標頭範本描述群組資訊,而不是個別項目。因此我們不需要變更
subtitle
屬性。這裡是標頭範本:<div class="headertemplate" data-win-control="WinJS.Binding.Template"> <h2 class="group-subtitle" data-win-bind="textContent: subtitle"></h2> <img class="group-image" src="#" data-win-bind="src: backgroundImage; alt: title" /> <h4 class="group-description" data-win-bind="innerHTML: description"></h4> </div>
不過,並非每個群組都有
description
屬性 (項目才有這個屬性),因此我們必須在上述程式碼中,將該屬性變更為updated
,如下所示。<h4 class="group-description" data-win-bind="innerHTML: updated"></h4>
開啟 groupedItems.html,它會顯示所有群組及其中個別的部落格文章。
在這個網頁上,一般 WinJS 項目範本會顯示個別項目 (部落格文章),所以我們需要更新
subtitle
屬性。將這個:<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
變更為:
<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: author"></h6>
儲存專案,然後按 F5 來偵錯應用程式。
您會立刻看到網頁標題,但是在抓取摘要資料時會有短暫延遲。傳回資料即表示已完成應完成的事項,您會在首頁看到每個部落格中的項目。按一下群組標題來檢視群組頁面,或是按一下項目來檢視個別部落格文章。
data.js 的程式碼清單
以下是 data.js 的完整程式碼清單。相同的 data.js 檔案會使用於先前顯示的格線與分割範本範例。如需中樞/樞紐範本的 data.js 檔案,請參閱使用中樞/樞紐範本的 JSON Web 讀取程式。如需中樞範本的 data.js 檔案,請參閱使用中樞範本的 JSON Web 讀取程式。
(function () {
"use strict";
var lightGray = "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY7h4+cp/AAhpA3h+ANDKAAAAAElFTkSuQmCC";
var mediumGray = "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY5g8dcZ/AAY/AsAlWFQ+AAAAAElFTkSuQmCC";
var dataPromises = [];
var blogs;
var blogPosts = new WinJS.Binding.List();
var list = getBlogPosts();
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);
WinJS.Namespace.define("Data", {
items: groupedItems,
groups: groupedItems.groups,
getItemReference: getItemReference,
getItemsFromGroup: getItemsFromGroup,
resolveGroupReference: resolveGroupReference,
resolveItemReference: resolveItemReference
});
// Get a reference for an item, using the group key and item title as a
// unique reference to the item that can be easily serialized.
function getItemReference(item) {
return [item.group.key, item.title];
}
// This function returns a WinJS.Binding.List containing only the items
// that belong to the provided group.
function getItemsFromGroup(group) {
return list.createFiltered(function (item) { return item.group.key === group.key; });
}
// Get the unique group corresponding to the provided group key.
function resolveGroupReference(key) {
return groupedItems.groups.getItemFromKey(key).data;
}
// Get a unique item from the provided string array, which should contain a
// group key and an item title.
function resolveItemReference(reference) {
for (var i = 0; i < groupedItems.length; i++) {
var item = groupedItems.getAt(i);
if (item.group.key === reference[0] && item.title === reference[1]) {
return item;
}
}
}
function getFeeds() {
// Create an object for each feed.
blogs = [
{
key: "blog1", url:
'https://windowsteamblog.com/windows/b/developers/atom.aspx',
title: 'tbd', subtitle: 'subtitle', updated: 'tbd',
backgroundImage: lightGray,
acquireSyndication: acquireSyndication, dataPromise: null
},
{
key: "blog2", url:
'https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx',
title: 'tbd', subtitle: 'subtitle', updated: 'tbd',
backgroundImage: lightGray,
acquireSyndication: acquireSyndication, dataPromise: null
}]
// Get the content for each feed in the blog's array.
blogs.forEach(function (feed) {
feed.dataPromise = feed.acquireSyndication(feed.url);
dataPromises.push(feed.dataPromise);
});
// Return when all asynchronous operations are complete
return WinJS.Promise.join(dataPromises).then(function () {
return blogs;
});
};
function acquireSyndication(url) {
return WinJS.xhr({
url: url,
headers: { "If-Modified-Since": "Mon, 27 Mar 1972 00:00:00 GMT" }
});
}
function getBlogPosts() {
getFeeds().then(function () {
// Process each blog.
blogs.forEach(function (feed) {
feed.dataPromise.then(function (articlesResponse) {
var articleSyndication = articlesResponse.responseXML;
if (articleSyndication) {
// Get the blog title and last updated date.
feed.title = articleSyndication.querySelector(
"feed > title").textContent;
var ds = articleSyndication.querySelector(
"feed > updated").textContent;
var date = ds.substring(5, 7) + "-" +
ds.substring(8, 10) + "-" + ds.substring(0, 4);
feed.updated = "Last updated " + date;
// Process the blog posts.
getItemsFromXml(articleSyndication, blogPosts, feed);
}
else {
// There was an error loading the blog.
feed.title = "Error loading blog";
feed.updated = "Error";
blogPosts.push({
group: feed,
key: "Error loading blog",
title: feed.url,
author: "Unknown",
month: "?",
day: "?",
year: "?",
content: "Unable to load the blog at " + feed.url
});
}
});
});
});
return blogPosts;
}
function getItemsFromXml(articleSyndication, blogPosts, feed) {
var posts = articleSyndication.querySelectorAll("entry");
// Process each blog post.
for (var postIndex = 0; postIndex < posts.length; postIndex++) {
var post = posts[postIndex];
// Get the title, author, and date published.
var postTitle = post.querySelector("title").textContent;
var postAuthor = post.querySelector("author > name").textContent;
var pds = post.querySelector("published").textContent;
var postDate = pds.substring(5, 7) + "-" + pds.substring(8, 10)
+ "-" + pds.substring(0, 4);
// Process the content so that it displays nicely.
var staticContent = toStaticHTML(post.querySelector(
"content").textContent);
// Store the post info we care about in the array.
blogPosts.push({
group: feed, key: feed.title, title: postTitle,
author: postAuthor, pubDate: postDate,
backgroundImage: mediumGray, content: staticContent
});
}
}
})();