Browse Source

Add Image Settings
Auto get QiNiu Config

zhuzhuyule 7 years ago
parent
commit
2ac73a1a93

+ 7 - 1
app/moe-config-default.js

@@ -44,5 +44,11 @@ module.exports = {
     'hexo-auto-setting': false,
     'hexo-config-enable': false,
     'hexo-config': '',
-    'hexo-tag-paths': []
+    'hexo-tag-paths': [] ,
+    'image-source-center': '',
+    'image-web-type':'sm',
+    'image-qiniu-accessKey':'',
+    'image-qiniu-secretKey':'',
+    'image-qiniu-bucket':'',
+    'image-qiniu-url':'',
 };

+ 24 - 0
app/moe-l10n.js

@@ -172,6 +172,7 @@ const strings = {
         "File Rename": "File Rename",
         "Hexo Auto": "Auto Hexo Setting",
         "Hexo": "Hexo",
+        "Hexo Mode": "Hexo Mode",
         "Extend Highlight": "Extend Highlight",
         "Hexo Config": "Hexo Config",
         "Hexo tags": "Tag Templates",
@@ -210,6 +211,17 @@ const strings = {
         "Prompt": "Prompt",
         "Never": "Never",
 
+        "Image": "Image",
+        "Source Center": "Source Center",
+        "Web Type": "Web Type",
+        "WebSM": "WebSM",
+        "QiNiu": "QiNiu",
+        "AccessKey": "Access Key",
+        "SecretKey": "Secret Key",
+        "Bucket": "Bucket",
+        "BaseWeb": "Base Web",
+
+
         "Undo": "Undo",
         "Redo": "Redo",
         "Cut": "Cut",
@@ -236,6 +248,7 @@ const strings = {
         "Toggle Developer Tools": "Toggle Developer Tools",
         "Preference": "Preference",
 
+        "No Find File.": "No Find File.",
         "Upload Finished": "Upload Finished.",
         "Access Denied.": "Access Denied.",
         "Upload file count limit.": "Upload file count limit.",
@@ -318,6 +331,7 @@ const strings = {
         "File Rename": "同步文件名",
         "Hexo Auto": "Hexo自动设置",
         "Hexo": "Hexo",
+        "Hexo Mode": "Hexo模式",
         "Extend Highlight": "自定义代码块",
         "Hexo Config": "Hexo配置文件",
         "Hexo tags": "Tag模板目录",
@@ -357,6 +371,15 @@ const strings = {
         "Prompt": "询问",
         "Never": "从不",
 
+        "Image": "图片",
+        "Source Center": "默认资源库",
+        "Web Type": "云图类型",
+        "WebSM": "SM.MS 图床",
+        "QiNiu": "七牛云",
+        "AccessKey": "Access Key",
+        "SecretKey": "Secret Key",
+        "Bucket": "存储空间",
+        "BaseWeb": "域名",
 
         "Undo": "撤销",
         "Redo": "重做",
@@ -384,6 +407,7 @@ const strings = {
         "Toggle Developer Tools": "切换开发者工具",
         "Preference": "偏好设置",
 
+        "No Find File.": "未找到文件.",
         "Upload Finished": "上传完成.",
         "Access Denied.": "服务器拒绝访问.",
         "Upload file count limit.": "超出上传文件数量.",

+ 68 - 61
views/main/hexo-image.js

@@ -60,8 +60,8 @@ class ImgManager {
 
     updateBase() {
         let rootPaht = '';
-        if (moeApp.config.get('image-path')) {
-            rootPaht = moeApp.config.get('image-path');
+        if (moeApp.config.get('image-source-center')) {
+            rootPaht = moeApp.config.get('image-source-center');
         } else if (moeApp.useHexo && hexo.config.__basedir) {
             rootPaht = path.join(hexo.config.__basedir, 'source', 'images');
         } else {
@@ -97,6 +97,18 @@ class ImgManager {
         }
         return path.resolve(this.imgBasePath, p).replace(/\\/g, '/');
     }
+    getQiNiuServer(){
+        if(!this.qiniuServer){
+            this.qiniuServer = new (require('./hexo-qiniu'))();
+            this.qiniuServer.update(
+                moeApp.config.get('image-qiniu-accessKey'),
+                moeApp.config.get('image-qiniu-secretKey'),
+                moeApp.config.get('image-qiniu-bucket'),
+                moeApp.config.get('image-qiniu-url')
+            );
+        }
+        return this.qiniuServer;
+    }
 
     getImage(img) {
         if (typeof img === "string") {
@@ -175,11 +187,11 @@ class ImgManager {
         return this.getImageOfPath(imageAbsolutePath, md5ID)
     }
 
-    updateImgage(imgName, newImgName) {
+    renameImage(imgName, newImgName) {
         this.updateDictionary(imgName + '$', newImgName)
     }
 
-    updateFile(fileName) {
+    renameDirPath(fileName) {
         this.updateDictionary('/' + this.filename + '/', '/' + fileName + '/')
         this.filename = fileName;
     }
@@ -205,7 +217,7 @@ class ImgManager {
         xhr.send();
     }
 
-    uploadSm(file, callback) {
+    asyncUploadToSm(imgPath, file, callback) {
         let formdata = new FormData();
         formdata.append('smfile', file);
         let xhr = new XMLHttpRequest();
@@ -216,48 +228,83 @@ class ImgManager {
                 if (xhr.status === 200) {
                     // 成功,通过responseText拿到响应的文本:
                     if (typeof callback === "function") {
-                        callback(file.name, JSON.parse(xhr.responseText))
+                        callback(imgPath, JSON.parse(xhr.responseText))
                     }
                 } else {
                     // 失败,根据响应码判断失败原因:
                     if (typeof callback === "function") {
-                        callback(file.name, {code: xhr.status})
+                        callback(imgPath, {code: xhr.status})
                     }
                 }
-            } else {
-                // HTTP请求还在继续...
             }
         }
-        xhr.upload.onprogress = (e) => {
-            console.log(Math.floor(100 * e.loaded / e.total) + '%');
-        }
+        // xhr.upload.onprogress = (e) => {
+        //     console.log(Math.floor(100 * e.loaded / e.total) + '%');
+        // }
         xhr.send(formdata)
     }
 
-    uploadQiNiu(imgPath, callback) {
-        require('./hexo-qiniu')(this.relativePath(imgPath).slice(1),imgPath,callback)
+    asyncUploadToQiNiu(imgPath, callback) {
+        this.getQiNiuServer().uploadFile(imgPath,this.relativePath(imgPath).slice(1),callback);
     }
 
-    uploadPath(imgPath, callback) {
-        if (!this.isQiNiu){
-            this.uploadQiNiu(imgPath,callback)
+    asyncUploadFile(imgPath, callback) {
+        if (this.isQiNiu){
+            this.asyncUploadToQiNiu(imgPath,callback)
         } else {
             let file = new File([fs.readFileSync(imgPath)], md5(imgPath) + path.extname(imgPath), {type: 'image/' + path.extname(imgPath).slice(1)});
-            return this.uploadSm(file, callback);
+            return this.asyncUploadToSm(imgPath,file, callback);
         }
     }
 
     uploadLocalSrc() {
         this.isUploading = true;
         this.timeout = 0;
-        this.isQiNiu = moeApp.config.get('image-config-qiniu');
+        this.isQiNiu = moeApp.config.get('image-web-type') == 'qiniu';
 
         let finishedCount = 0;
         let uploadList = new Map();
         let successList = new Map();
         let errorList = new Map();
 
-        function replaceSrc() {
+        function checktime(isBreak) {
+            clearTimeout(imgManager.timeout);
+            if (!isBreak)
+                imgManager.timeout = setTimeout(() => {
+                    uploadEnd(true);
+                }, 30000)
+        }
+
+        document.querySelector('#right-panel').querySelectorAll('img[localimg="true"]').forEach((item) => {
+            let filePath = decodeURI(item.src).replace(/^file:\/\/\//, '');
+            if (fs.existsSync(filePath)) {
+                uploadList.set(filePath, filePath);
+            } else {
+                errorList.set(filePath, 'No Find File.');
+            }
+        })
+
+        checktime();
+        uploadList.forEach((filepath) => {
+            imgManager.asyncUploadFile(filepath, uploadRequest)
+        })
+
+        function uploadRequest(fileID, response) {
+            finishedCount++;
+            console.log(finishedCount + '/' + uploadList.size)
+            if (response.code == 'success') {
+                successList.set(fileID, response)
+            } else {
+                errorList.set(fileID, response.error)
+            }
+            if (finishedCount >= uploadList.size) {
+                checktime(true);
+                uploadEnd(false);
+                return;
+            }
+            checktime();
+        }
+        function updateSrc() {
             let value = editor.getValue();
             successList.forEach((v, k) => {
                 value = value.replace(new RegExp(imgManager.relativePath(k), 'g'), v.data.url);
@@ -269,10 +316,9 @@ class ImgManager {
             hexoWindow.content = value;
             hexoWindow.changed = true;
         }
-
         function uploadEnd(isTimeout) {
             try {
-                replaceSrc();
+                updateSrc();
                 let errMsg = '';
                 errorList.forEach((v, k) => {
                     errMsg += k + ':' + __(v) + '</br>';
@@ -296,45 +342,6 @@ class ImgManager {
                 imgManager.isUploading = false;
             }
         }
-
-        function checktime(isBreak) {
-            clearTimeout(imgManager.timeout);
-            if (!isBreak)
-                imgManager.timeout = setTimeout(() => {
-                    uploadEnd(true);
-                }, 30000)
-        }
-
-        document.querySelector('#right-panel').querySelectorAll('img[localimg="true"]').forEach((item) => {
-            let filePath = item.src.replace(/^file:\/\/\//, '');
-            if (fs.existsSync(filePath)) {
-                uploadList.set(md5(filePath), filePath);
-            } else {
-                errorList.set(filePath, 'No Find File.');
-            }
-        })
-
-        checktime();
-        uploadList.forEach((filepath) => {
-            imgManager.uploadPath(filepath, uploadRequest)
-        })
-
-        function uploadRequest(fileID, response) {
-            finishedCount++;
-            console.log(finishedCount + '/' + uploadList.size)
-            fileID = path.basename(fileID, path.extname(fileID));
-            if (response.code == 'success') {
-                successList.set(uploadList.get(fileID), response)
-            } else {
-                errorList.set(uploadList.get(fileID), response.error)
-            }
-            if (finishedCount >= uploadList.size) {
-                checktime(true);
-                uploadEnd(false);
-                return;
-            }
-            checktime();
-        }
     }
 }
 

+ 157 - 42
views/main/hexo-qiniu.js

@@ -1,48 +1,163 @@
-var qiniu = require("qiniu");
-//需要填写你的 Access Key 和 Secret Key
-qiniu.conf.ACCESS_KEY = '';
-qiniu.conf.SECRET_KEY = '';
-qiniu.baseWeb = 'http://*.bkt.clouddn.com/'
-//要上传的空间
-const bucket = 'test';
-
-//构建上传策略函数,设置回调的url以及需要回调给业务服务器的数据
-function uptoken(bucket, key) {
-    var options = {
-        scope: bucket + ":" + key
-    };
-    var putPolicy = new qiniu.rs.PutPolicy(options);
-    return putPolicy.uploadToken();
-}
+class qiniuServer {
+    constructor(acessKey, secretKey) {
+        this.qiniu = require('qiniu');
+        this.bucket = '';
+        this.qiniu.conf.ACCESS_KEY = acessKey;
+        this.qiniu.conf.SECRET_KEY = secretKey;
+        this.mac = new this.qiniu.auth.digest.Mac(acessKey, secretKey);
+    }
+
+    /**
+     * 更新信息
+     * @param acessKey
+     * @param secretKey
+     * @param bucket
+     * @param url
+     */
+    update(acessKey, secretKey, bucket, url) {
+        this.qiniu.conf.ACCESS_KEY = acessKey;
+        this.qiniu.conf.SECRET_KEY = secretKey;
+        this.mac = new this.qiniu.auth.digest.Mac(acessKey, secretKey);
+        this.bucket = bucket||this.bucket||'';
+        this.url = url||this.url||'';
+    }
+
+    /**
+     * 生成空间 文件名
+     * @param bucket        空间名(必传)
+     * @param key           Key值
+     * @returns {string}
+     */
+    getUptoken(bucket, key) {
+        var options = {
+            scope: bucket + ":" + key
+        };
+        var putPolicy = new this.qiniu.rs.PutPolicy(options);
+        return putPolicy.uploadToken();
+    }
+
+    /**
+     * 生成 AccessToken
+     * @param url
+     * @returns {string}
+     */
+    getAccessToken(url) {
+        return this.qiniu.util.generateAccessToken(this.mac, url);
+    }
 
-//构造上传函数
-function uploadFile(key, localFile,callback) {
-    //生成上传 Token
-    let token = uptoken(bucket, key);
-    var formUploader = new qiniu.form_up.FormUploader(qiniu.conf);
-    var extra = new qiniu.form_up.PutExtra();
-    formUploader.putFile(token, key, localFile, extra,
-        function (respErr, respBody, respInfo) {
-            if (respErr) {
-                throw respErr;
+    /**
+     * 异步获取空间列表
+     */
+    getBuckets(callback) {
+        const url_api_bukets = 'https://rs.qbox.me/buckets';
+        let xhr = new XMLHttpRequest();
+        xhr.open('get', url_api_bukets);
+        xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
+        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+        xhr.onreadystatechange = () => {
+            if (xhr.readyState === 4) { // 成功完成
+                console.log(xhr.status)
+                console.log(xhr.responseText)
+                if (typeof callback === "function") {
+                    callback({
+                        code: xhr.status,
+                        data: JSON.parse(xhr.responseText)
+                    })
+                }
             }
-            console.log(2, respBody);
-            if (respInfo.statusCode == 200 || respInfo.statusCode ==  579) {
-                console.log(respBody);
-                let response = {
-                    code: 'success',
-                    data: {
-                        url: qiniu.baseWeb+respBody.key
-                    }
+        }
+        xhr.send();
+    }
+
+    /**
+     * 异步获取空间地址URL列表
+     * @param buketName     空间名(必传)
+     */
+    getBucketsUrl(buketName,callback) {
+        const url_api_bukets = 'https://api.qiniu.com/v6/domain/list?tbl=' + buketName;
+
+        let xhr = new XMLHttpRequest();
+        xhr.open('get', url_api_bukets);
+        xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
+        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+        xhr.onreadystatechange = () => {
+            if (xhr.readyState === 4) { // 成功完成
+                console.log(xhr.status)
+                console.log(xhr.responseText)
+                if (typeof callback === "function") {
+                    callback({
+                        code: xhr.status,
+                        data: JSON.parse(xhr.responseText)
+                    })
                 }
-                 callback(md5(localFile),response);
-            } else {
-                console.log(respInfo.statusCode);
-                let response = {
-                    error: respInfo.statusCode + respBody,
+            }
+        }
+        xhr.send();
+    }
+
+    /**
+     * 异步获取服务器文件列表
+     * @param buketName     空间名称(必传)
+     * @param prefix        虚拟目录(选填)
+     */
+    getBucketsFiles(buketName, prefix, callback) {
+        if (!buketName) return;
+        const url_api_bukets = require('util').format(
+            'https://rsf.qbox.me/list?bucket=%s&marker=&limit=1000&prefix=%s&delimiter=/', buketName, prefix || '')
+        let xhr = new XMLHttpRequest();
+        xhr.open('get', url_api_bukets);
+        xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
+        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+        xhr.onreadystatechange = () => {
+            if (xhr.readyState === 4) { // 成功完成
+                console.log(xhr.status)
+                console.log(xhr.responseText)
+                if (typeof callback === "function") {
+                    callback({
+                        code: xhr.status,
+                        data: JSON.parse(xhr.responseText)
+                    })
                 }
-                callback(md5(localFile),response);
             }
-        });
+        }
+        xhr.send();
+    }
+
+    /**
+     * 异步上传单个文件
+     * @param localFile         本地文件全路径
+     * @param serverFileName    服务器保存名称(可带地址)
+     * @param callback          上传完成响应回调
+     */
+    uploadFile(localFile,serverFileName, callback) {
+        //生成上传 Token
+        let token = this.getUptoken(this.bucket, serverFileName);
+        var formUploader = new this.qiniu.form_up.FormUploader(this.qiniu.conf);
+        var extra = new this.qiniu.form_up.PutExtra();
+        let qiniuServer = this;
+        formUploader.putFile(token, serverFileName, localFile, extra,
+            function (respErr, respBody, respInfo) {
+                console.log( respBody);
+                if (respErr) {
+                    throw respErr;
+                }
+                if (respInfo.statusCode == 200 || respInfo.statusCode == 579) {
+                    let response = {
+                        code: 'success',
+                        data: {
+                            url: qiniuServer.url + respBody.key
+                        }
+                    }
+                    callback(localFile, response);
+                } else {
+                    console.log(respInfo.statusCode);
+                    let response = {
+                        error: respInfo.statusCode + respBody,
+                    }
+                    callback(localFile, response);
+                }
+            });
+    }
 }
-module.exports = uploadFile;
+
+module.exports = qiniuServer;

+ 2 - 2
views/main/hexo/previewer.js

@@ -85,8 +85,8 @@ Previewer.prototype.render = function (content, MoeMark, options, callback) {
                 if (!fs.existsSync(src)) {
                     srcLocal = imgManager.resolvePath(src)|| '';
                     //首先查询用户设置目录
-                    if (!srcLocal && moeApp.config.get('image-path')) {
-                        srcLocal = path.join(moeApp.config.get('image-path'), src);
+                    if (!srcLocal && moeApp.config.get('image-source-center')) {
+                        srcLocal = path.join(moeApp.config.get('image-source-center'), src);
                         if (!fs.existsSync(srcLocal))
                             srcLocal = '';
                     }

+ 2 - 2
views/main/moe-document.js

@@ -285,7 +285,7 @@ $(() => {
                             let relativePathOld = imgManager.relativePath(imgPathOld);
                             let relativePathNew = imgManager.relativePath(imgPathNew);
                             editor.setValue(editor.getValue().replace(new RegExp('\\]\\(/' + relativePathOld, 'g'), '](/' + relativePathNew))
-                            imgManager.updateFile(nameNew);
+                            imgManager.renameDirPath(nameNew);
                         })
                     })
                 }
@@ -361,7 +361,7 @@ $(() => {
                 editor.setValue(hexoWindow.content);
                 hexoWindow.changed = false;
                 //更新字典
-                imgManager.updateImgage(path.basename(relativePath),newName);
+                imgManager.renameImage(path.basename(relativePath),newName);
                 //更新结果提示
                 renameForm.classList.remove('show');
                 window.popMessageShell(e, {

+ 53 - 1
views/main/moe-settings.js

@@ -185,6 +185,46 @@ function setCustomCSSs(val) {
     }
 }
 
+function setTabSize(val) {
+    window.editor.setOption('tabSize', parseInt(val));
+    window.editor.setOption('indentUnit', parseInt(val));
+    window.editor.refresh();
+}
+
+function setSourceCenter(val) {
+    if(imgManager ){
+        imgManager.updateBase();
+    }
+}
+
+function setImageWebType(val) {
+}
+
+function SetQiNiuAccessKey(val) {
+    if(imgManager && imgManager.qiniuServer){
+        imgManager.qiniuServer.update(val)
+    }
+}
+
+function SetQiNiuSecretKey(val) {
+    if(imgManager && imgManager.qiniuServer){
+        imgManager.qiniuServer.update('',val)
+    }
+}
+
+
+function SetQiNiuBucket(val) {
+    if(imgManager && imgManager.qiniuServer){
+        imgManager.qiniuServer.update('','',val)
+    }
+}
+
+function SetQiNiuWeb(val) {
+    if(imgManager && imgManager.qiniuServer){
+        imgManager.qiniuServer.update('','','',val)
+    }
+}
+
 tryRun(setEditorFont, moeApp.config.get('editor-font'));
 tryRun(setShowLineNumber, !!moeApp.config.get('editor-ShowLineNumber'));
 tryRun(setScrollTogether, moeApp.config.get('scroll-Together'));
@@ -213,7 +253,7 @@ ipcRenderer.on('setting-changed', (e, arg) => {
         tryRun(setEditorFontSize, arg.val);
     } else if (arg.key === 'editor-line-height') {
         tryRun(setEditorLineHeight, arg.val);
-    } else if (arg.key === 'image-path') {
+    } else if (arg.key === 'image-source-center') {
         tryRun(setImagePath, arg.val);
     } else if (arg.key === 'math') {
         tryRun(setMath, arg.val);
@@ -235,5 +275,17 @@ ipcRenderer.on('setting-changed', (e, arg) => {
         tryRun(setHexoTagPaths, arg.val);
     } else if (arg.key === 'custom-csss') {
         tryRun(setCustomCSSs, arg.val);
+    } else if (arg.key === 'image-source-center') {
+        tryRun(setSourceCenter, arg.val);
+    } else if (arg.key === 'image-web-type') {
+        tryRun(setImageWebType, arg.val);
+    } else if (arg.key === 'image-qiniu-accessKey') {
+        tryRun(SetQiNiuAccessKey, arg.val);
+    } else if (arg.key === 'image-qiniu-secretKey') {
+        tryRun(SetQiNiuSecretKey, arg.val);
+    } else if (arg.key === 'image-qiniu-bucket') {
+        tryRun(SetQiNiuBucket, arg.val);
+    } else if (arg.key === 'image-qiniu-url') {
+        tryRun(SetQiNiuWeb, arg.val);
     }
 });

+ 165 - 16
views/settings/moe-settings.js

@@ -76,18 +76,6 @@ document.addEventListener('DOMContentLoaded', () => {
         }
     }
 
-    // Image Path
-    let defImagePath =  document.querySelector('.settings-item[data-key="image-path"]');
-    defImagePath.value = moeApp.config.get('image-path');
-    document.querySelector('#image-path-button').addEventListener('click', () => {
-        dialog.showOpenDialog(window.hexoWindow, {properties: ['openDirectory']}, (dirPath) => {
-            if (!dirPath || dirPath.length === 0) return;
-            defImagePath.value = dirPath;
-            moeApp.config.set('image-path', dirPath.replace(/\\/g, '/'));
-            ipcRenderer.send('image-path', {key: "image-path", val: dirPath});
-        });
-    });
-
     // Custom render themes
     let renderThemeSelect = document.querySelector('select[data-key="render-theme"]');
 
@@ -234,11 +222,12 @@ document.addEventListener('DOMContentLoaded', () => {
             ]
         }, (fileName) => {
             if (!fileName || fileName.length === 0) return;
-            moeApp.config.set('hexo-config', fileName);
-            console.log(fileName);
-            if (hexoConfigInput.value !== fileName)
+            fileName = fileName[0];
+            if (hexoConfigInput.value !== fileName){
+                moeApp.config.set('hexo-config', fileName);
                 ipcRenderer.send('setting-changed', {key: 'hexo-config', val: fileName});
-            hexoConfigInput.value = fileName;
+                hexoConfigInput.value = fileName;
+            }
         });
     });
 
@@ -465,4 +454,164 @@ document.addEventListener('DOMContentLoaded', () => {
             )
         }
     })
+
+
+    //
+    let imageSourceButton = document.querySelector('#image-source-center-btn');
+    let imageSourceInput = document.querySelector('#image-source-center');
+    imageSourceInput.value = moeApp.config.get(imageSourceInput.id);
+    imageSourceButton.addEventListener('click', () => {
+        dialog.showOpenDialog(window.hexoWindow, {
+            properties: ['openDirectory'],
+            filters: [
+                {name: __('All Files'), extensions: ['yml']},
+                {name: __('All Files'), extensions: ['*']}
+            ]
+        }, (fileName) => {
+            if (!fileName || fileName.length === 0) return;
+            fileName = fileName[0];
+            if (imageSourceInput.value !== fileName){
+                moeApp.config.set(imageSourceInput.id, fileName);
+                ipcRenderer.send('setting-changed', {key: imageSourceInput.id, val: fileName});
+                imageSourceInput.value = fileName;
+            }
+        });
+    });
+
+    let imageTabItem = document.querySelector('.item[data-tab="image"]');
+    let imageTabContents = document.querySelector('.panel[data-tab="image"]');
+    let imageType = imageTabContents.querySelector('#image-web-type');
+    let imageAccessKey = imageTabContents.querySelector('#image-qiniu-accessKey');
+    let imageSecretKey = imageTabContents.querySelector('#image-qiniu-secretKey');
+    let imageBucket = imageTabContents.querySelector('#image-qiniu-bucket');
+    let imageBaseWeb = imageTabContents.querySelector('#image-qiniu-url');
+
+    function hasQiNiuServer() {
+        if (imageAccessKey.value && imageSecretKey.value && !global.qiniuServer) {
+            global.qiniuServer = new (require('../main/hexo-qiniu'))(imageAccessKey.value, imageSecretKey.value)
+            return true;
+        }
+        return global.qiniuServer;
+    }
+
+    function checkBuckets() {
+        if (hasQiNiuServer()) {
+            let oldBucket = moeApp.config.get(imageBucket.id);
+            qiniuServer.getBuckets((response) => {
+                imageBucket.innerHTML = '';
+                if (response.code == 200) {
+                    response.data.forEach((name) => {
+                        let option = document.createElement('option');
+                        option.value = name;
+                        option.innerText = name;
+                        imageBucket.appendChild(option);
+                        if ((option.value == oldBucket)) {
+                            option.selected = true;
+                            imageBucket.value = option.value;
+                        }
+                    })
+                    if (!imageBucket.value && imageBucket.firstChild){
+                        imageBucket.value = imageBucket.firstChild.value;
+                    }
+                    let event = new Event('change');
+                    imageBucket.dispatchEvent(event);
+                } else {
+                }
+            })
+        }
+    }
+    function checkURLs(bucket) {
+        if (hasQiNiuServer()) {
+            let oldurl = moeApp.config.get(imageBaseWeb.id);
+            qiniuServer.getBucketsUrl(bucket,(response) => {
+                if (response.code == 200) {
+                    imageBaseWeb.innerHTML = '';
+                    response.data.forEach((url) => {
+                        let option = document.createElement('option');
+                        option.value = `https://${url}/`;
+                        option.innerText = url;
+                        imageBaseWeb.appendChild(option);
+                        if ((option.value == oldurl)) {
+                            option.selected = true;
+                            imageBaseWeb.value = oldurl;
+                        }
+                    })
+                    if (!imageBaseWeb.value && imageBaseWeb.firstChild){
+                        imageBaseWeb.value = imageBaseWeb.firstChild.value;
+                    }
+                    let event = new Event('change');
+                    imageBaseWeb.dispatchEvent(event);
+                } else {
+                }
+            })
+        }
+    }
+
+    imageType.addEventListener('change', function (e) {
+        moeApp.config.set(imageType.id, imageType.value);
+        let imageQiNiuItems = imageTabContents.querySelectorAll('tr[data-type="image-qiniu"]')
+        if (imageType.value == 'qiniu') {
+            imageAccessKey.value = moeApp.config.get(imageAccessKey.id);
+            imageSecretKey.value = moeApp.config.get(imageSecretKey.id);
+            checkBuckets();
+            imageQiNiuItems.forEach((item) => {
+                item.style.display = 'table-row'
+            })
+        } else {
+            imageQiNiuItems.forEach((item) => {
+                item.style.display = 'none'
+            })
+            ipcRenderer.send('setting-changed', {key: imageType.id, val: imageType.value});
+        }
+        imageTabItem.click();
+    })
+
+    imageAccessKey.addEventListener('blur',()=>{
+        imageAccessKey.type = 'password';
+        if (imageAccessKey.value && imageAccessKey.value == 40 && imageSecretKey.value && imageSecretKey.value == 40){
+            if (hasQiNiuServer()){
+                qiniuServer.update(imageAccessKey.value,imageSecretKey.value)
+                ipcRenderer.send('setting-changed', {key: imageAccessKey.id, val: imageAccessKey.value});
+
+            }
+            let event = new Event('change');
+            imageBucket.dispatchEvent(event);
+        }
+    })
+    imageAccessKey.addEventListener('focus',()=>{
+        imageAccessKey.type = 'type';
+    })
+    imageSecretKey.addEventListener('blur',()=>{
+        imageSecretKey.type = 'password';
+        if (imageAccessKey.value && imageAccessKey.value == 40 && imageSecretKey.value && imageSecretKey.value == 40){
+            if (hasQiNiuServer()){
+                qiniuServer.update(imageAccessKey.value,imageSecretKey.value)
+                ipcRenderer.send('setting-changed', {key: imageSecretKey.id, val: imageSecretKey.value});
+            }
+            let event = new Event('change');
+            imageBucket.dispatchEvent(event);
+        }
+    })
+
+    imageSecretKey.addEventListener('focus',()=>{
+        imageSecretKey.type = 'type';
+    })
+
+    imageBucket.addEventListener('change', function (e) {
+        moeApp.config.set(imageBucket.id, imageBucket.value);
+        checkURLs(imageBucket.value);
+        ipcRenderer.send('setting-changed', {key: imageBucket.id, val: imageBucket.value});
+    })
+
+    imageBaseWeb.addEventListener('change', function (e) {
+        moeApp.config.set(imageBaseWeb.id, imageBaseWeb.value);
+        ipcRenderer.send('setting-changed', {key: imageBaseWeb.id, val: imageBaseWeb.value});
+    })
+
+    let type =  moeApp.config.get(imageType.id);
+    if (type == 'qiniu') {
+        imageType.value = type;
+        let event = new Event('change');
+        imageType.dispatchEvent(event);
+    }
 });

+ 6 - 25
views/settings/moe-tabview.js

@@ -22,19 +22,17 @@ document.addEventListener('DOMContentLoaded', () => {
     const tabs = tabView.getElementsByClassName('item');
     const panels = tabView.getElementsByClassName('panel');
     const hexoWindow = require('electron').remote.getCurrentWindow();
+    const barHeight = document.querySelector('.bar').clientHeight + 40;
 
     for (const tab of tabs) {
         const s = tab.getAttribute('data-tab');
         tab.addEventListener('click', () => {
-            if (tab.classList.contains('selected')) return;
-
-            let oldHeight = null, newHeight = null;
+            let newHeight = null;
             for (const panel of panels) {
                 if (panel.getAttribute('data-tab') === s) {
                     panel.style.display = 'block';
-                    newHeight = panel.getElementsByTagName('table')[0].clientHeight;
+                    newHeight = panel.clientHeight;
                 } else if (panel.style.display === 'block') {
-                    oldHeight = panel.getElementsByTagName('table')[0].clientHeight;
                     panel.style.display = 'none';
                 }
             }
@@ -44,27 +42,10 @@ document.addEventListener('DOMContentLoaded', () => {
             }
             tab.classList.add('selected');
 
-            if (oldHeight !== null && newHeight !== null && oldHeight !== newHeight) {
-                let size = hexoWindow.getSize();
-                /*
-                // The animate has flickering issue
-                let delta = newHeight - oldHeight;
-                const sgn = Math.sign(delta);
-                delta *= sgn;
-                let unit = 20;
-                function ani() {
-                    if (delta < unit) unit = delta;
-                    size[1] += unit * sgn;
-                    delta -= unit;
-                    hexoWindow.setSize(size[0], size[1], false);
-                    if (delta) setTimeout(ani, 20);
-                }
-                ani();
-                */
-                hexoWindow.setSize(size[0], size[1] + newHeight - oldHeight, true);
+            let size = hexoWindow.getSize();
+            if (newHeight !== null && size[1] !== newHeight) {
+                hexoWindow.setSize(size[0], barHeight + newHeight, true);
             }
         });
     }
-
-    ;
 })

+ 53 - 0
views/settings/settings.html

@@ -69,6 +69,10 @@
                         <div class="image"><i class="fa fa-magic" aria-hidden="true"></i></div>
                         <div class="text l10n">Render</div>
                     </div>
+                    <div class="item" data-tab="image">
+                        <div class="image"><i class="fa fa-picture-o" aria-hidden="true"></i></div>
+                        <div class="text l10n">Image</div>
+                    </div>
                 </div>
                 <div class="content">
                     <div class="panel" data-tab="general" style="display: block; ">
@@ -282,6 +286,55 @@
                             </tr>
                         </table>
                     </div>
+                    <div class="panel" data-tab="image" style="display: block; ">
+                        <table width="80%" align="center">
+                            <tr>
+                                <td width="40%" class="l10n">Source Center</td>
+                                <td width="60%">
+                                    <input class="settings-item-image" disabled="disabled" id="image-source-center" type="url" style="width: calc(100% - 25px);"/>
+                                    <div style="display: inline-block; margin-left: -1px; position: absolute; " id="image-source-center-btn">
+                                    <button class="button-add-remove button-add" ><i class="fa fa-folder-o" aria-hidden="true"></i></button>
+
+                                </div>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td width="40%" class="l10n">Web Type</td>
+                                <td width="60%">
+                                    <select class="settings-item-image" id="image-web-type">
+                                        <option value="sm" class="l10n">WebSM</option>
+                                        <option value="qiniu" class="l10n">QiNiu</option>
+                                    </select>
+                                </td>
+                            </tr>
+                            <tr data-type="image-qiniu" style="display:none">
+                                <td width="40%" class="l10n">AccessKey</td>
+                                <td width="60%">
+                                    <input class="settings-item-image" type="password" id="image-qiniu-accessKey"/>
+                                </td>
+                            </tr>
+                            <tr data-type="image-qiniu" style="display:none">
+                                <td width="40%" class="l10n">SecretKey</td>
+                                <td width="60%">
+                                    <input class="settings-item-image" type="password" id="image-qiniu-secretKey"/>
+                                </td>
+                            </tr>
+                            <tr data-type="image-qiniu" style="display:none">
+                                <td width="40%" class="l10n">Bucket</td>
+                                <td width="60%">
+                                    <select class="settings-item-image" id="image-qiniu-bucket">
+                                    </select>
+                                </td>
+                            </tr>
+                            <tr data-type="image-qiniu" style="display:none">
+                                <td width="40%" class="l10n">BaseWeb</td>
+                                <td width="60%">
+                                    <select class="settings-item-image" id="image-qiniu-url">
+                                    </select>
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
                 </div>
             </div>
         </div>