Commit 6cdbb500 authored by antoor's avatar antoor

Add plug-store module

增加插件模块
parent a302c474
...@@ -8,22 +8,36 @@ ...@@ -8,22 +8,36 @@
const electron = require('electron'), const electron = require('electron'),
app = electron.app, app = electron.app,
path = require('path'),
BrowserWindow = electron.BrowserWindow; BrowserWindow = electron.BrowserWindow;
app app
.once('window-all-closed', app.quit)
.once('ready', () => { .once('ready', () => {
/**
* 注册静态资源protocol
* - 可通过注册的协议访问资源文件,如ant-static://libs/jquery.jquery.js
*/
[
['static', '/static/', 13],
['views', '/views/', 12], //- 通过访问访问ant-views来访问views 文件
['src', '/source/', 10] //- 通过访问访问ant-src来访问source 文件
].map((_) => {
electron.protocol.registerFileProtocol(`ant-${_[0]}`, (req, cb) => {
cb({
path: path.join(__dirname, _[1], req.url.substr(_[2]))
});
});
});
// 初始化窗口
let mainWindow = new BrowserWindow({ let mainWindow = new BrowserWindow({
width: 1040, width: 1040, height: 699,
height: 699, minWidth: 888, minHeight: 555,
minWidth: 888, webgl: false, title: 'AntSword'
minHeight: 555,
webgl: false,
title: 'AntSword'
}); });
// 加载views // 加载views
mainWindow.loadURL(`file:\/\/${__dirname}/views/index.html`); mainWindow.loadURL('ant-views://index.html');
// 调整部分UI // 调整部分UI
const reloadUI = mainWindow.webContents.send.bind( const reloadUI = mainWindow.webContents.send.bind(
...@@ -33,19 +47,23 @@ app ...@@ -33,19 +47,23 @@ app
// 窗口事件监听 // 窗口事件监听
mainWindow mainWindow
.on('closed', () => { mainWindow = null }) .on('close', (event) => {
event.preventDefault();
app.exit(0);
})
.on('resize', reloadUI) .on('resize', reloadUI)
.on('maximize', reloadUI) .on('maximize', reloadUI)
.on('unmaximize', reloadUI) .on('unmaximize', reloadUI)
.on('enter-full-screen', reloadUI) .on('enter-full-screen', reloadUI)
.on('leave-full-screen', reloadUI); .on('leave-full-screen', reloadUI);
// 打开调试控制台 // 打开调试控制台
// mainWindow.webContents.openDevTools(); // mainWindow.webContents.openDevTools();
electron.Logger = require('./modules/logger')(mainWindow); electron.Logger = require('./modules/logger')(mainWindow);
// 初始化模块 // 初始化模块
['menubar', 'request', 'database', 'cache', 'update'].map((_) => { ['menubar', 'request', 'database', 'cache', 'plugStore'].map((_) => {
new ( require(`./modules/${_}`) )(electron, app, mainWindow); new ( require(`./modules/${_}`) )(electron, app, mainWindow);
}); });
}); });
...@@ -14,13 +14,26 @@ const fs = require('fs'), ...@@ -14,13 +14,26 @@ const fs = require('fs'),
class Conf { class Conf {
constructor() { constructor() {
// 获取数据存储目录 // 数据存储目录
this.basePath = path.join( let _oldPath = path.join(
process.env.HOME || process.env.LOCALAPPPATH || process.cwd() || '.', process.env.HOME || process.env.LOCALAPPPATH || process.cwd() || '.',
'.antSword' '.antSword',
'shell.db'
); );
// 创建.antSword目录 // 数据存储目录
this.basePath = path.join(
process.env.AS_WORKDIR,
'antData'
)
// 初始化目录
!fs.existsSync(this.basePath) ? fs.mkdirSync(this.basePath) : null; !fs.existsSync(this.basePath) ? fs.mkdirSync(this.basePath) : null;
// 旧数据搬迁
if (fs.existsSync(_oldPath) && !fs.existsSync(this.dataPath)) {
fs.writeFileSync(
this.dataPath,
fs.readFileSync(_oldPath)
)
}
} }
/** /**
...@@ -28,7 +41,7 @@ class Conf { ...@@ -28,7 +41,7 @@ class Conf {
* @return {String} file-path * @return {String} file-path
*/ */
get dataPath() { get dataPath() {
return path.join(this.basePath, 'shell.db'); return path.join(this.basePath, 'db.ant');
} }
/** /**
...@@ -42,6 +55,28 @@ class Conf { ...@@ -42,6 +55,28 @@ class Conf {
return _; return _;
} }
/**
* 获取插件目录
* - 当前目录为下载的插件保存目录,而并非开发者的插件目录,同时开发者所设置的插件目录也不应为此
* @return {String} plug-path
*/
get plugPath() {
let _ = path.join(this.basePath, '/plugins/');
!fs.existsSync(_) ? fs.mkdirSync(_) : null;
return _;
}
/**
* 获取临时目录
* - 用户存储下载文件等缓存内容
* @return {String} temp-path
*/
get tmpPath() {
let _ = path.join(this.basePath, '/.temp/');
!fs.existsSync(_) ? fs.mkdirSync(_) : null;
return _;
}
/** /**
* 获取package.json配置信息 * 获取package.json配置信息
* @return {Object} [description] * @return {Object} [description]
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
'use strict'; 'use strict';
const Update = require('./update');
class Menubar { class Menubar {
constructor(electron, app, mainWindow) { constructor(electron, app, mainWindow) {
...@@ -48,12 +50,15 @@ class Menubar { ...@@ -48,12 +50,15 @@ class Menubar {
label: LANG['main']['aproxy'], label: LANG['main']['aproxy'],
accelerator: 'Shift+CmdOrCtrl+A', accelerator: 'Shift+CmdOrCtrl+A',
click: event.sender.send.bind(event.sender, 'menubar', 'settings-aproxy') click: event.sender.send.bind(event.sender, 'menubar', 'settings-aproxy')
// }, {
// label: LANG['main']['update'],
// accelerator: 'Shift+CmdOrCtrl+U',
// click: event.sender.send.bind(event.sender, 'menubar', 'settings-update')
}, { }, {
type: 'separator' type: 'separator'
}, {
label: LANG['main']['update'],
enabled: false,
accelerator: 'Shift+CmdOrCtrl+U',
click: () => {
new Update();
}
}, { }, {
label: LANG['main']['settings'], label: LANG['main']['settings'],
accelerator: 'Shift+CmdOrCtrl+S', accelerator: 'Shift+CmdOrCtrl+S',
...@@ -61,9 +66,9 @@ class Menubar { ...@@ -61,9 +66,9 @@ class Menubar {
}, { }, {
type: 'separator' type: 'separator'
}, { }, {
label: LANG['main']['plugin'], label: LANG['main']['pluginStore'],
accelerator: 'Shift+CmdOrCtrl+P', accelerator: 'Shift+CmdOrCtrl+P',
click: event.sender.send.bind(event.sender, 'menubar', 'plugin') click: event.sender.send.bind(event.sender, 'menubar', 'plugin-store')
}, { }, {
type: 'separator' type: 'separator'
}, { }, {
...@@ -145,7 +150,13 @@ class Menubar { ...@@ -145,7 +150,13 @@ class Menubar {
{ {
label: LANG['debug']['restart'], label: LANG['debug']['restart'],
accelerator: 'Shift+CmdOrCtrl+R', accelerator: 'Shift+CmdOrCtrl+R',
click: this.mainWindow.webContents.reload.bind(this.mainWindow.webContents) click: () => {
// 在有多个窗口的时候,不刷新
if (this.electron.BrowserWindow.getAllWindows().length > 1) {
return;
}
this.mainWindow.webContents.reload();//.bind(this.mainWindow.webContents)
}
}, { }, {
label: LANG['debug']['devtools'], label: LANG['debug']['devtools'],
accelerator: 'Alt+CmdOrCtrl+J', accelerator: 'Alt+CmdOrCtrl+J',
......
/**
* 应用商店后台模块
* - 用于进行下载、安装、卸载等后台操作
* create at: 2016/05/25
*/
let logger;
const fs = require('fs');
const path = require('path');
const CONF = require('./config');
const UNZIP = require('extract-zip');
class PlugStore {
constructor(electron, app, mainWindow) {
logger = new electron.Logger('PlugStore');
this.listenDownload(mainWindow);
electron.ipcMain
.on('store-uninstall', (event, plugName) => {
logger.warn('UnInstall', plugName);
// 删除目录
this.rmdir(
path.join(CONF.plugPath, `${plugName}-master`)
).then((ret) => {
event.returnValue = ret;
// 重新加载插件列表
mainWindow.webContents.send('reloadPlug', true);
});
})
.on('store-uninstall-dev', (event, plugPath) => {
logger.warn('UnInstall.DEV', plugPath);
// 删除目录
this.rmdir(plugPath).then((ret) => {
event.returnValue = ret;
// 重新加载插件列表
mainWindow.webContents.send('reloadPlug', true);
});
})
// 获取插件路径
.on('store-config-plugPath', (event) => {
event.returnValue = CONF.plugPath;
})
}
/**
* 监听下载
* @param {Object} mainWindow [description]
* @return {[type]} [description]
*/
listenDownload(mainWindow) {
mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
let fileName = item.getFilename().replace(/\-master\.zip$/,'');
let downLink = item.getURL();
logger.info('down-store-plug', downLink);
// 判断是否下载为插件
if (downLink.indexOf('github.com/asStore') > 0) {
// 1. 设置插件存储目录
let savePath = path.join(CONF.tmpPath, `${fileName}.zip`);
item.setSavePath(savePath);
webContents.send('store-download-progress', {
file: fileName,
type: 'init',
total: item.getTotalBytes()
});
// 2. 插件下载进度更新
item.on('updated', () => {
webContents.send('store-download-progress', {
file: fileName,
type: 'downloading',
size: item.getReceivedBytes()
});
});
// 3. 插件下载完毕
item.on('done', (e, state) => {
webContents.send('store-download-progress', {
file: fileName,
path: savePath,
type: 'downloaded',
state: state
});
if (state !== 'completed') { return };
// 解压安装插件
UNZIP(savePath, {
dir: CONF.plugPath
}, (err) => {
webContents.send('store-download-progress', {
type: 'installed',
file: fileName
});
logger.info('Installed', fileName);
// 重新加载插件列表
mainWindow.webContents.send('reloadPlug', true);
});
});
}
});
}
/**
* 删除目录
* @param {String} dir 目录
* @return {[type]} [description]
*/
rmdir(dir) {
return new Promise((res, rej) => {
let ret = true;
// 循环删除目录
const _rmdir = (_dir) => {
if (!fs.existsSync(_dir)) { return }
fs.readdirSync(_dir).map((_) => {
// 生成完整路径
let _path = path.join(dir, _);
// 如果是目录,则继续循环,否则删除
if (fs.lstatSync(_path).isDirectory()) {
return _rmdir(_path);
}
fs.unlinkSync(_path);
});
fs.rmdirSync(_dir);
}
try{
_rmdir(dir);
} catch (e) {
ret = e;
}
return res(ret);
});
}
}
module.exports = PlugStore;
## 中国蚁剑::UI框架库
> 用于在插件、扩展以及自身模块中调用。
**本UI框架基于`dhtmlx`进行二次封装API**
开发者可以采用原生框架API进行开发,也可以使用本UI框架进行开发。
原生API文档:[http://docs.dhtmlx.com/](http://docs.dhtmlx.com/)
/**
* UI::tabbar
* - 创建一个面板
* 开写:2016/05/03
* 更新:-
* 作者:蚁逅 <https://github.com/antoor>
*/
'use strict';
class Tabbar {
constructor(opts) {
// 生成一个随机ID,用于指定唯一的面板
let id = 'tabbar_' + (Math.random() * +new Date).toString(16).replace('.', '').substr(0,11);
let tabbar = antSword['tabbar'];
// 添加面板对象
tabbar.addTab(
id,
'<i class="fa fa-puzzle-piece"></i>',
null, null, true, true
);
this.cell = tabbar.tabs(id);
}
/**
* 面板获取焦点
* @return {[type]} [description]
* @return {Object} this
*/
active() {
this.cell.setActive();
return this;
}
/**
* 关闭面板
* @return {Object} this
*/
close() {
this.cell.close();
return this;
}
/**
* 设置面板标题
* @param {String} title = 'New Title' [description]
* @return {Object} this
*/
setTitle(title = 'New Title') {
this.cell.setText(`<i class="fa fa-puzzle-piece"></i> ${title}`);
return this;
}
/**
* 安全输出HTML
* - 采用`iframe`框架进行HTML输出,避免变量污染&&一些安全问题
* @param {String} html = "" [description]
* @return {Object} this
*/
safeHTML(html = "") {
let _html = new Buffer(html).toString('base64');
let _iframe = `
<iframe
src="data:text/html;base64,${_html}"
style="width:100%;height:100%;border:0;padding:0;margin:0;">
</iframe>
`;
this.cell.attachHTMLString(_iframe);
return this;
}
/**
* 显示加载中
* @param {Boolean} loading = true 是否显示/false=隐藏
* @return {Object} this
*/
showLoading(loading = true) {
this.cell[loading ? 'progressOn' : 'progressOff']();
return this;
}
}
module.exports = Tabbar;
/**
* UI::Window
* - 弹窗窗口
* 开写:2016/05/03
* 更新:-
* 作者:蚁逅 <https://github.com/antoor>
*/
'use strict';
class Window {
/**
* 初始化一个窗口对象
* @param {Object} opts 窗口设置(title,width,height
* @return {[type]} [description]
*/
constructor(opts) {
// 生成一个随机ID,用于指定唯一的窗口
let id = 'win_' + (Math.random() * +new Date).toString(16).replace('.', '').substr(0,11);
// 默认配置
let opt = $.extend({
title: id,
width: 500,
height: 400,
// 在那个dom内显示
view: document.body
}, opts);
// 创建窗口
let winObj = new dhtmlXWindows();
winObj.attachViewportTo(opt['view']);
let win = winObj.createWindow(
id, 0, 0,
opt['width'], opt['height']
);
win.setText(opt['title']);
win.centerOnScreen();
win.button('minmax').show();
win.button('minmax').enable();
this.win = win;
}
/**
* 关闭窗口
* @return {[type]} [description]
*/
close() {
this.win.close();
}
/**
* 设置标题
* @param {String} title = 'New Title' 新标题
*/
setTitle(title = 'New Title') {
this.win.setText(title);
}
}
module.exports = Window;
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>AntSword.Store</title>
<!-- <script src="http://localhost:8000/static/js/loader.js"></script> -->
<script src="https://asstore.github.io/v0.1/loader.js"></script>
<style>
html,body,#container,#loader{
margin: 0;padding: 0;width:100%;height:100%;
}
#loader {
display: flex;
justify-content: center;align-items: center;
}
::-webkit-scrollbar-track-piece {
background-color:#f5f5f5;
border-left:1px solid #d2d2d2;
}
::-webkit-scrollbar {
width:13px;
height:13px;
}
::-webkit-scrollbar-thumb {
background-color:#c2c2c2;
background-clip:padding-box;
border:1px solid #979797;
min-height:28px;
}
::-webkit-scrollbar-thumb:hover {
border:1px solid #636363;
background-color:#929292;
}
.cssload-loader{position:relative;width:62px;height:62px;border-radius:50%;perspective:780px}.cssload-inner{position:absolute;width:100%;height:100%;box-sizing:border-box;border-radius:50%}.cssload-inner.cssload-one{left:0;top:0;animation:cssload-rotate-one 1.15s linear infinite;border-bottom:3px solid #000}.cssload-inner.cssload-two{right:0;top:0;animation:cssload-rotate-two 1.15s linear infinite;border-right:3px solid #000}.cssload-inner.cssload-three{right:0;bottom:0;animation:cssload-rotate-three 1.15s linear infinite;border-top:3px solid #000}@keyframes cssload-rotate-one{0%{transform:rotateX(35deg) rotateY(-45deg) rotateZ(0deg)}100%{transform:rotateX(35deg) rotateY(-45deg) rotateZ(360deg)}}@keyframes cssload-rotate-two{0%{transform:rotateX(50deg) rotateY(10deg) rotateZ(0deg)}100%{transform:rotateX(50deg) rotateY(10deg) rotateZ(360deg)}}@keyframes cssload-rotate-three{0%{transform:rotateX(35deg) rotateY(55deg) rotateZ(0deg)}100%{transform:rotateX(35deg) rotateY(55deg) rotateZ(360deg)}}</style>
</head>
<body>
<div id="container">
<div id="loader">
<div class="cssload-loader">
<div class="cssload-inner cssload-one"></div>
<div class="cssload-inner cssload-two"></div>
<div class="cssload-inner cssload-three"></div>
</div>
</div>
</div>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment