Files
App-Installer-For-Windows-8…/shared/html/js/pagemgr.js
T
2026-04-04 19:27:45 +08:00

361 lines
13 KiB
JavaScript

(function(global) {
"use strict";
var eu = Windows.UI.Event.Util;
var anime = Windows.UI.Animation;
function PagePair(guideNode, pageNode, respHandler) {
var _guide = guideNode;
var _page = pageNode;
var _handler = respHandler || null;
Object.defineProperty(this, "guide", {
get: function() { return _guide; },
set: function(value) { _guide = value; }
});
Object.defineProperty(this, "page", {
get: function() { return _page; },
set: function(value) { _page = value; }
});
Object.defineProperty(this, "handler", {
get: function() { return _handler; },
set: function(value) { _handler = value; }
});
}
function PageManager() {
var dict = {};
var stack = [];
var current = -1;
var record = {}; // 记录哪些界面已经第一次加载过
var paramStack = [];
// scrollStack 与 stack 对齐:scrollStack[i] 对应 stack[i]
var scrollStack = [];
var nowScroll = 0;
var events = {
firstload: [],
beforeload: [],
load: [],
afterload: [],
willunload: [],
unload: []
};
function addHandler(type, fn) {
if (typeof fn !== "function") return;
events[type].push(fn);
}
function removeHandler(type, fn) {
var list = events[type];
for (var i = list.length - 1; i >= 0; i--) {
if (list[i] === fn) {
list.splice(i, 1);
}
}
}
function emit(type, arg) {
var list = events[type];
for (var i = 0; i < list.length; i++) {
try {
list[i](arg);
} catch (e) {}
}
}
function emitCancelable(type, arg) {
var list = events[type];
for (var i = 0; i < list.length; i++) {
try {
var r = list[i](arg);
if (r === false) return false;
} catch (e) {}
}
return true;
}
/**
* 添加载入事件
* @param {string} type 支持:"firstload"
"beforeload"
"load"
"afterload"
"willunload"
"unload"
* @param {function} fn
*/
this.addEventListener = function(type, fn) {
addHandler(type, fn);
};
/**
* 移除载入事件
* @param {string} type 支持:"firstload"
"beforeload"
"load"
"afterload"
"willunload"
"unload"
* @param {function} fn
*/
this.removeEventListener = function(type, fn) {
removeHandler(type, fn);
};
function guideClickHandler(e) {
var tag = this.__pageTag;
if (!tag) return;
if (this.classList.contains("selected")) return;
self.go(tag);
return;
var keys = Object.keys(dict);
var promises = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var pair = dict[key];
if (pair.guide.classList.contains("selected")) {
promises.push(anime.runAsync(
pair.page, [
anime.Keyframes.Opacity.hidden,
anime.Keyframes.Scale.down
]
).then(function(el) {
el.style.display = "none";
}));
}
}
this.classList.add("selected");
var after = Promise.join(promises);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var pair = dict[key];
if (pair.guide.classList.contains("selected")) {
pair.page.style.display = "";
after.then(function() {
anime.runAsync(
pair.page, [
anime.Keyframes.Opacity.visible,
anime.Keyframes.Flyout.toLeft
]
);
});
}
}
}
var self = this;
function _activate(tag, args, fromHistory) {
var pair = dict[tag];
if (!pair) throw "Page not found: " + tag;
if (!emitCancelable("beforeload", tag)) {
return;
}
var keys = Object.keys(dict);
var promises = [];
var oldTags = [];
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
var p = dict[k];
if (p.guide.classList.contains("selected") && k !== tag) {
if (!emitCancelable("willunload", k)) {
return;
}
oldTags.push(k);
promises.push(
(function(page, key) {
// 返回 anime.runAsync 产生的 Promise
return anime.runAsync(page, [anime.Keyframes.Opacity.hidden])
.then(function() {
page.style.display = "none";
page.style.opacity = 0;
page.style.height = 0;
emit("unload", key);
});
})(p.page, k)
);
p.guide.classList.remove("selected");
}
}
pair.guide.classList.add("selected");
pair.page.style.display = "";
emit("load", tag);
var after = Promise.join(promises);
after.then(function() {
if (!record[tag]) {
record[tag] = true;
emit("firstload", tag);
}
pair.page.style.opacity = 1;
if (pair.handler) {
// fix: use pair.handler
pair.handler(args);
}
try {
setTimeout(function(tnode) {
try {
tnode.scrollTop = nowScroll || 0;
} catch (ex) {}
}, 10, pair.page.parentNode);
} catch (ex) {}
return anime.runAsync(pair.page, [
anime.Keyframes.Opacity.visible,
anime.Keyframes.Flyout.toLeft
]).then(function() {
});
}).then(function() {
emit("afterload", tag);
});
}
this.register = function(tag, guideNode, pageNode, respHandler) {
pageNode.style.opacity = 0;
dict[tag] = new PagePair(guideNode, pageNode, respHandler);
guideNode.__pageTag = tag;
try {
eu.removeEvent(guideNode, "click", guideClickHandler);
eu.addEvent(guideNode, "click", guideClickHandler);
} catch (e) {}
};
this.edit = function(tag, pagePair) {
try {
if (dict[tag] && dict[tag].guide) {
dict[tag].guide.__pageTag = null;
}
} catch (e) {}
dict[tag] = pagePair;
try {
pagePair.guide.__pageTag = tag;
eu.removeEvent(pagePair.guide, "click", guideClickHandler);
eu.addEvent(pagePair.guide, "click", guideClickHandler);
} catch (e) {}
};
this.get = function(tag) {
return dict[tag];
};
this.getGuide = function(tag) {
return dict[tag].guide;
};
this.getPage = function(tag) {
return dict[tag].page;
};
this.getHandler = function(tag) {
return dict[tag].handler;
};
this.setGuide = function(tag, guideNode) {
try {
if (dict[tag] && dict[tag].guide) {
eu.removeEvent(dict[tag].guide, "click", guideClickHandler);
dict[tag].guide.__pageTag = null;
}
} catch (e) {}
dict[tag].guide = guideNode;
try {
guideNode.__pageTag = tag;
eu.removeEvent(guideNode, "click", guideClickHandler);
eu.addEvent(guideNode, "click", guideClickHandler);
} catch (e) {}
};
this.setPage = function(tag, pageNode) {
dict[tag].page = pageNode;
};
this.setHandler = function(tag, handler) {
dict[tag].handler = handler;
};
this.remove = function(tag) {
try {
try {
if (dict[tag] && dict[tag].guide) {
eu.removeEvent(dict[tag].guide, "click", guideClickHandler);
}
} catch (e) {}
delete dict[tag];
} catch (e) {}
};
this.clear = function() {
try {
var keys = Object.keys(dict);
for (var i = 0; i < keys.length; i++) {
this.remove(keys[i]);
}
} catch (e) {}
};
this.jump = function(tag, args) {
_activate(tag, args, true);
};
this.go = function(tag, params) {
// limit history
if (stack.length > 300) {
stack.length = 0;
paramStack.length = 0;
scrollStack.length = 0;
current = -1;
}
// if we are in the middle, truncate forward history
if (current < stack.length - 1) {
stack.splice(current + 1);
paramStack.splice(current + 1);
scrollStack.splice(current + 1);
}
// save current page scrollTop
try {
if (current >= 0 && stack[current] && dict[stack[current]] && dict[stack[current]].page && dict[stack[current]].page.parentNode) {
scrollStack[current] = dict[stack[current]].page.parentNode.scrollTop;
}
} catch (e) {}
// push new entry
stack.push(tag);
paramStack.push(params);
// initialize scroll value for the new page (will be used if user goes back to it later)
scrollStack.push(0);
current++;
_activate(tag, params, false);
};
this.back = function() {
if (current <= 0) return false;
// save scroll of current page
try {
if (stack[current] && dict[stack[current]] && dict[stack[current]].page && dict[stack[current]].page.parentNode) {
scrollStack[current] = dict[stack[current]].page.parentNode.scrollTop;
}
} catch (e) {}
// move back
current--;
// restore scroll for new current
nowScroll = scrollStack[current] || 0;
_activate(stack[current], paramStack[current], true);
return true;
};
this.next = function() {
if (current >= stack.length - 1) return false;
// save scroll of current page
try {
if (stack[current] && dict[stack[current]] && dict[stack[current]].page && dict[stack[current]].page.parentNode) {
scrollStack[current] = dict[stack[current]].page.parentNode.scrollTop;
}
} catch (e) {}
// move forward
current++;
// restore scroll for new current
nowScroll = scrollStack[current] || 0;
_activate(stack[current], paramStack[current], true);
return true;
};
Object.defineProperty(this, "current", {
get: function() { return stack[current]; },
set: function(value) {
if (value < 0 || value >= stack.length) return;
current = value;
// restore scroll for assigned current
nowScroll = scrollStack[current] || 0;
_activate(stack[current], paramStack[current], true);
}
});
Object.defineProperty(this, "canback", {
get: function() { return current > 0; }
});
Object.defineProperty(this, "cannext", {
get: function() { return current < stack.length - 1; }
});
}
global.PageManager = PageManager;
})(this);