這裏文章都是從我的的github博客直接複製過來的,排版可能有點亂. 原始地址
http://benq.im
上一篇咱們搭建好了項目結構,簡單的實現了第一個模塊(studio)的基本功能,已經可以進行簡單的markdown編輯.javascript
在這篇裏咱們將實現如下功能:css
- 底部工具條UI,狀態欄信息
- 新建文件,打開文件,保存文件
工具條
因爲工具條按鈕綁定的都是studio模塊下的功能,所以我把index.html
上的工具條移動到了studio
模塊的視圖模版modules/studio/views/studio.html
裏.html
樣式
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="content studio-wrap"> <textarea name="" cols="30" rows="10" hmd-editor></textarea> </div> <footer class="tool"> <section class="msg" id="msg"></section> <section class="btn-group studio-btn-group"> <a studio-newfile href="javascript://" class="btn btn-primary" style="border-radius:0;" title="新建文件"><i class="glyphicon glyphicon glyphicon-file"></i></a> <a studio-openfile href="javascript://" class="btn btn-primary" title="打開文件" ng-click="openTerminal()"><i class="glyphicon glyphicon-folder-open"></i></a> <a studio-save href="javascript://" class="btn btn-primary" title="保存更改" style="border-radius:0;"><i class="glyphicon glyphicon-floppy-disk"></i></a> </section> </footer> |
CSS寫得比較難看,沒什麼好說的.樣式裏我大量使用了calc這個功能,這在佈局的時候很是的方便,好比:java
1 2 3 4 5 6 |
body { height: calc(100% - 50px); overflow: hidden; color: #fff; background: #1E1E1E; } |
工具條截圖

配色比較醜,一開始我是隻在意功能的,UI是個人弱項,咱們仍是先能用再好用最後纔好看吧.git
狀態欄消息
狀態欄消息這功能很簡單,用來顯示各類操做的信息.這個功能爲全局可用,所以把功能寫到app.js
裏github
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
var msgTimer = null; var MSG_LEVEL = { info: 'info', warning: 'warning', debug: 'debug', error:'error' };
hmd.msg = function (txt, lv) { lv = lv || MSG_LEVEL.info; $('#msg') .removeClass(MSG_LEVEL.info) .removeClass(MSG_LEVEL.warning) .removeClass(MSG_LEVEL.debug) .removeClass(MSG_LEVEL.error) .addClass(lv).text(txt); clearTimeout(msgTimer); msgTimer = setTimeout(function () { $('#msg') .removeClass(MSG_LEVEL.info) .removeClass(MSG_LEVEL.warning) .removeClass(MSG_LEVEL.debug) .removeClass(MSG_LEVEL.error); }, 5000); }; |
eg: 文件保存成功時顯示windows
1 2 3 4 5 6 7 8 9 |
studio.directive('hmdEditor', function () { return function ($scope, elem) { hmd.editor.init({el:elem[0]},'E:\\Temp\\test\\test.md'); hmd.editor.on('saved',function(filepath){ var fileNameArr = filepath.split('\\'); hmd.msg('文件:' + fileNameArr[fileNameArr.length - 1] + '保存成功!'); }); }; }); |
有一點要注意的事,editor都是儘可能採用事件的方式來對外提供接口,這樣可讓editor與外部的耦合度下降.markdown
文件操做
工具條的bt-group裏有三個按鈕,分別綁定了三個studio下的directive:studio-newfile
,studio-openfile
,studio-save
.
如今打開modules/studio/directives.js
文件,開始實現這3個功能.hexo
新建文件
這個功能很簡單,只要把當前文件設爲空,而且清空編輯器內容就算是新建文件了,保存的時候纔會讓用戶選擇保存路徑.app
修改editor.js
的setFile
方法
1 2 3 4 5 6 7 8 9 10 11 12 |
setFile:function(filepath){ if(filepath && fs.existsSync(filepath)){ var txt = util.readFileSync(filepath); this.filepath = filepath; this.cm.setValue(txt); } else{ this.filepath = null; this.cm.setValue(''); } } |
實現directive
,點擊按鈕時調用編輯器的setFile
讓filepath
爲空
1 2 3 4 5 6 7 |
studio.directive('studioNewfile', function () { return function ($scope, elem) { $(elem[0]).on('click',function(){ hmd.editor.setFile(); }); }; }); |
這樣新建文件按鈕就完成了,其實這按鈕的功能就是清空編輯器,真正的保存新文件功能在保存按鈕功能裏實現.
保存文件
1 |
<a studio-save ng-class="{'disabled':!editorChanged}" href="javascript://" class="btn btn-primary" title="保存更改(Ctrl+S)" style="border-radius:0;">省略..</a> |
保存按鈕只有在文本有改動時纔可用,這樣用戶就能很直觀的看到是否已保存(禁用按鈕時,按ctrl+s依然能夠保存,不少人都習慣一直按ctrl+s)
經過ng-class來實現這個功能,將classdisabled
綁定到editorChanged這個上下文變量上ng-class="{'disabled':!editorChanged}"
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
studio.directive('studioSave',function(){ return function($scope,elem){ var editor = hmd.editor; $scope.editorChanged = false; editor.on('change', function (cm, change) { $scope.editorChanged = true; $scope.$digest(); }); editor.on('saved', function () { $scope.editorChanged = false; $scope.$digest(); });
$(elem[0]).on('click',function(){ editor.save(); }); }; }); |
這樣$scope.editorChanged變化時,保存按鈕也會跟着變化,咱們不須要直接操做dom元素.
editor.js裏保存功能的實現
1 2 3 4 5 6 7 8 9 10 11 |
save : function () { var txt = this.cm.getValue(); if(this.filepath){ util.writeFileSync(this.filepath, txt); this.fire('saved',this.filepath); } else{ this.saveAs(); } } |
若是filepath不存在,那就調用saveAs方法來引導用戶保存到新建的文件裏.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
saveAs:function(){ var me = this; this.saveAsInput = $('<input style="display:none;" type="file" accept=".md" nwsaveas/>'); this.saveAsInput[0].addEventListener("change", function (evt) { if(this.value){ me.filepath = this.value; me.save(); } }, false); this.saveAsInput.trigger('click');
hmd.msg('保存新文件'); }, |
nw.js
的文件對話框比較特殊,能夠經過代碼觸發單擊事件來打開對話框,並無必定要用戶點擊的限制,做爲一個客戶端開發框架,這樣的改動是必要的.
所以咱們上面的代碼裏直接建立了一個input
標籤,隨即觸發它的單擊事件.其中nwsaveas
是指定對話框的類型爲另存爲對話框.
用戶輸入或者選擇好文件後,將filepath設置爲用戶指定的,並調用me.save()保存文件.
打開文件
將實現經常使用的三種打開文件方式:
- 經過打開文件按鈕
- 拖動文件到編輯器
- 雙擊md文件打開
經過按鈕打開
經過按鈕打開與另存爲相似,直接上代碼,不用多作解釋.
1 2 3 4 5 6 7 8 9 10 |
openFile:function(){ var me = this; this.openFileInput = $('<input style="display:none;" type="file" accept=".md"/>'); this.openFileInput[0].addEventListener("change", function (evt) { if(this.value){ me.setFile(this.value); } }, false); this.openFileInput.trigger('click'); }, |
而後是實現按鈕綁定的directive
1 |
<a studio-openfile href="javascript://" class="btn btn-primary" title="打開文件" ng-click="openTerminal()">省略..</a> |
1 2 3 4 5 6 7 |
studio.directive('studioOpenfile', function () { return function ($scope, elem) { $(elem[0]).on('click',function(){ hmd.editor.openFile(); }); }; }); |
拖動打開
到此三個按鈕的功能都已實現,可是拖動打開文件是windows上程序的基本功能,所以咱們也來實現它.
這個功能的實現放在編輯器的初始化代碼後面,由於要編輯器初始化以後才能打開文件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
studio.directive('hmdEditor', function () { return function ($scope, elem) { hmd.editor.init({el:elem[0]}); hmd.editor.on('saved',function(filepath){ var fileNameArr = filepath.split('\\'); hmd.msg('文件:' + fileNameArr[fileNameArr.length - 1] + '保存成功!'); }); document.ondrop = function (e) { var path, $target = $(e.target), dir, system; e.preventDefault(); if (!e.dataTransfer.files.length) return; path = e.dataTransfer.files[0].path; if (!fs.statSync(path).isDirectory() && ~path.indexOf('.md')) { hmd.editor.setFile(path); } }; }; }); |
添加了document.ondrop
這一段代碼,若是拖動的是一個md
文件,則打開它
雙擊md文件打開
編寫代碼以前,咱們選隨便選中一個md
文件,並設置默認用咱們的程序打開.

而後關閉咱們的程序,雙擊隨便一個md
文件試試,能夠看到雙擊後咱們的程序會啓動,可是並不會打開雙擊的文件,接下來就寫代碼實現它.
咱們把功能實現也放到editor的init以後.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
studio.directive('hmdEditor', function () { return function ($scope, elem) { hmd.editor.init({el:elem[0]}); var gui = require('nw.gui'), filepath = gui.App.argv[0]; ~filepath.indexOf('.md') && hmd.editor.setFile(filepath); gui.App.on('open', function(cmdline) { window.focus(); filepath = cmdline.split(' ')[2].replace(/\"/g,''); ~filepath.indexOf('.md') && hmd.editor.setFile(filepath); }); }; }); |
相關知識
關掉程序,再次雙擊md文件,就能夠看到打開功能正常了.在軟件已啓動的狀態雙擊文件也能夠打開該文件.
總結
今天實現了文件操做功能,這樣咱們的編輯器已經可用了.明天將實現系統設置
的功能.
附件
本篇程序打包
項目地址