682 lines
26 KiB
JavaScript
682 lines
26 KiB
JavaScript
// 角色管理页面脚本 - 包含角色权重系统
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// DOM元素
|
|
const charactersContainer = document.getElementById('characters-container');
|
|
const characterSearch = document.getElementById('character-search');
|
|
const characterTypeFilter = document.getElementById('character-type-filter');
|
|
const newCharacterBtn = document.getElementById('new-character-btn');
|
|
|
|
// 所有模态框
|
|
const characterModal = document.getElementById('character-modal');
|
|
const eventModal = document.getElementById('event-modal');
|
|
const characterDetailModal = document.getElementById('character-detail-modal');
|
|
const confirmModal = document.getElementById('confirm-modal');
|
|
|
|
// 角色表单元素
|
|
const characterForm = document.getElementById('character-form');
|
|
const characterIdInput = document.getElementById('character-id');
|
|
const characterNameInput = document.getElementById('character-name');
|
|
const characterTitleInput = document.getElementById('character-title');
|
|
const characterAvatarInput = document.getElementById('character-avatar');
|
|
const characterAvatarPathInput = document.getElementById('character-avatar-path');
|
|
const characterTypeInput = document.getElementById('character-type');
|
|
const characterWeightInput = document.getElementById('character-weight'); // 权重输入字段
|
|
|
|
// 事件表单元素
|
|
const eventForm = document.getElementById('event-form');
|
|
const eventCharacterIdInput = document.getElementById('event-character-id');
|
|
const eventIdInput = document.getElementById('event-id');
|
|
const eventTextInput = document.getElementById('event-text');
|
|
const optionATextInput = document.getElementById('option-a-text');
|
|
const optionBTextInput = document.getElementById('option-b-text');
|
|
|
|
// 详情页元素
|
|
const detailTitle = document.getElementById('detail-title');
|
|
const detailAvatar = document.getElementById('detail-avatar');
|
|
const detailName = document.getElementById('detail-name');
|
|
const detailTitleText = document.getElementById('detail-title-text');
|
|
const detailType = document.getElementById('detail-type');
|
|
const editCharacterBtn = document.getElementById('edit-character-btn');
|
|
const deleteCharacterBtn = document.getElementById('delete-character-btn');
|
|
const addEventBtn = document.getElementById('add-event-btn');
|
|
const eventsContainer = document.getElementById('events-container');
|
|
|
|
// 确认对话框元素
|
|
const confirmTitle = document.getElementById('confirm-title');
|
|
const confirmMessage = document.getElementById('confirm-message');
|
|
const confirmOkBtn = document.getElementById('confirm-ok');
|
|
const confirmCancelBtn = document.getElementById('confirm-cancel');
|
|
|
|
// 状态变量
|
|
let characters = [];
|
|
let currentCharacter = null;
|
|
let filteredCharacters = [];
|
|
let confirmCallback = null;
|
|
|
|
// 初始化
|
|
loadCharacters();
|
|
setupEventListeners();
|
|
|
|
// 设置事件监听器
|
|
function setupEventListeners() {
|
|
// 搜索和过滤
|
|
characterSearch.addEventListener('input', filterCharacters);
|
|
characterTypeFilter.addEventListener('change', filterCharacters);
|
|
|
|
// 新建角色按钮
|
|
newCharacterBtn.addEventListener('click', () => {
|
|
openNewCharacterModal();
|
|
});
|
|
|
|
// 表单提交
|
|
characterForm.addEventListener('submit', saveCharacter);
|
|
eventForm.addEventListener('submit', saveEvent);
|
|
|
|
// 编辑角色按钮
|
|
editCharacterBtn.addEventListener('click', () => {
|
|
if (currentCharacter) {
|
|
// 先关闭详情模态框
|
|
closeModal(characterDetailModal);
|
|
// 再打开编辑模态框
|
|
setTimeout(() => {
|
|
openEditCharacterModal(currentCharacter);
|
|
}, 300);
|
|
}
|
|
});
|
|
|
|
// 删除角色按钮
|
|
deleteCharacterBtn.addEventListener('click', () => {
|
|
if (currentCharacter) {
|
|
openConfirmModal(
|
|
'删除角色',
|
|
`确定要删除角色 "${currentCharacter.name}" 吗?此操作不可恢复,且会删除该角色的所有事件。`,
|
|
() => deleteCharacter(currentCharacter.id)
|
|
);
|
|
}
|
|
});
|
|
|
|
// 添加事件按钮
|
|
addEventBtn.addEventListener('click', () => {
|
|
if (currentCharacter) {
|
|
// 先关闭详情模态框
|
|
closeModal(characterDetailModal);
|
|
// 再打开事件模态框
|
|
setTimeout(() => {
|
|
openNewEventModal(currentCharacter.id);
|
|
}, 300);
|
|
}
|
|
});
|
|
|
|
// 确认对话框按钮
|
|
confirmOkBtn.addEventListener('click', () => {
|
|
if (confirmCallback) {
|
|
confirmCallback();
|
|
}
|
|
closeModal(confirmModal);
|
|
});
|
|
|
|
confirmCancelBtn.addEventListener('click', () => {
|
|
closeModal(confirmModal);
|
|
});
|
|
|
|
// 关闭按钮
|
|
document.querySelectorAll('.close-modal, .close-btn').forEach(btn => {
|
|
btn.addEventListener('click', (e) => {
|
|
const modal = e.target.closest('.modal');
|
|
if (modal) {
|
|
closeModal(modal);
|
|
}
|
|
});
|
|
});
|
|
|
|
// 点击模态框背景关闭
|
|
document.querySelectorAll('.modal').forEach(modal => {
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) {
|
|
closeModal(modal);
|
|
}
|
|
});
|
|
});
|
|
|
|
// 防止冒泡
|
|
document.querySelectorAll('.modal-content').forEach(content => {
|
|
content.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
});
|
|
});
|
|
}
|
|
|
|
// 加载所有角色
|
|
function loadCharacters() {
|
|
charactersContainer.innerHTML = '<div class="loading-indicator">加载中...</div>';
|
|
|
|
fetch('/api/admin/characters')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
characters = data.characters || [];
|
|
filteredCharacters = [...characters];
|
|
renderCharacters();
|
|
})
|
|
.catch(error => {
|
|
console.error('加载角色失败:', error);
|
|
charactersContainer.innerHTML = '<div class="error-message">加载失败,请刷新页面重试。</div>';
|
|
});
|
|
}
|
|
|
|
// 过滤角色
|
|
function filterCharacters() {
|
|
const searchTerm = characterSearch.value.toLowerCase();
|
|
const typeFilter = characterTypeFilter.value;
|
|
|
|
filteredCharacters = characters.filter(character => {
|
|
const nameMatch = (character.name || '').toLowerCase().includes(searchTerm) ||
|
|
((character.title || '').toLowerCase().includes(searchTerm));
|
|
const typeMatch = typeFilter === 'all' || character.type === typeFilter;
|
|
|
|
return nameMatch && typeMatch;
|
|
});
|
|
|
|
renderCharacters();
|
|
}
|
|
|
|
// 渲染角色列表
|
|
function renderCharacters() {
|
|
if (filteredCharacters.length === 0) {
|
|
charactersContainer.innerHTML = '<div class="empty-message">没有找到符合条件的角色</div>';
|
|
return;
|
|
}
|
|
|
|
charactersContainer.innerHTML = '';
|
|
|
|
filteredCharacters.forEach(character => {
|
|
const card = document.createElement('div');
|
|
card.className = 'character-card';
|
|
card.dataset.id = character.id;
|
|
|
|
// 设置角色类型标签
|
|
let typeLabel = '未知';
|
|
let typeClass = '';
|
|
|
|
switch(character.type) {
|
|
case 'resident':
|
|
typeLabel = '常驻角色';
|
|
typeClass = 'type-resident';
|
|
break;
|
|
case 'special':
|
|
typeLabel = '特殊角色';
|
|
typeClass = 'type-special';
|
|
break;
|
|
case 'status':
|
|
typeLabel = '状态效果';
|
|
typeClass = 'type-status';
|
|
break;
|
|
}
|
|
|
|
// 添加权重标签
|
|
const weightLabel = `<div class="character-weight">权重: ${character.weight || 100}</div>`;
|
|
|
|
card.innerHTML = `
|
|
<div class="card-avatar">${character.avatar || '👤'}</div>
|
|
<h3>${character.name || '无名角色'}</h3>
|
|
<p>${character.title || '无头衔'}</p>
|
|
<div class="character-type ${typeClass}">${typeLabel}</div>
|
|
${weightLabel}
|
|
`;
|
|
|
|
card.addEventListener('click', () => openCharacterDetail(character));
|
|
|
|
charactersContainer.appendChild(card);
|
|
});
|
|
}
|
|
|
|
// 打开新角色模态框
|
|
function openNewCharacterModal() {
|
|
// 重置表单
|
|
characterForm.reset();
|
|
characterIdInput.value = generateUUID();
|
|
|
|
// 设置默认权重值
|
|
characterWeightInput.value = 5;
|
|
|
|
// 设置标题
|
|
document.getElementById('modal-title').textContent = '新建人物';
|
|
|
|
// 打开模态框
|
|
openModal(characterModal);
|
|
}
|
|
|
|
// 打开编辑角色模态框
|
|
function openEditCharacterModal(character) {
|
|
// 设置标题
|
|
document.getElementById('modal-title').textContent = '编辑人物';
|
|
|
|
// 填充表单
|
|
characterIdInput.value = character.id;
|
|
characterNameInput.value = character.name || '';
|
|
characterTitleInput.value = character.title || '';
|
|
characterAvatarInput.value = character.avatar || '';
|
|
characterAvatarPathInput.value = character.avatarPath || '';
|
|
characterTypeInput.value = character.type || 'resident';
|
|
characterWeightInput.value = character.weight || 100; // 设置权重值
|
|
|
|
// 打开模态框
|
|
openModal(characterModal);
|
|
}
|
|
|
|
// 打开新事件模态框
|
|
function openNewEventModal(characterId) {
|
|
// 重置表单
|
|
eventForm.reset();
|
|
eventCharacterIdInput.value = characterId;
|
|
eventIdInput.value = generateUUID();
|
|
|
|
// 设置所有数值为0
|
|
document.querySelectorAll('.effect-item input[type="number"]').forEach(input => {
|
|
input.value = 0;
|
|
});
|
|
|
|
// 设置标题
|
|
document.getElementById('event-modal-title').textContent = '新建事件';
|
|
|
|
// 打开模态框
|
|
openModal(eventModal);
|
|
}
|
|
|
|
// 打开编辑事件模态框
|
|
function openEditEventModal(characterId, event) {
|
|
// 设置标题
|
|
document.getElementById('event-modal-title').textContent = '编辑事件';
|
|
|
|
// 填充表单基本信息
|
|
eventCharacterIdInput.value = characterId;
|
|
eventIdInput.value = event.id;
|
|
eventTextInput.value = event.text || '';
|
|
|
|
// 填充选项A
|
|
if (event.optionA) {
|
|
optionATextInput.value = event.optionA.text || '';
|
|
|
|
if (event.optionA.effects) {
|
|
document.getElementById('option-a-loyalty').value = event.optionA.effects.loyalty || 0;
|
|
document.getElementById('option-a-chaos').value = event.optionA.effects.chaos || 0;
|
|
document.getElementById('option-a-population').value = event.optionA.effects.population || 0;
|
|
document.getElementById('option-a-military').value = event.optionA.effects.military || 0;
|
|
document.getElementById('option-a-resources').value = event.optionA.effects.resources || 0;
|
|
}
|
|
|
|
document.getElementById('option-a-add-status').value = event.optionA.add_status || '';
|
|
document.getElementById('option-a-remove-status').value = event.optionA.remove_status || '';
|
|
}
|
|
|
|
// 填充选项B
|
|
if (event.optionB) {
|
|
optionBTextInput.value = event.optionB.text || '';
|
|
|
|
if (event.optionB.effects) {
|
|
document.getElementById('option-b-loyalty').value = event.optionB.effects.loyalty || 0;
|
|
document.getElementById('option-b-chaos').value = event.optionB.effects.chaos || 0;
|
|
document.getElementById('option-b-population').value = event.optionB.effects.population || 0;
|
|
document.getElementById('option-b-military').value = event.optionB.effects.military || 0;
|
|
document.getElementById('option-b-resources').value = event.optionB.effects.resources || 0;
|
|
}
|
|
|
|
document.getElementById('option-b-add-status').value = event.optionB.add_status || '';
|
|
document.getElementById('option-b-remove-status').value = event.optionB.remove_status || '';
|
|
}
|
|
|
|
// 打开模态框
|
|
openModal(eventModal);
|
|
}
|
|
|
|
// 打开角色详情
|
|
function openCharacterDetail(character) {
|
|
// 保存当前角色
|
|
currentCharacter = character;
|
|
|
|
// 填充详情
|
|
detailTitle.textContent = '角色详情: ' + character.name;
|
|
detailAvatar.textContent = character.avatar || '👤';
|
|
detailName.textContent = character.name || '无名角色';
|
|
detailTitleText.textContent = character.title || '无头衔';
|
|
|
|
// 显示权重
|
|
const weightElement = document.getElementById('detail-weight').querySelector('span');
|
|
if (weightElement) {
|
|
weightElement.textContent = character.weight || 100;
|
|
}
|
|
|
|
// 设置角色类型
|
|
let typeLabel = '未知类型';
|
|
switch(character.type) {
|
|
case 'resident':
|
|
typeLabel = '常驻角色';
|
|
break;
|
|
case 'special':
|
|
typeLabel = '特殊角色';
|
|
break;
|
|
case 'status':
|
|
typeLabel = '状态效果';
|
|
break;
|
|
}
|
|
detailType.textContent = typeLabel;
|
|
|
|
// 渲染事件列表
|
|
renderEvents(character);
|
|
|
|
// 打开模态框
|
|
openModal(characterDetailModal);
|
|
}
|
|
|
|
// 打开确认对话框
|
|
function openConfirmModal(title, message, callback) {
|
|
// 设置内容
|
|
confirmTitle.textContent = title;
|
|
confirmMessage.textContent = message;
|
|
confirmCallback = callback;
|
|
|
|
// 打开模态框
|
|
openModal(confirmModal);
|
|
}
|
|
|
|
// 打开模态框通用函数
|
|
function openModal(modal) {
|
|
// 禁止背景滚动
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// 确保模态框在DOM中
|
|
if (!document.body.contains(modal)) {
|
|
document.body.appendChild(modal);
|
|
}
|
|
|
|
// 显示模态框
|
|
modal.style.display = 'block';
|
|
}
|
|
|
|
// 关闭模态框通用函数
|
|
function closeModal(modal) {
|
|
modal.style.display = 'none';
|
|
|
|
// 恢复背景滚动
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
// 渲染角色的事件列表
|
|
function renderEvents(character) {
|
|
if (!character.events || character.events.length === 0) {
|
|
eventsContainer.innerHTML = '<div class="empty-events">该角色暂无事件</div>';
|
|
return;
|
|
}
|
|
|
|
eventsContainer.innerHTML = '';
|
|
|
|
character.events.forEach(event => {
|
|
const eventItem = document.createElement('div');
|
|
eventItem.className = 'event-item';
|
|
|
|
eventItem.innerHTML = `
|
|
<div class="event-text">
|
|
<h4>事件ID: ${event.id}</h4>
|
|
<p>${event.text || '无描述'}</p>
|
|
</div>
|
|
<div class="event-actions">
|
|
<button class="edit-event-btn" title="编辑事件">✏️</button>
|
|
<button class="delete-event-btn" title="删除事件">🗑️</button>
|
|
</div>
|
|
`;
|
|
|
|
// 编辑事件按钮
|
|
eventItem.querySelector('.edit-event-btn').addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
closeModal(characterDetailModal);
|
|
setTimeout(() => {
|
|
openEditEventModal(character.id, event);
|
|
}, 300);
|
|
});
|
|
|
|
// 删除事件按钮
|
|
eventItem.querySelector('.delete-event-btn').addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
openConfirmModal(
|
|
'删除事件',
|
|
`确定要删除此事件吗?此操作不可恢复。`,
|
|
() => deleteEvent(character.id, event.id)
|
|
);
|
|
});
|
|
|
|
eventsContainer.appendChild(eventItem);
|
|
});
|
|
}
|
|
|
|
// 保存角色
|
|
function saveCharacter(e) {
|
|
e.preventDefault();
|
|
|
|
// 获取表单数据
|
|
const characterData = {
|
|
id: characterIdInput.value,
|
|
name: characterNameInput.value,
|
|
title: characterTitleInput.value,
|
|
avatar: characterAvatarInput.value,
|
|
avatarPath: characterAvatarPathInput.value,
|
|
type: characterTypeInput.value,
|
|
weight: parseInt(characterWeightInput.value) || 100, // 添加权重字段
|
|
events: []
|
|
};
|
|
|
|
// 如果是编辑现有角色,保留原有事件
|
|
const existingCharacter = characters.find(c => c.id === characterData.id);
|
|
if (existingCharacter && existingCharacter.events) {
|
|
characterData.events = existingCharacter.events;
|
|
}
|
|
|
|
// 发送请求
|
|
fetch('/api/admin/character', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(characterData)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// 更新列表
|
|
loadCharacters();
|
|
|
|
// 关闭模态框
|
|
closeModal(characterModal);
|
|
|
|
// 如果是编辑现有角色,更新详情页
|
|
if (currentCharacter && currentCharacter.id === characterData.id) {
|
|
currentCharacter = characterData;
|
|
setTimeout(() => {
|
|
openCharacterDetail(currentCharacter);
|
|
}, 300);
|
|
}
|
|
} else {
|
|
alert('保存失败: ' + (data.error || '未知错误'));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('保存角色失败:', error);
|
|
alert('保存失败,请稍后重试');
|
|
});
|
|
}
|
|
|
|
// 保存事件
|
|
function saveEvent(e) {
|
|
e.preventDefault();
|
|
|
|
// 收集表单数据
|
|
const characterId = eventCharacterIdInput.value;
|
|
const eventData = {
|
|
id: eventIdInput.value,
|
|
text: eventTextInput.value,
|
|
optionA: {
|
|
text: optionATextInput.value,
|
|
effects: {
|
|
loyalty: parseInt(document.getElementById('option-a-loyalty').value) || 0,
|
|
chaos: parseInt(document.getElementById('option-a-chaos').value) || 0,
|
|
population: parseInt(document.getElementById('option-a-population').value) || 0,
|
|
military: parseInt(document.getElementById('option-a-military').value) || 0,
|
|
resources: parseInt(document.getElementById('option-a-resources').value) || 0
|
|
}
|
|
},
|
|
optionB: {
|
|
text: optionBTextInput.value,
|
|
effects: {
|
|
loyalty: parseInt(document.getElementById('option-b-loyalty').value) || 0,
|
|
chaos: parseInt(document.getElementById('option-b-chaos').value) || 0,
|
|
population: parseInt(document.getElementById('option-b-population').value) || 0,
|
|
military: parseInt(document.getElementById('option-b-military').value) || 0,
|
|
resources: parseInt(document.getElementById('option-b-resources').value) || 0
|
|
}
|
|
}
|
|
};
|
|
|
|
// 添加状态效果(如果有)
|
|
const optionAAddStatus = document.getElementById('option-a-add-status').value;
|
|
if (optionAAddStatus) {
|
|
eventData.optionA.add_status = optionAAddStatus;
|
|
}
|
|
|
|
const optionARemoveStatus = document.getElementById('option-a-remove-status').value;
|
|
if (optionARemoveStatus) {
|
|
eventData.optionA.remove_status = optionARemoveStatus;
|
|
}
|
|
|
|
const optionBAddStatus = document.getElementById('option-b-add-status').value;
|
|
if (optionBAddStatus) {
|
|
eventData.optionB.add_status = optionBAddStatus;
|
|
}
|
|
|
|
const optionBRemoveStatus = document.getElementById('option-b-remove-status').value;
|
|
if (optionBRemoveStatus) {
|
|
eventData.optionB.remove_status = optionBRemoveStatus;
|
|
}
|
|
|
|
// 判断是新增还是更新
|
|
const isNewEvent = !characters.some(c =>
|
|
c.id === characterId &&
|
|
c.events &&
|
|
c.events.some(e => e.id === eventData.id)
|
|
);
|
|
|
|
const url = isNewEvent
|
|
? `/api/admin/character/${characterId}/event`
|
|
: `/api/admin/character/${characterId}/event/${eventData.id}`;
|
|
|
|
const method = isNewEvent ? 'POST' : 'PUT';
|
|
|
|
// 发送请求
|
|
fetch(url, {
|
|
method: method,
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(eventData)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// 重新加载数据
|
|
loadCharacters();
|
|
|
|
// 关闭模态框
|
|
closeModal(eventModal);
|
|
|
|
// 如果当前有打开的角色详情,更新详情
|
|
if (currentCharacter && currentCharacter.id === characterId) {
|
|
fetch(`/api/admin/character/${characterId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.character) {
|
|
currentCharacter = data.character;
|
|
setTimeout(() => {
|
|
openCharacterDetail(currentCharacter);
|
|
}, 300);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
alert('保存失败: ' + (data.error || '未知错误'));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('保存事件失败:', error);
|
|
alert('保存失败,请稍后重试');
|
|
});
|
|
}
|
|
|
|
// 删除角色
|
|
function deleteCharacter(characterId) {
|
|
fetch(`/api/admin/character/${characterId}`, {
|
|
method: 'DELETE'
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// 更新角色列表
|
|
loadCharacters();
|
|
|
|
// 关闭所有模态框
|
|
closeModal(characterDetailModal);
|
|
closeModal(confirmModal);
|
|
|
|
// 清空当前角色
|
|
currentCharacter = null;
|
|
} else {
|
|
alert('删除失败: ' + (data.error || '未知错误'));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('删除角色失败:', error);
|
|
alert('删除失败,请稍后重试');
|
|
});
|
|
}
|
|
|
|
// 删除事件
|
|
function deleteEvent(characterId, eventId) {
|
|
fetch(`/api/admin/character/${characterId}/event/${eventId}`, {
|
|
method: 'DELETE'
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// 如果当前有打开的角色详情,更新详情
|
|
if (currentCharacter && currentCharacter.id === characterId) {
|
|
fetch(`/api/admin/character/${characterId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.character) {
|
|
// 更新当前角色数据
|
|
currentCharacter = data.character;
|
|
// 重新渲染事件列表
|
|
renderEvents(currentCharacter);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 更新角色列表
|
|
loadCharacters();
|
|
|
|
// 关闭确认框
|
|
closeModal(confirmModal);
|
|
} else {
|
|
alert('删除失败: ' + (data.error || '未知错误'));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('删除事件失败:', error);
|
|
alert('删除失败,请稍后重试');
|
|
});
|
|
}
|
|
|
|
// 生成UUID
|
|
function generateUUID() {
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
}); |