/** * 导出Evernote插件 * @author life life@leanote.com * * 注意/遗留问题: * * 1. 导出的文件有可能不能导入到evernote, 即使可以导入, 也有可能不能同步 * 原因: enml.dtd * 2. 导出markdown问题, 加一个
markdown content
. 导出的markdown没有图片 * * https://dev.evernote.com/doc/articles/enml.php Before submitting HTML content over the EDAM API the client application is expected to follow the following steps: 1. Convert the document into valid XML 2. Discard all tags that are not accepted by the ENML DTD 3. Convert tags to the proper ENML equivalent (e.g. BODY becomes EN-NOTE) 4. Validate against the ENML DTD 5. Validate href and src values to be valid URLs and protocols */ define(function() { var async; // = require('async'); var enml; // = nodeRequire('./public/plugins/export_evernote/enml'); //============== // tpls var evernoteTpl = ` {title} {content} ]]> {createdTime} {updatedTime} {tags} {isMarkdown} {authorEmail} leanote.desktop 0 {resources} `; var resourceTpl = ` {base64} {type} {width} {height} 0 {createdTime} {name} `; //=========== // start var exportEvernote = { langs: { 'en-us': { 'export': 'Export Evernote', 'Exporting': 'Exporting', 'Exporting: ': 'Exporting: ', 'exportSuccess': 'Evernote saved successful!', 'exportFailure': 'Evernote saved failure!', 'notExists': 'Please sync your note to ther server firslty.' }, 'de-de': { 'export': 'Als Evernote exportieren', 'Exporting': 'Exportiere', 'Exporting: ': 'Exportiere: ', 'exportSuccess': 'Evernote erfolgreich gespeichert!', 'exportFailure': 'Evernote speichern fehlgeschlagen!', 'notExists': 'Bitte Notizen zuerst mit dem Server synchronisieren.' }, 'zh-cn': { 'export': '导出Evernote', 'Exporting': '正在导出', 'Exporting: ': '正在导出: ', 'exportSuccess': 'Evernote导出成功!', 'exportFailure': 'Evernote导出失败!' }, 'zh-hk': { 'export': '導出Evernote', 'Exporting': '正在導出', 'Exporting: ': '正在導出: ', 'exportSuccess': 'Evernote導出成功!', 'exportFailure': 'Evernote導出失敗!' } }, _inited: false, init: function() { var me = this; if (me._inited) { return; } async = require('async'); enml= nodeRequire('./public/plugins/export_evernote/enml'); me._inited = true; }, replaceAll: function(src, pattern, to) { if(!src) { return src; } while(true) { var oldSrc = src; src = src.replace(pattern, to); if(oldSrc === src) { return src; } } }, fixFilename: function(filename) { var reg = new RegExp("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\", 'g'); filename = filename.replace(reg, "-"); // 防止出现两个连续的- while(filename.indexOf('--') != -1) { filename = this.replaceAll(filename, '--', '-'); } if (filename.length > 1) { // 最后一个- filename = filename.replace(/\-$/, ''); } return filename; }, getEvernoteTime: function(t) { // 20151026T033928Z // 2015 10 26 T 03 39 28 Z if (!t) { t = new Date(); } if (typeof t != 'object' || !('getTime' in t)) { try { t = new Date(t); } catch(e) { t = new Date(); } } return t.format("yyyyMMddThhmmssZ"); }, renderTpl: function (tpl, info, keys) { for(var i = 0; i < keys.length; ++i) { tpl = tpl.replace('{' + keys[i] + '}', info[keys[i]]); } return tpl; }, render: function(note, callback) { var me = this; var keys = ['title', 'content', 'createdTime', 'updatedTime', 'tags', 'isMarkdown', 'exportedTime', 'appVersion', 'authorEmail', 'platform', 'resources']; var tpl = evernoteTpl; var appVersion = Api.getCurVersion() || {version: 'unknown'}; var info = { title: note.Title, content: note.Content, createdTime: me.getEvernoteTime(note.createdTime), updatedTime: me.getEvernoteTime(note.updatedTime), exportedTime: me.getEvernoteTime(), authorEmail: Api.userService.email || Api.userService.username, platform: process.platform, appVersion: appVersion.version, isMarkdown: note.IsMarkdown ? 'true' : 'false', tags: me.renderTags(note.Tags) }; me.fixResources(note.Content, function (content, resources) { content = $('
' + content + '
').html(); content = content.replace(//g, '
'); content = content.replace(//g, '
'); info.resources = resources; enml.ENMLOfHTML(content, function(err, ENML) { if (err) { info.content = content; } else { info.content = ENML; } if (note.IsMarkdown) { info.content = '
' + info.content + '
'; } callback(me.renderTpl(tpl, info, keys)); }); }); }, renderTags: function (tags) { if (!tags || tags.length === 0) { return '' } var str = ''; for (var i = 0; i < tags.length; ++i) { if (tags[i]) { str += '' + tags[i] + ''; } } return str; }, // 得到resource renderResource: function (fileInfo) { var me = this; var keys = ['name', 'width', 'height', 'type', 'createdTime', 'base64']; var tpl = resourceTpl; fileInfo.width = ''; fileInfo.height = ''; fileInfo.createdTime = me.getEvernoteTime(fileInfo.createdTime); return me.renderTpl(tpl, fileInfo, keys); }, findAllImages: function (content) { var reg = new RegExp(']*?src=["\']?' + Api.evtService.getImageLocalUrlPrefix() + '\\?fileId=([0-9a-zA-Z]{24})["\']?.*?>', 'g'); var matches = reg.exec(content); // width="330" height="330", style="width:200px" var widthReg = /width(=|:)[ "']*?([0-9]+)/; var heightReg = /height(=|:)[ "']*?([0-9]+)/; // 先找到所有的 var allMatchs = []; while(matches) { var all = matches[0]; var fileId = matches[1]; // 得到width, height var widthRet = all.match(widthReg); var heightRet = all.match(heightReg); var width = ''; var height = ''; if (widthRet) { width = widthRet[2]; } if (heightRet) { height = heightRet[2]; } allMatchs.push({ width: width, height: height, fileId: fileId, all: all }); // 下一个 matches = reg.exec(content); } return allMatchs; }, findAllAttachs: function (content) { var reg = new RegExp(']*?href=["\']?' + Api.evtService.getAttachLocalUrlPrefix() + '\\?fileId=([0-9a-zA-Z]{24})["\']?.*?>([^<]*)', 'g'); var matches = reg.exec(content); // 先找到所有的 var allMatchs = []; while(matches) { var all = matches[0]; var fileId = matches[1]; var title = matches[2]; allMatchs.push({ fileId: fileId, title: title, isAttach: true, all: all }); // 下一个 matches = reg.exec(content); } return allMatchs; }, fixResources: function (content, callback) { var me = this; var allImages = me.findAllImages(content) || []; var allAttachs = me.findAllAttachs(content) || []; var allMatchs = allImages.concat(allAttachs); // console.log(allMatchs); if (allMatchs.length == 0) { callback(content, ''); return; } var resources = ''; var fileIdFixed = {}; var fileInfos = {}; // fileId => var fileRendered = {}; function replaceContent () { for (var i = 0; i < allMatchs.length; ++i) { var eachMatch = allMatchs[i]; var fileInfo = fileInfos[eachMatch.fileId]; if (!fileInfo) { continue; } var media = ' 1) { pathInfo.nameNotExt = pathInfo.nameNotExtRaw + '-' + n; } var absPath = pathInfo.getFullPath(); // Api.nodeFs.existsSync(absPath) 总是返回false, 不知道什么原因 // 在控制台上是可以的 Api.nodeFs.exists(absPath, function(exists) { if(!exists) { cb(absPath); } else { me.getExportedFilePath(pathInfo, n+1, cb); } }); }, getTargetPath: function(callback) { // showSaveDialog 不支持property选择文件夹 var po = Api.gui.dialog.showOpenDialog(Api.gui.getCurrentWindow(), { defaultPath: Api.gui.app.getPath('userDesktop') + '/', properties: ['openDirectory'] }, function(targetPath) { callback(targetPath); } ); if(typeof(po) != "object"){ return; } po.then(function(re){ if(re.canceled !== false || re.filePaths.length < 1){ return; } callback(re.filePaths[0]); }, function(err){ alert(err); } ); }, loadingIsClosed: false, exportEvernoteForNotebook: function (notebookId) { var me = this; if (!notebookId) { return; } me.getTargetPath(function(targetPath) { if (!targetPath) { return; } me.loadingIsClosed = false; Api.loading.show(Api.getMsg('plugin.export_evernote.Exporting'), { hasProgress: true, isLarge: true, onClose: function () { me.loadingIsClosed = true; setTimeout(function() { me.hideLoading(); }); }}); Api.loading.setProgress(1); Api.noteService.getNotes(notebookId, function(notes) { if (!notes) { me.hideLoading(); return; } var total = notes.length; var i = 0; async.eachSeries(notes, function(note, cb) { if (me.loadingIsClosed) { cb(); me.hideLoading(); return; } i++; Api.loading.setProgress(100 * i / total); me._exportEvernote(note, targetPath, function() { cb(); }, i, total); }, function() { me.hideLoading(); Notify.show({title: 'Info', body: getMsg('plugin.export_evernote.exportSuccess')}); }); }); }); }, hideLoading: function () { setTimeout(function () { Api.loading.hide(); }, 1000); }, exportEvernote: function (noteIds) { var me = this; if (!noteIds || noteIds.length == 0) { return; } me.getTargetPath(function(targetPath) { if (!targetPath) { return; } me.loadingIsClosed = false; Api.loading.show(Api.getMsg('plugin.export_evernote.Exporting'), { hasProgress: true, isLarge: true, onClose: function () { me.loadingIsClosed = true; setTimeout(function() { me.hideLoading(); }); }}); Api.loading.setProgress(1); var i = 0; var total = noteIds.length; async.eachSeries(noteIds, function(noteId, cb) { if (me.loadingIsClosed) { cb(); return; } i++; Api.loading.setProgress(100 * i / total); Api.noteService.getNote(noteId, function(note) { me._exportEvernote(note, targetPath, function() { cb(); }, i, total); }); }, function () { me.hideLoading(); Notify.show({title: 'Info', body: getMsg('plugin.export_evernote.exportSuccess')}); }); }); }, _exportEvernote: function(note, path, callback, i, total) { var me = this; if(!note) { return; } if (me.loadingIsClosed) { callback(); return; } setTimeout(function () { Api.loading.setMsg(Api.getMsg('plugin.export_evernote.Exporting: ') + (note.Title || getMsg('Untitled'))); Api.loading.setProgressRate(i + '/' + total); }, 100); var name = note.Title ? note.Title + '.enex' : getMsg('Untitled') + '.enex'; name = me.fixFilename(name); var targetPath = path + Api.commonService.getPathSep() + name; // 将路径和名字区分开 var pathInfo = Api.commonService.splitFile(targetPath); pathInfo.nameNotExt = me.fixFilename(pathInfo.nameNotExt); // 重新修正一次 var nameNotExt = pathInfo.nameNotExt; pathInfo.nameNotExtRaw = pathInfo.nameNotExt; // 得到可用文件的绝对路径 me.getExportedFilePath(pathInfo, 1, function(absEvernoteFilePath) { me.render(note, function (content) { Api.commonService.writeFile(absEvernoteFilePath, content); callback(); }); }); }, // 打开前要执行的 onOpen: function() { var me = this; var gui = Api.gui; var menu = { label: Api.getMsg('plugin.export_evernote.export'), enabled: function(noteIds) { return true; }, click: (function() { return function(noteIds) { me.init(); me.exportEvernote(noteIds); } })() }; Api.addExportMenu(menu); Api.addExportMenuForNotebook({ label: Api.getMsg('plugin.export_evernote.export'), enabled: function(notebookId) { return true; }, click: (function() { return function(notebookId) { me.init(); me.exportEvernoteForNotebook(notebookId); } })() }); }, // 打开后 onOpenAfter: function() { }, // 关闭时需要运行的 onClose: function() { } }; return exportEvernote; });