177 lines
5.6 KiB
JavaScript
177 lines
5.6 KiB
JavaScript
(function (root, factory) {
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = factory(root);
|
|
return;
|
|
}
|
|
|
|
root.AppPromotionPages = factory(root);
|
|
if (root.document) {
|
|
root.document.addEventListener('DOMContentLoaded', function () {
|
|
root.AppPromotionPages.init();
|
|
});
|
|
}
|
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
|
'use strict';
|
|
|
|
var MediaDisplay = root.MediaDisplay || (typeof require === 'function' ? require('./media-display.js') : null);
|
|
|
|
function escapeHtml(value) {
|
|
return String(value === undefined || value === null ? '' : value)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function normalizeId(value) {
|
|
var normalized;
|
|
|
|
if (typeof value === 'number' && !Number.isSafeInteger(value)) return '';
|
|
if (value === undefined || value === null) return '';
|
|
normalized = String(value).trim();
|
|
return /^[1-9][0-9]*$/.test(normalized) ? normalized : '';
|
|
}
|
|
|
|
function text(value) {
|
|
return value === undefined || value === null ? '' : String(value).trim();
|
|
}
|
|
|
|
function normalizeTargetUrl(value) {
|
|
var normalized = text(value);
|
|
var parsed;
|
|
|
|
if (!normalized) return '';
|
|
try {
|
|
parsed = new URL(normalized);
|
|
} catch (error) {
|
|
return '';
|
|
}
|
|
return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.href : '';
|
|
}
|
|
|
|
function normalizePromotion(item) {
|
|
var source = item || {};
|
|
var promotionId = normalizeId(source.promotionId);
|
|
var promotionTitle = text(source.promotionTitle);
|
|
var coverFile = MediaDisplay && MediaDisplay.normalizeFileAccess(source.coverFile);
|
|
var promotion;
|
|
|
|
if (!promotionId || !promotionTitle || String(source.status) !== '0') return null;
|
|
if (source.coverFile !== undefined && source.coverFile !== null && !coverFile) return null;
|
|
promotion = {
|
|
promotionId: promotionId,
|
|
promotionTitle: promotionTitle,
|
|
promotionDesc: text(source.promotionDesc),
|
|
targetUrl: normalizeTargetUrl(source.targetUrl)
|
|
};
|
|
if (coverFile) promotion.coverFile = coverFile;
|
|
return promotion;
|
|
}
|
|
|
|
function normalizePromotionList(data) {
|
|
var items;
|
|
|
|
if (!Array.isArray(data)) return [];
|
|
items = data.map(normalizePromotion);
|
|
return items.some(function (item) { return !item; }) ? [] : items;
|
|
}
|
|
|
|
function renderPromotionCard(item) {
|
|
var content = (MediaDisplay ? MediaDisplay.renderImage(item.coverFile, {
|
|
alt: item.promotionTitle + '封面', className: 'promotion-cover-media'
|
|
}) : '') + '<div><h3>' + escapeHtml(item.promotionTitle) + '</h3>' +
|
|
'<p>' + escapeHtml(item.promotionDesc) + '</p></div>';
|
|
|
|
if (!item.targetUrl) {
|
|
return '<article class="promotion-card" data-promotion-id="' +
|
|
escapeHtml(item.promotionId) + '">' + content + '</article>';
|
|
}
|
|
return '<a class="promotion-card" data-promotion-id="' + escapeHtml(item.promotionId) +
|
|
'" href="' + escapeHtml(item.targetUrl) +
|
|
'" target="_blank" rel="noopener noreferrer">' + content + '</a>';
|
|
}
|
|
|
|
function renderPromotionList(data) {
|
|
var items = normalizePromotionList(data);
|
|
|
|
return items.map(renderPromotionCard).join('');
|
|
}
|
|
|
|
function setPromotionState(container, type, message) {
|
|
if (!container) return;
|
|
if (root.ProfileUI && root.ProfileUI.setApiState) {
|
|
root.ProfileUI.setApiState(container, type, message);
|
|
return;
|
|
}
|
|
container.textContent = message;
|
|
}
|
|
|
|
async function loadPromotions(api) {
|
|
var data;
|
|
var items;
|
|
|
|
if (!api || typeof api.promotions !== 'function') throw new Error('应用推广接口不可用');
|
|
data = await api.promotions({ platform: 'pc' });
|
|
items = normalizePromotionList(data);
|
|
if (!Array.isArray(data) || items.length !== data.length) {
|
|
throw new Error('推广列表响应无效');
|
|
}
|
|
return items;
|
|
}
|
|
|
|
function isUnauthorized(error) {
|
|
return Number(error && (error.status || error.code)) === 401;
|
|
}
|
|
|
|
function isForbidden(error) {
|
|
return Number(error && (error.status || error.code)) === 403;
|
|
}
|
|
|
|
async function init() {
|
|
var page = root.document && root.document.querySelector('[data-promotion-page]');
|
|
var list = root.document && root.document.querySelector('[data-promotion-list]');
|
|
var api = root.GenealogyApi && root.GenealogyApi.defaultClient;
|
|
|
|
if (!page || !list) return;
|
|
if (!api || !api.getToken || !api.getToken()) {
|
|
setPromotionState(list, 'forbidden', '登录后可查看应用推广。');
|
|
return;
|
|
}
|
|
|
|
setPromotionState(list, 'loading', '正在读取应用推广…');
|
|
try {
|
|
var items = await loadPromotions(api);
|
|
|
|
if (!items.length) {
|
|
setPromotionState(list, 'empty', '当前暂无应用推广。');
|
|
return;
|
|
}
|
|
list.innerHTML = items.map(renderPromotionCard).join('');
|
|
} catch (error) {
|
|
if (isUnauthorized(error)) {
|
|
if (api.clearToken) api.clearToken();
|
|
setPromotionState(list, 'forbidden', '登录后可查看应用推广。');
|
|
return;
|
|
}
|
|
setPromotionState(
|
|
list,
|
|
isForbidden(error) ? 'forbidden' : 'error',
|
|
isForbidden(error) ? '当前账号无权查看应用推广。' : '应用推广读取失败,请稍后重试。'
|
|
);
|
|
}
|
|
}
|
|
|
|
return {
|
|
normalizePromotion: normalizePromotion,
|
|
normalizePromotionList: normalizePromotionList,
|
|
normalizeTargetUrl: normalizeTargetUrl,
|
|
renderPromotionList: renderPromotionList,
|
|
setPromotionState: setPromotionState,
|
|
loadPromotions: loadPromotions,
|
|
isUnauthorized: isUnauthorized,
|
|
isForbidden: isForbidden,
|
|
init: init
|
|
};
|
|
});
|