mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-04-11 17:57:19 +10:00
479 lines
16 KiB
JavaScript
479 lines
16 KiB
JavaScript
(function(global) {
|
||
"use strict";
|
||
global.DataView = {
|
||
ChangeType: {
|
||
add: "add",
|
||
remove: "remove",
|
||
change: "change",
|
||
clear: "clear",
|
||
move: "move",
|
||
sort: "sort",
|
||
},
|
||
};
|
||
var childAnimeDuration = 120;
|
||
var parentAnimeDuration = 400;
|
||
|
||
function showItemAmine(node) {
|
||
return Windows.UI.Animation.runAsync(node, [
|
||
Windows.UI.Animation.Keyframes.Scale.up,
|
||
Windows.UI.Animation.Keyframes.Opacity.visible,
|
||
], childAnimeDuration);
|
||
}
|
||
|
||
function hideItemAmine(node) {
|
||
return Windows.UI.Animation.runAsync(node, [
|
||
Windows.UI.Animation.Keyframes.Scale.down,
|
||
Windows.UI.Animation.Keyframes.Opacity.hidden,
|
||
], childAnimeDuration);
|
||
}
|
||
|
||
function updateItemAmine(node, updateCallback) {
|
||
return Windows.UI.Animation.runAsync(node, [
|
||
Windows.UI.Animation.Keyframes.Opacity.hidden,
|
||
Windows.UI.Animation.Keyframes.Scale.down
|
||
], 120).then(function() {
|
||
if (updateCallback && typeof updateCallback === 'function') {
|
||
updateCallback(node);
|
||
}
|
||
return Windows.UI.Animation.runAsync(node, [
|
||
Windows.UI.Animation.Keyframes.Opacity.visible,
|
||
Windows.UI.Animation.Keyframes.Scale.up
|
||
], 120);
|
||
}).then(function() {
|
||
return node;
|
||
});
|
||
}
|
||
|
||
function PMChangeEvent(type, datas, detailOperation) {
|
||
this.type = type; // ChangeType
|
||
this.datas = datas || []; // 受影响的数据
|
||
this.detail = detailOperation || null;
|
||
}
|
||
|
||
function PMDataSource() {
|
||
var _list = [];
|
||
var _listeners = [];
|
||
this.subscribe = function(fn) {
|
||
if (typeof fn === "function") {
|
||
_listeners.push(fn);
|
||
}
|
||
};
|
||
|
||
function emit(evt) {
|
||
for (var i = 0; i < _listeners.length; i++) {
|
||
_listeners[i](evt);
|
||
}
|
||
}
|
||
this.add = function(item) {
|
||
_list.push(item);
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.add, [item], { index: _list.length - 1 }
|
||
));
|
||
};
|
||
this.removeAt = function(index) {
|
||
if (index < 0 || index >= _list.length) return;
|
||
var item = _list.splice(index, 1)[0];
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.remove, [item], { index: index }
|
||
));
|
||
};
|
||
this.changeAt = function(index, newItem) {
|
||
if (index < 0 || index >= _list.length) return;
|
||
_list[index] = newItem;
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.change, [newItem], { index: index }
|
||
));
|
||
};
|
||
this.clear = function() {
|
||
_list.length = 0;
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.clear
|
||
));
|
||
};
|
||
this.move = function(from, to) {
|
||
if (from === to ||
|
||
from < 0 || to < 0 ||
|
||
from >= _list.length || to >= _list.length) {
|
||
return;
|
||
}
|
||
var item = _list.splice(from, 1)[0];
|
||
_list.splice(to, 0, item);
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.move, [item], { from: from, to: to }
|
||
));
|
||
};
|
||
this.sort = function(compareFn) {
|
||
_list.sort(compareFn);
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.sort,
|
||
_list.slice(0), { compare: compareFn }
|
||
));
|
||
};
|
||
this.get = function() {
|
||
return _list.slice(0);
|
||
};
|
||
this.addList = function(list, keySelector) {
|
||
if (!list || !list.length) return;
|
||
|
||
var added = [];
|
||
var changed = [];
|
||
|
||
var useKey = keySelector !== void 0;
|
||
var getKey;
|
||
|
||
if (keySelector === null) {
|
||
getKey = function(item) {
|
||
return item && item.id;
|
||
};
|
||
} else if (typeof keySelector === "function") {
|
||
getKey = keySelector;
|
||
}
|
||
|
||
for (var i = 0; i < list.length; i++) {
|
||
var item = list[i];
|
||
|
||
if (!useKey) {
|
||
_list.push(item);
|
||
added.push({ item: item, index: _list.length - 1 });
|
||
continue;
|
||
}
|
||
|
||
var key = getKey(item);
|
||
if (key === void 0) {
|
||
_list.push(item);
|
||
added.push({ item: item, index: _list.length - 1, key: key });
|
||
continue;
|
||
}
|
||
|
||
var found = -1;
|
||
for (var j = 0; j < _list.length; j++) {
|
||
if (getKey(_list[j]) === key) {
|
||
found = j;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (found >= 0) {
|
||
_list[found] = item;
|
||
changed.push({ item: item, index: found, key: key });
|
||
} else {
|
||
_list.push(item);
|
||
added.push({ item: item, index: _list.length - 1, key: key });
|
||
}
|
||
}
|
||
|
||
// 统一发出一个事件
|
||
if (added.length > 0) {
|
||
emit(new PMChangeEvent(DataView.ChangeType.add, added));
|
||
}
|
||
if (changed.length > 0) {
|
||
emit(new PMChangeEvent(DataView.ChangeType.change, changed));
|
||
}
|
||
};
|
||
this.updateList = function(list, fnGetKey) {
|
||
if (!list) list = [];
|
||
|
||
var getKey;
|
||
|
||
if (fnGetKey === null) {
|
||
getKey = function(item) {
|
||
return item && item.id;
|
||
};
|
||
} else if (typeof fnGetKey === "function") {
|
||
getKey = fnGetKey;
|
||
} else {
|
||
// 不提供 key:直接整体替换
|
||
_list = list.slice(0);
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.clear
|
||
));
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.add,
|
||
list.map(function(item, index) {
|
||
return { item: item, index: index };
|
||
})
|
||
));
|
||
return;
|
||
}
|
||
|
||
var oldList = _list;
|
||
var newList = list;
|
||
|
||
var oldKeyIndex = {};
|
||
var newKeyIndex = {};
|
||
|
||
var i;
|
||
|
||
// 建立旧列表 key → index
|
||
for (i = 0; i < oldList.length; i++) {
|
||
var ok = getKey(oldList[i]);
|
||
if (ok !== void 0) {
|
||
oldKeyIndex[ok] = i;
|
||
}
|
||
}
|
||
|
||
// 建立新列表 key → index
|
||
for (i = 0; i < newList.length; i++) {
|
||
var nk = getKey(newList[i]);
|
||
if (nk !== void 0) {
|
||
newKeyIndex[nk] = i;
|
||
}
|
||
}
|
||
|
||
var added = [];
|
||
var changed = [];
|
||
var removed = [];
|
||
|
||
// 1️⃣ 找 remove
|
||
for (i = oldList.length - 1; i >= 0; i--) {
|
||
var oldItem = oldList[i];
|
||
var oldKey = getKey(oldItem);
|
||
|
||
if (oldKey === void 0 || newKeyIndex[oldKey] === void 0) {
|
||
removed.push({
|
||
item: oldItem,
|
||
index: i,
|
||
key: oldKey
|
||
});
|
||
}
|
||
}
|
||
|
||
// 2️⃣ 找 add / change
|
||
for (i = 0; i < newList.length; i++) {
|
||
var newItem = newList[i];
|
||
var newKey = getKey(newItem);
|
||
|
||
if (newKey === void 0 || oldKeyIndex[newKey] === void 0) {
|
||
added.push({
|
||
item: newItem,
|
||
index: i,
|
||
key: newKey
|
||
});
|
||
} else {
|
||
var oldIndex = oldKeyIndex[newKey];
|
||
var oldItem2 = oldList[oldIndex];
|
||
|
||
if (oldItem2 !== newItem) {
|
||
changed.push({
|
||
item: newItem,
|
||
index: oldIndex,
|
||
key: newKey
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3️⃣ 执行 remove(从后往前)
|
||
if (removed.length > 0) {
|
||
for (i = 0; i < removed.length; i++) {
|
||
_list.splice(removed[i].index, 1);
|
||
}
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.remove,
|
||
removed
|
||
));
|
||
}
|
||
|
||
// 4️⃣ 执行 add / change(重建顺序)
|
||
_list = newList.slice(0);
|
||
|
||
if (added.length > 0) {
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.add,
|
||
added
|
||
));
|
||
}
|
||
|
||
if (changed.length > 0) {
|
||
emit(new PMChangeEvent(
|
||
DataView.ChangeType.change,
|
||
changed
|
||
));
|
||
}
|
||
};
|
||
|
||
}
|
||
|
||
function PMDataListView(container, templateFn) {
|
||
this.container = container;
|
||
this.templateFn = templateFn;
|
||
this.listViewControl = this;
|
||
}
|
||
PMDataListView.prototype.bind = function(ds) {
|
||
var self = this;
|
||
var items = ds.get();
|
||
self.container.innerHTML = "";
|
||
|
||
// 动画队列,保证异步操作不会乱序
|
||
var queue = Promise.resolve();
|
||
|
||
function renderItem(data, index) {
|
||
var el = self.templateFn(data, index);
|
||
|
||
el.addEventListener("click", function() {
|
||
self._toggleSelect(el);
|
||
});
|
||
|
||
return el;
|
||
}
|
||
|
||
// 初始化渲染
|
||
for (var i = 0; i < items.length; i++) {
|
||
self.container.appendChild(renderItem(items[i], i));
|
||
}
|
||
|
||
ds.subscribe(function(evt) {
|
||
|
||
// 把每次事件放进队列,保证顺序执行
|
||
queue = queue.then(function() {
|
||
switch (evt.type) {
|
||
|
||
case DataView.ChangeType.add:
|
||
{
|
||
// evt.datas = [{item, index}, ...]
|
||
var datas = evt.datas;
|
||
|
||
// 先批量 append 到 DOM(顺序必须保持)
|
||
var nodes = [];
|
||
for (var i = 0; i < datas.length; i++) {
|
||
var n = renderItem(datas[i].item, datas[i].index);
|
||
nodes.push(n);
|
||
self.container.appendChild(n);
|
||
}
|
||
|
||
// 如果数量>=20,动画并行,否则串行
|
||
if (datas.length >= 20) {
|
||
var promises = [];
|
||
for (var j = 0; j < nodes.length; j++) {
|
||
promises.push(showItemAmine(nodes[j]));
|
||
}
|
||
return Promise.all(promises);
|
||
} else {
|
||
// 串行
|
||
var p = Promise.resolve();
|
||
for (var k = 0; k < nodes.length; k++) {
|
||
(function(node) {
|
||
p = p.then(function() {
|
||
return showItemAmine(node);
|
||
});
|
||
})(nodes[k]);
|
||
}
|
||
return p;
|
||
}
|
||
}
|
||
|
||
case DataView.ChangeType.remove:
|
||
{
|
||
var node = self.container.children[evt.detail.index];
|
||
if (!node) return;
|
||
|
||
// 隐藏动画完成后再移除
|
||
return hideItemAmine(node).then(function() {
|
||
self.container.removeChild(node);
|
||
});
|
||
}
|
||
|
||
case DataView.ChangeType.change:
|
||
{
|
||
var oldNode = self.container.children[evt.detail.index];
|
||
if (!oldNode) return;
|
||
|
||
// 先淡出旧节点
|
||
return hideItemAmine(oldNode).then(function() {
|
||
// 替换节点
|
||
var newNode = renderItem(evt.datas[0], evt.detail.index);
|
||
self.container.replaceChild(newNode, oldNode);
|
||
|
||
// 再淡入新节点
|
||
return showItemAmine(newNode);
|
||
});
|
||
}
|
||
|
||
case DataView.ChangeType.clear:
|
||
self.container.innerHTML = "";
|
||
return Promise.resolve();
|
||
|
||
case DataView.ChangeType.move:
|
||
{
|
||
var node = self.container.children[evt.detail.from];
|
||
var ref = self.container.children[evt.detail.to] || null;
|
||
if (node) self.container.insertBefore(node, ref);
|
||
return Promise.resolve();
|
||
}
|
||
|
||
case DataView.ChangeType.sort:
|
||
{
|
||
self.container.innerHTML = "";
|
||
for (var i = 0; i < evt.datas.length; i++) {
|
||
self.container.appendChild(renderItem(evt.datas[i], i));
|
||
}
|
||
return Promise.resolve();
|
||
}
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
PMDataListView.prototype._toggleSelect = function(ele) {
|
||
// 如果选择模式为 none,则不处理
|
||
if (this.selectionMode === "none") return;
|
||
|
||
var isSelected = ele.classList.contains("selected");
|
||
|
||
if (this.selectionMode === "single") {
|
||
// 单选:先取消所有选中
|
||
this._clearSelected();
|
||
if (!isSelected) {
|
||
ele.classList.add("selected");
|
||
}
|
||
} else if (this.selectionMode === "multiple") {
|
||
// 多选:点一次切换状态
|
||
if (isSelected) {
|
||
ele.classList.remove("selected");
|
||
} else {
|
||
ele.classList.add("selected");
|
||
}
|
||
}
|
||
};
|
||
PMDataListView.prototype._clearSelected = function() {
|
||
var selected = this.container.querySelectorAll(".selected");
|
||
for (var i = 0; i < selected.length; i++) {
|
||
selected[i].classList.remove("selected");
|
||
}
|
||
};
|
||
Object.defineProperty(PMDataListView.prototype, "selectionMode", {
|
||
get: function() {
|
||
return this._selectionMode || "none";
|
||
},
|
||
set: function(value) {
|
||
var mode = String(value).toLowerCase();
|
||
if (mode !== "none" && mode !== "single" && mode !== "multiple") {
|
||
mode = "none";
|
||
}
|
||
this._selectionMode = mode;
|
||
|
||
// 切换模式时,清空选中状态(可选)
|
||
if (mode === "none") {
|
||
this._clearSelected();
|
||
}
|
||
if (mode === "single") {
|
||
// 单选模式:如果多选了多个,保留第一个
|
||
var selected = this.container.querySelectorAll(".selected");
|
||
if (selected.length > 1) {
|
||
for (var i = 1; i < selected.length; i++) {
|
||
selected[i].classList.remove("selected");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
Object.defineProperty(PMDataListView.prototype, "selectedItems", {
|
||
get: function() {
|
||
return Array.prototype.slice.call(this.container.querySelectorAll(".selected"));
|
||
}
|
||
});
|
||
|
||
global.DataView.ChangeEvent = PMChangeEvent;
|
||
global.DataView.DataSource = PMDataSource;
|
||
global.DataView.ListView = PMDataListView;
|
||
})(this); |