feat: 实现MeNav浏览器扩展支持接口
为支持浏览器扩展的HTML替换方案,对原仓库进行以下修改: - 在generator.js中添加配置数据序列化和嵌入功能 - 在default.hbs中添加配置数据存储元素 - 在site-card.hbs和category.hbs中添加数据属性标识符 - 在script.js中添加全局MeNav对象和API方法
This commit is contained in:
@@ -402,6 +402,13 @@ function prepareRenderData(config) {
|
||||
// 移除警告日志,数据处理逻辑保留
|
||||
}
|
||||
|
||||
// 添加序列化的配置数据,用于浏览器扩展
|
||||
renderData.configJSON = JSON.stringify({
|
||||
version: process.env.npm_package_version || '1.0.0',
|
||||
timestamp: new Date().toISOString(),
|
||||
data: config
|
||||
});
|
||||
|
||||
// 添加导航项的活动状态标记和子菜单
|
||||
if (Array.isArray(renderData.navigation)) {
|
||||
renderData.navigation = renderData.navigation.map((item, index) => {
|
||||
@@ -824,7 +831,10 @@ function generateHTML(config) {
|
||||
currentYear,
|
||||
socialLinks,
|
||||
navigation: generateNavigation(config.navigation, config), // 兼容旧版
|
||||
social: Array.isArray(config.social) ? config.social : [] // 兼容旧版
|
||||
social: Array.isArray(config.social) ? config.social : [], // 兼容旧版
|
||||
|
||||
// 确保配置数据可用于浏览器扩展
|
||||
configJSON: config.configJSON // 从prepareRenderData函数中获取的配置数据
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
193
src/script.js
193
src/script.js
@@ -1,3 +1,185 @@
|
||||
// 全局MeNav对象 - 用于浏览器扩展
|
||||
window.MeNav = {
|
||||
version: "1.0.0",
|
||||
|
||||
// 获取配置数据
|
||||
getConfig: function() {
|
||||
const configData = document.getElementById('menav-config-data');
|
||||
return configData ? JSON.parse(configData.textContent) : null;
|
||||
},
|
||||
|
||||
// 更新DOM元素
|
||||
updateElement: function(id, newData) {
|
||||
const element = document.querySelector(`[data-menav-id="${id}"]`);
|
||||
if (!element) return false;
|
||||
|
||||
// 根据元素类型更新内容
|
||||
const type = element.getAttribute('data-menav-type');
|
||||
|
||||
if (type === 'site') {
|
||||
// 更新站点卡片
|
||||
if (newData.url) element.href = newData.url;
|
||||
if (newData.name) element.querySelector('h3').textContent = newData.name;
|
||||
if (newData.description) element.querySelector('p').textContent = newData.description;
|
||||
if (newData.icon) {
|
||||
const iconElement = element.querySelector('i');
|
||||
if (iconElement) {
|
||||
iconElement.className = newData.icon;
|
||||
}
|
||||
}
|
||||
if (newData.title) element.title = newData.title;
|
||||
|
||||
// 触发元素更新事件
|
||||
this.events.emit('elementUpdated', {
|
||||
id: id,
|
||||
type: 'site',
|
||||
data: newData
|
||||
});
|
||||
|
||||
return true;
|
||||
} else if (type === 'category') {
|
||||
// 更新分类
|
||||
if (newData.name) {
|
||||
const titleElement = element.querySelector('h2');
|
||||
if (titleElement) {
|
||||
// 保留图标
|
||||
const iconElement = titleElement.querySelector('i');
|
||||
const iconClass = iconElement ? iconElement.className : '';
|
||||
titleElement.innerHTML = `<i class="${newData.icon || iconClass}"></i> ${newData.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 触发元素更新事件
|
||||
this.events.emit('elementUpdated', {
|
||||
id: id,
|
||||
type: 'category',
|
||||
data: newData
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// 添加新元素
|
||||
addElement: function(type, parentId, data) {
|
||||
const parent = document.querySelector(`[data-menav-id="${parentId}"]`);
|
||||
if (!parent) return null;
|
||||
|
||||
if (type === 'site' && parent.getAttribute('data-menav-type') === 'category') {
|
||||
// 添加站点卡片到分类
|
||||
const sitesGrid = parent.querySelector('.sites-grid');
|
||||
if (!sitesGrid) return null;
|
||||
|
||||
// 创建新的站点卡片
|
||||
const newSite = document.createElement('a');
|
||||
newSite.className = 'site-card';
|
||||
newSite.href = data.url || '#';
|
||||
newSite.title = data.name + (data.description ? ' - ' + data.description : '');
|
||||
const elementId = `site-new-${Date.now()}`;
|
||||
newSite.setAttribute('data-menav-id', elementId);
|
||||
newSite.setAttribute('data-menav-type', 'site');
|
||||
newSite.setAttribute('data-menav-category', parent.id);
|
||||
|
||||
// 添加内容
|
||||
newSite.innerHTML = `
|
||||
<i class="${data.icon || 'fas fa-link'}"></i>
|
||||
<h3>${data.name || '未命名站点'}</h3>
|
||||
<p>${data.description || ''}</p>
|
||||
`;
|
||||
|
||||
// 添加到DOM
|
||||
sitesGrid.appendChild(newSite);
|
||||
|
||||
// 移除"暂无网站"提示(如果存在)
|
||||
const emptyMessage = sitesGrid.querySelector('.empty-sites');
|
||||
if (emptyMessage) {
|
||||
emptyMessage.remove();
|
||||
}
|
||||
|
||||
// 触发元素添加事件
|
||||
this.events.emit('elementAdded', {
|
||||
id: elementId,
|
||||
type: 'site',
|
||||
parentId: parentId,
|
||||
data: data
|
||||
});
|
||||
|
||||
return elementId;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
// 删除元素
|
||||
removeElement: function(id) {
|
||||
const element = document.querySelector(`[data-menav-id="${id}"]`);
|
||||
if (!element) return false;
|
||||
|
||||
// 获取元素类型和分类(如果是站点卡片)
|
||||
const type = element.getAttribute('data-menav-type');
|
||||
const category = element.getAttribute('data-menav-category');
|
||||
|
||||
// 删除元素
|
||||
element.remove();
|
||||
|
||||
// 触发元素删除事件
|
||||
this.events.emit('elementRemoved', {
|
||||
id: id,
|
||||
type: type,
|
||||
category: category
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// 获取所有元素
|
||||
getAllElements: function(type) {
|
||||
return Array.from(document.querySelectorAll(`[data-menav-type="${type}"]`)).map(el => {
|
||||
return {
|
||||
id: el.getAttribute('data-menav-id'),
|
||||
type: el.getAttribute('data-menav-type'),
|
||||
element: el
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
// 事件系统
|
||||
events: {
|
||||
listeners: {},
|
||||
|
||||
// 添加事件监听器
|
||||
on: function(event, callback) {
|
||||
if (!this.listeners[event]) {
|
||||
this.listeners[event] = [];
|
||||
}
|
||||
this.listeners[event].push(callback);
|
||||
return this;
|
||||
},
|
||||
|
||||
// 触发事件
|
||||
emit: function(event, data) {
|
||||
if (this.listeners[event]) {
|
||||
this.listeners[event].forEach(callback => callback(data));
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// 移除事件监听器
|
||||
off: function(event, callback) {
|
||||
if (this.listeners[event]) {
|
||||
if (callback) {
|
||||
this.listeners[event] = this.listeners[event].filter(cb => cb !== callback);
|
||||
} else {
|
||||
delete this.listeners[event];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 先声明所有状态变量
|
||||
let isSearchActive = false;
|
||||
@@ -764,6 +946,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// 初始化搜索引擎选择
|
||||
initSearchEngine();
|
||||
|
||||
// 初始化MeNav对象版本信息
|
||||
try {
|
||||
const config = window.MeNav.getConfig();
|
||||
if (config && config.version) {
|
||||
window.MeNav.version = config.version;
|
||||
console.log('MeNav API initialized with version:', config.version);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error initializing MeNav API:', error);
|
||||
}
|
||||
|
||||
// 立即执行初始化,不再使用requestAnimationFrame延迟
|
||||
// 显示首页
|
||||
showPage('home');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<section class="category" id="{{name}}">
|
||||
<section class="category" id="{{name}}" data-menav-id="category-{{@index}}" data-menav-type="category">
|
||||
<h2><i class="{{icon}}"></i> {{name}}</h2>
|
||||
<div class="sites-grid">
|
||||
<div class="sites-grid" data-menav-category="{{name}}">
|
||||
{{#if sites.length}}
|
||||
{{#each sites}}
|
||||
{{> site-card}}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{{#if url}}
|
||||
<a href="{{url}}" class="site-card{{#if style}} site-card-{{style}}{{/if}}" title="{{name}} - {{#if description}}{{description}}{{else}}{{url}}{{/if}}" {{#if external}}target="_blank" rel="noopener"{{/if}}>
|
||||
<a href="{{url}}" class="site-card{{#if style}} site-card-{{style}}{{/if}}"
|
||||
title="{{name}} - {{#if description}}{{description}}{{else}}{{url}}{{/if}}"
|
||||
{{#if external}}target="_blank" rel="noopener"{{/if}}
|
||||
data-menav-id="site-{{@index}}"
|
||||
data-menav-type="site"
|
||||
data-menav-category="{{../name}}">
|
||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}}"></i>
|
||||
<h3>{{#if name}}{{name}}{{else}}未命名站点{{/if}}</h3>
|
||||
<p>{{#if description}}{{description}}{{else}}{{url}}{{/if}}</p>
|
||||
|
||||
@@ -123,6 +123,10 @@
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 配置数据 - 用于浏览器扩展 -->
|
||||
<script id="menav-config-data" type="application/json" style="display: none;">
|
||||
{{{configJSON}}}
|
||||
</script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user