mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-06-18 21:30:12 +10:00
Update manager and add features for App Installer.
This commit is contained in:
+357
-27
@@ -13,6 +13,7 @@
|
||||
var childAnimeDuration = 120;
|
||||
var parentAnimeDuration = 400;
|
||||
|
||||
|
||||
function showItemAmine(node) {
|
||||
return Windows.UI.Animation.runAsync(node, [
|
||||
Windows.UI.Animation.Keyframes.Scale.up,
|
||||
@@ -27,6 +28,23 @@
|
||||
], childAnimeDuration);
|
||||
}
|
||||
|
||||
function noAnime(node) {
|
||||
return Promise.resolve(node);
|
||||
}
|
||||
|
||||
function runShowAnime(node, enable) {
|
||||
if (enable === void 0) enable = true;
|
||||
if (!enable) return noAnime(node);
|
||||
return showItemAmine(node);
|
||||
}
|
||||
|
||||
function runHideAnime(node, enable) {
|
||||
if (enable === void 0) enable = true;
|
||||
if (!enable) return noAnime(node);
|
||||
return hideItemAmine(node);
|
||||
}
|
||||
|
||||
|
||||
function updateItemAmine(node, updateCallback) {
|
||||
return Windows.UI.Animation.runAsync(node, [
|
||||
Windows.UI.Animation.Keyframes.Opacity.hidden,
|
||||
@@ -53,6 +71,43 @@
|
||||
function PMDataSource() {
|
||||
var _list = [];
|
||||
var _listeners = [];
|
||||
|
||||
var _keySelector = null;
|
||||
var _autoKeySeed = 1;
|
||||
this.setKeySelector = function(fn) {
|
||||
_keySelector = (typeof fn === "function") ? fn : null;
|
||||
};
|
||||
|
||||
function getKey(item) {
|
||||
if (!item) return null;
|
||||
|
||||
// 用户提供
|
||||
if (_keySelector) {
|
||||
return _keySelector(item);
|
||||
}
|
||||
|
||||
// 自动注入(对象)
|
||||
if (typeof item === "object") {
|
||||
if (item.__pm_key !== void 0) {
|
||||
return item.__pm_key;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.defineProperty(item, "__pm_key", {
|
||||
value: "pm_" + (_autoKeySeed++),
|
||||
enumerable: false
|
||||
});
|
||||
} catch (e) {
|
||||
// IE10 兜底
|
||||
item.__pm_key = "pm_" + (_autoKeySeed++);
|
||||
}
|
||||
return item.__pm_key;
|
||||
}
|
||||
|
||||
// 原始类型兜底
|
||||
return typeof item + ":" + item;
|
||||
}
|
||||
|
||||
this.subscribe = function(fn) {
|
||||
if (typeof fn === "function") {
|
||||
_listeners.push(fn);
|
||||
@@ -64,26 +119,47 @@
|
||||
_listeners[i](evt);
|
||||
}
|
||||
}
|
||||
this.indexOf = function(item) {
|
||||
var key = getKey(item);
|
||||
for (var i = 0; i < _list.length; i++) {
|
||||
if (getKey(_list[i]) === key) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
this.add = function(item) {
|
||||
_list.push(item);
|
||||
emit(new PMChangeEvent(
|
||||
DataView.ChangeType.add, [item], { index: _list.length - 1 }
|
||||
DataView.ChangeType.add, [{ item: item, index: _list.length - 1, key: getKey(item) }]
|
||||
));
|
||||
};
|
||||
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 }
|
||||
DataView.ChangeType.remove, [{ item: item, index: index, key: getKey(item) }]
|
||||
));
|
||||
};
|
||||
this.remove = function(item) {
|
||||
var index = this.indexOf(item);
|
||||
if (index >= 0) {
|
||||
this.removeAt(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 }
|
||||
DataView.ChangeType.change, [{ item: newItem, index: index, key: getKey(newItem) }]
|
||||
));
|
||||
};
|
||||
this.change = function(oldItem, newItem) {
|
||||
var index = this.indexOf(oldItem);
|
||||
if (index >= 0) {
|
||||
this.changeAt(index, newItem);
|
||||
}
|
||||
};
|
||||
this.clear = function() {
|
||||
_list.length = 0;
|
||||
emit(new PMChangeEvent(
|
||||
@@ -224,7 +300,6 @@
|
||||
var changed = [];
|
||||
var removed = [];
|
||||
|
||||
// 1️⃣ 找 remove
|
||||
for (i = oldList.length - 1; i >= 0; i--) {
|
||||
var oldItem = oldList[i];
|
||||
var oldKey = getKey(oldItem);
|
||||
@@ -238,7 +313,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 2️⃣ 找 add / change
|
||||
for (i = 0; i < newList.length; i++) {
|
||||
var newItem = newList[i];
|
||||
var newKey = getKey(newItem);
|
||||
@@ -263,7 +337,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 3️⃣ 执行 remove(从后往前)
|
||||
if (removed.length > 0) {
|
||||
for (i = 0; i < removed.length; i++) {
|
||||
_list.splice(removed[i].index, 1);
|
||||
@@ -274,7 +347,6 @@
|
||||
));
|
||||
}
|
||||
|
||||
// 4️⃣ 执行 add / change(重建顺序)
|
||||
_list = newList.slice(0);
|
||||
|
||||
if (added.length > 0) {
|
||||
@@ -291,25 +363,50 @@
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
this._getKey = getKey;
|
||||
}
|
||||
var MAX_ANIMATE_COUNT = 100;
|
||||
|
||||
function PMDataListView(container, templateFn) {
|
||||
this.container = container;
|
||||
this.templateFn = templateFn;
|
||||
this.listViewControl = this;
|
||||
this._emptyView = null;
|
||||
// === 新增 ===
|
||||
this._filter = null;
|
||||
|
||||
this._searchHandler = null;
|
||||
this._searchText = null;
|
||||
this._searchSuggestProvider = null;
|
||||
|
||||
this.onSearchSuggest = null; // function(text, list)
|
||||
this._isSearching = false;
|
||||
this.onsearchstart = null;
|
||||
this.onsearchend = null;
|
||||
}
|
||||
PMDataListView.prototype.bind = function(ds) {
|
||||
var self = this;
|
||||
var items = ds.get();
|
||||
this._ds = ds;
|
||||
|
||||
self.container.innerHTML = "";
|
||||
|
||||
var items = ds.get();
|
||||
|
||||
// 动画队列,保证异步操作不会乱序
|
||||
var queue = Promise.resolve();
|
||||
|
||||
function renderItem(data, index) {
|
||||
var el = self.templateFn(data, index);
|
||||
|
||||
var key = ds && ds._getKey ? ds._getKey(data) : null;
|
||||
|
||||
el.__pm_item = data;
|
||||
el.__pm_key = key;
|
||||
|
||||
if (key != null) {
|
||||
el.setAttribute("data-pm-key", key);
|
||||
}
|
||||
|
||||
el.addEventListener("click", function() {
|
||||
self._toggleSelect(el);
|
||||
});
|
||||
@@ -317,10 +414,13 @@
|
||||
return el;
|
||||
}
|
||||
|
||||
|
||||
// 初始化渲染
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
self.container.appendChild(renderItem(items[i], i));
|
||||
}
|
||||
// 初始化 emptyView 状态
|
||||
self._updateEmptyView();
|
||||
|
||||
ds.subscribe(function(evt) {
|
||||
|
||||
@@ -337,37 +437,58 @@
|
||||
var nodes = [];
|
||||
for (var i = 0; i < datas.length; i++) {
|
||||
var n = renderItem(datas[i].item, datas[i].index);
|
||||
n.style.display = "none";
|
||||
nodes.push(n);
|
||||
self.container.appendChild(n);
|
||||
}
|
||||
|
||||
// 如果数量>=20,动画并行,否则串行
|
||||
if (datas.length >= 20) {
|
||||
var enableAnime = datas.length <= MAX_ANIMATE_COUNT;
|
||||
if (!enableAnime) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// 如果数量>=20,动画串行,否则并行
|
||||
if (datas.length <= 20) {
|
||||
var promises = [];
|
||||
for (var j = 0; j < nodes.length; j++) {
|
||||
promises.push(showItemAmine(nodes[j]));
|
||||
promises.push((function(node) {
|
||||
node.style.display = "";
|
||||
return showItemAmine(node);
|
||||
})(nodes[j]));
|
||||
}
|
||||
return Promise.all(promises);
|
||||
} else {
|
||||
// 串行
|
||||
var p = Promise.resolve();
|
||||
var group = [];
|
||||
for (var k = 0; k < nodes.length; k++) {
|
||||
(function(node) {
|
||||
p = p.then(function() {
|
||||
group.push((function(node) {
|
||||
node.style.display = "";
|
||||
return showItemAmine(node);
|
||||
});
|
||||
})(nodes[k]);
|
||||
})
|
||||
(nodes[k]));
|
||||
if (group.length === 20 || k === nodes.length - 1) {
|
||||
(function(g) {
|
||||
p = p.then(function() {
|
||||
return Promise.join(g);
|
||||
});
|
||||
})(group);
|
||||
group = [];
|
||||
}
|
||||
}
|
||||
(function(g) {
|
||||
p = p.then(function() {
|
||||
return Promise.join(g);
|
||||
});
|
||||
})(group);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
case DataView.ChangeType.remove:
|
||||
{
|
||||
var node = self.container.children[evt.detail.index];
|
||||
var info = evt.datas[0];
|
||||
var node = self._findNodeByKey(info.key);
|
||||
if (!node) return;
|
||||
|
||||
// 隐藏动画完成后再移除
|
||||
return hideItemAmine(node).then(function() {
|
||||
self.container.removeChild(node);
|
||||
});
|
||||
@@ -375,29 +496,30 @@
|
||||
|
||||
case DataView.ChangeType.change:
|
||||
{
|
||||
var oldNode = self.container.children[evt.detail.index];
|
||||
var info = evt.datas[0];
|
||||
var oldNode = self._findNodeByKey(info.key);
|
||||
if (!oldNode) return;
|
||||
|
||||
// 先淡出旧节点
|
||||
return hideItemAmine(oldNode).then(function() {
|
||||
// 替换节点
|
||||
var newNode = renderItem(evt.datas[0], evt.detail.index);
|
||||
var newNode = renderItem(info.item);
|
||||
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 info = evt.datas[0];
|
||||
var node = self._findNodeByKey(info.key);
|
||||
if (!node) return;
|
||||
|
||||
var ref = self.container.children[evt.detail.to] || null;
|
||||
if (node) self.container.insertBefore(node, ref);
|
||||
self.container.insertBefore(node, ref);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -410,9 +532,22 @@
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
promises.push(self._refreshVisibility());
|
||||
return Promise.join(promises);
|
||||
});
|
||||
});
|
||||
};
|
||||
PMDataListView.prototype._findNodeByKey = function(key) {
|
||||
if (key == null) return null;
|
||||
|
||||
var children = this.container.children;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
if (children[i].__pm_key === key) {
|
||||
return children[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
PMDataListView.prototype._toggleSelect = function(ele) {
|
||||
// 如果选择模式为 none,则不处理
|
||||
@@ -472,7 +607,202 @@
|
||||
return Array.prototype.slice.call(this.container.querySelectorAll(".selected"));
|
||||
}
|
||||
});
|
||||
PMDataListView.prototype._updateEmptyView = function() {
|
||||
if (!this._emptyView) return;
|
||||
|
||||
// container 中是否还有 item
|
||||
var hasItem = this.container.children.length > 0;
|
||||
|
||||
if (hasItem) {
|
||||
if (this._emptyView.parentNode) {
|
||||
this._emptyView.style.display = "none";
|
||||
}
|
||||
} else {
|
||||
if (!this._emptyView.parentNode) {
|
||||
this.container.appendChild(this._emptyView);
|
||||
}
|
||||
this._emptyView.style.display = "";
|
||||
}
|
||||
};
|
||||
Object.defineProperty(PMDataListView.prototype, "emptyView", {
|
||||
get: function() {
|
||||
return this._emptyView;
|
||||
},
|
||||
set: function(value) {
|
||||
// 只接受 HTMLElement 或 null / undefined
|
||||
if (value !== null && value !== void 0 && !(value instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除旧的
|
||||
if (this._emptyView && this._emptyView.parentNode) {
|
||||
this._emptyView.parentNode.removeChild(this._emptyView);
|
||||
}
|
||||
|
||||
this._emptyView = value || null;
|
||||
|
||||
// 设置后立刻刷新一次
|
||||
this._updateEmptyView();
|
||||
}
|
||||
});
|
||||
PMDataListView.prototype._isItemVisible = function(item) {
|
||||
// 1️⃣ filter
|
||||
if (this._filter) {
|
||||
if (!this._filter(item)) return false;
|
||||
}
|
||||
|
||||
// 2️⃣ search(自动启用 / 禁用)
|
||||
var handler = this._searchHandler;
|
||||
var text = this._searchText;
|
||||
|
||||
if (typeof handler === "function") {
|
||||
if (text != null) {
|
||||
text = ("" + text).replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
|
||||
if (text && text.length > 0) {
|
||||
if (!handler(text, item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
PMDataListView.prototype._refreshVisibility = function() {
|
||||
var self = this;
|
||||
var children = self.container.children;
|
||||
var animes = [];
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
(function(node) {
|
||||
var item = node.__pm_item;
|
||||
if (!item) return;
|
||||
|
||||
var visible = self._isItemVisible(item);
|
||||
|
||||
var enableAnime = animes.length < MAX_ANIMATE_COUNT;
|
||||
if (visible) {
|
||||
if (node.style.display === "none") {
|
||||
node.style.display = "";
|
||||
animes.push(runShowAnime(node, enableAnime));
|
||||
}
|
||||
} else {
|
||||
if (node.style.display !== "none") {
|
||||
// 移除选择状态
|
||||
node.classList.remove("selected");
|
||||
animes.push(runHideAnime(node, enableAnime).then(function() {
|
||||
node.style.display = "none";
|
||||
}));
|
||||
}
|
||||
}
|
||||
})(children[i]);
|
||||
}
|
||||
return Promise.join(animes);
|
||||
};
|
||||
Object.defineProperty(PMDataListView.prototype, "filter", {
|
||||
get: function() {
|
||||
return this._filter;
|
||||
},
|
||||
set: function(fn) {
|
||||
this._filter = (typeof fn === "function") ? fn : null;
|
||||
this._refreshVisibility();
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PMDataListView.prototype, "searchHandler", {
|
||||
get: function() {
|
||||
return this._searchHandler;
|
||||
},
|
||||
set: function(fn) {
|
||||
this._searchHandler = (typeof fn === "function") ? fn : null;
|
||||
this._refreshVisibility();
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PMDataListView.prototype, "searchText", {
|
||||
get: function() {
|
||||
return this._searchText;
|
||||
},
|
||||
set: function(text) {
|
||||
var oldText = this._searchText;
|
||||
|
||||
this._searchText = text;
|
||||
|
||||
var oldActive = !!(oldText && oldText.trim());
|
||||
var newActive = !!(text && ("" + text).trim());
|
||||
|
||||
//if (!oldActive && newActive) {
|
||||
this._isSearching = true;
|
||||
this._emitSearchEvent("searchstart");
|
||||
//}
|
||||
|
||||
var handler = this._searchHandler;
|
||||
var provider = this._searchSuggestProvider;
|
||||
var cb = this.onSearchSuggest;
|
||||
|
||||
var t = text;
|
||||
if (t != null) {
|
||||
t = ("" + t).replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
|
||||
// 搜索建议
|
||||
if (
|
||||
typeof handler === "function" &&
|
||||
t &&
|
||||
t.length > 0 &&
|
||||
typeof provider === "function" &&
|
||||
typeof cb === "function"
|
||||
) {
|
||||
var list = provider(t);
|
||||
if (list && list.length) {
|
||||
cb(t, list.slice(0, 10));
|
||||
}
|
||||
}
|
||||
var self = this;
|
||||
var func = function() {
|
||||
//if (oldActive && !newActive) {
|
||||
self._isSearching = false;
|
||||
self._emitSearchEvent("searchend");
|
||||
//}
|
||||
};
|
||||
this._refreshVisibility().done(func, func);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PMDataListView.prototype, "searchSuggestProvider", {
|
||||
get: function() {
|
||||
return this._searchSuggestProvider;
|
||||
},
|
||||
set: function(fn) {
|
||||
this._searchSuggestProvider = (typeof fn === "function") ? fn : null;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PMDataListView.prototype, "findItemLength", {
|
||||
get: function() {
|
||||
var count = 0;
|
||||
var children = this.container.children;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var item = children[i].__pm_item;
|
||||
if (this._isItemVisible(item)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
});
|
||||
PMDataListView.prototype._emitSearchEvent = function(type) {
|
||||
if (typeof this["on" + type] === "function") {
|
||||
try {
|
||||
this["on" + type].call(this);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
try {
|
||||
var ev = document.createEvent("Event");
|
||||
ev.initEvent(type, true, true);
|
||||
this.container.dispatchEvent(ev);
|
||||
} catch (e) {}
|
||||
};
|
||||
PMDataListView.prototype.refresh = function() {
|
||||
this._refreshVisibility();
|
||||
};
|
||||
global.DataView.ChangeEvent = PMChangeEvent;
|
||||
global.DataView.DataSource = PMDataSource;
|
||||
global.DataView.ListView = PMDataListView;
|
||||
|
||||
Reference in New Issue
Block a user