79 lines
3.9 KiB
JavaScript
79 lines
3.9 KiB
JavaScript
(function (root) {
|
||
'use strict';
|
||
const modes = ['default', 'newest', 'oldest', 'heat', 'views', 'title'];
|
||
const collator = new Intl.Collator('zh-Hans-CN', {numeric: true, sensitivity: 'base'});
|
||
|
||
function chineseNumber(text) {
|
||
const digits = {零:0,〇:0,一:1,二:2,两:2,兩:2,三:3,四:4,五:5,六:6,七:7,八:8,九:9};
|
||
const units = {十:10,百:100,千:1000,万:10000,萬:10000};
|
||
if ([...text].every(character => character in digits)) return Number([...text].map(character => digits[character]).join(''));
|
||
let total=0, section=0, digit=0;
|
||
for (const character of text) {
|
||
if (character in digits) digit=digits[character];
|
||
else if (units[character]===10000) {total+=(section+digit||1)*10000;section=0;digit=0;}
|
||
else {section+=(digit||1)*units[character];digit=0;}
|
||
}
|
||
return total+section+digit;
|
||
}
|
||
|
||
function titleKey(drama) {
|
||
|
||
|
||
return String(drama.title || drama.name || '').normalize('NFKC').trim()
|
||
.replace(/第\s*([零〇一二两兩三四五六七八九十百千万萬]+)\s*(?=[季部集章卷篇册冊辑輯])/g, (_, number) => '第'+chineseNumber(number));
|
||
}
|
||
|
||
function metricValue(value) {
|
||
if (typeof value === 'number') return Number.isFinite(value) && value >= 0 ? value : null;
|
||
const text = String(value ?? '').replace(/[,,\s]/g, '');
|
||
const match = /^(\d+(?:\.\d+)?)(亿|万|千|w|k|m|b)?\+?(?:次播放|次观看|人看过|热度|播放|观看|次)?\+?$/i.exec(text);
|
||
if (!match) return null;
|
||
const unit = (match[2] || '').toLowerCase();
|
||
const number = Number(match[1]) * ({亿: 1e8, 万: 1e4, 千: 1e3, w: 1e4, k: 1e3, m: 1e6, b: 1e9}[unit] || 1);
|
||
return Number.isFinite(number) ? number : null;
|
||
}
|
||
|
||
function dateValue(value) {
|
||
const match = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(value || '').trim());
|
||
if (!match) return null;
|
||
const year = Number(match[1]), month = Number(match[2]), day = Number(match[3]);
|
||
const date = new Date(Date.UTC(year, month - 1, day));
|
||
if (year < 2000 || year > 2100 || date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return null;
|
||
return date.getTime();
|
||
}
|
||
|
||
function sortValue(drama, mode) {
|
||
if (mode === 'newest' || mode === 'oldest') return dateValue(drama.onlineDate);
|
||
if (mode === 'heat') return metricValue(drama.heat);
|
||
if (mode === 'views') return metricValue(drama.views);
|
||
return null;
|
||
}
|
||
|
||
function sortDramas(list, mode) {
|
||
if (!modes.includes(mode) || mode === 'default') return list.slice();
|
||
|
||
return list.map((drama, index) => ({drama, index, title: titleKey(drama), value: sortValue(drama, mode)})).sort((left, right) => {
|
||
if (mode !== 'title') {
|
||
if (left.value === null && right.value === null) return left.index - right.index;
|
||
if (left.value === null && right.value !== null) return 1;
|
||
if (left.value !== null && right.value === null) return -1;
|
||
if (left.value !== right.value) return mode === 'oldest' ? left.value - right.value : right.value - left.value;
|
||
}
|
||
return collator.compare(left.title, right.title) || left.index - right.index;
|
||
}).map(item => item.drama);
|
||
}
|
||
|
||
function summary(list, mode) {
|
||
if (!['newest', 'oldest', 'heat', 'views'].includes(mode)) return '';
|
||
const label = {newest: '上线日期', oldest: '上线日期', heat: '热度', views: '播放量'}[mode];
|
||
const known = list.filter(drama => sortValue(drama, mode) !== null).length;
|
||
if (!list.length) return '';
|
||
if (!known) return '当前结果缺少' + label + ',无法按此排序';
|
||
return known < list.length ? label + '未知 ' + (list.length - known) + ' 部,已置后' : '';
|
||
}
|
||
|
||
const librarySort = {modes, metricValue, dateValue, sortDramas, summary, titleKey};
|
||
if (typeof module !== 'undefined' && module.exports) module.exports = librarySort;
|
||
if (root) root.JukuLibrarySort = librarySort;
|
||
})(typeof window !== 'undefined' ? window : null);
|