diff --git a/README.md b/README.md index 83becde..b732ff2 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,8 @@ Last, you can set the language for Google and DeepL translation the label of the These global settings apply to the plugin as a whole. ![Opening settings](https://raw.githubusercontent.com/frianasoa/Ze-Notes/main/docs/images/07.zenotes-load-save-display-performance.png "Other settings") +Font settings was added. You can use this to zoom in and out of your table with the shortcut "Ctrl + Mouse wheel" or "Ctrl + Plus/Minus" + ## Use cases ### How to start a systematic review of the literature? diff --git a/content/notes/io.js b/content/notes/io.js index d6144b5..a17b571 100644 --- a/content/notes/io.js +++ b/content/notes/io.js @@ -4,6 +4,7 @@ Io = { var collection = Zotero.getActiveZoteroPane().getSelectedCollection(); this.turndownService = new TurndownService(); this.turndownPluginGfm = TurndownPluginGfmService; + this.initmd(); this.currentCollection = "All documents"; if(collection) { @@ -11,13 +12,270 @@ Io = { } }, + export(){ + var nsIFilePicker = Components.interfaces.nsIFilePicker; + var fp =Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); + fp.defaultString = "ZeNotes - "+Io.currentCollection+"."+Zotero.ZeNotes.Prefs.get("export-default-ext", "html"); + var filters = { + "Documents (*.doc)": "*.doc", + "Web page, HTML single file (*.html; *.htm)": "*.html; *.htm", + "Web page, complete (*.html; *.htm)": "*.html; *.htm", + "Markdown, MD single file (*.md; *.MD)": "*.md; *.MD", + "Markdown, complete (*.md; *.MD)": "*.md; *.MD", + "Markdown, MD with HTML (*.md; *.MD)": "*.md; *.MD", + "Markdown, complete, with HTML (*.md; *.MD)": "*.md; *.MD", + "Documents (*.xls)": "*.xls", + "CSV (*.csv)": "*.csv", + } + + for(let idx in filters) + { + fp.appendFilter(idx, filters[idx]); + } + + fp.defaultExtension=Zotero.ZeNotes.Prefs.get("export-default-ext", "html"); + fp.filterIndex = Zotero.ZeNotes.Prefs.get("export-default-filter-index", 0); + fp.init(window, "Save to file", nsIFilePicker.modeSave); + fp.open(result=>{ + if (result == fp.returnOK || result == fp.returnReplace) { + let ext = filters[Object.keys(filters)[fp.filterIndex]].split(";")[0].replace("*.", ""); + var mode1 = ""; + var mode2 = ""; + let mode = Object.keys(filters)[fp.filterIndex].replace(/\(.*\)/, "").split(/[\,\(]/); + + if(mode.length==2 && mode[1]) + { + mode1 = mode[1].trim(); + } + else if(mode.length==3 && mode[2]) + { + mode1 = mode[1].trim(); + mode2 = mode[2].trim(); + } + + Zotero.ZeNotes.Prefs.set("export-default-ext", ext); + Zotero.ZeNotes.Prefs.set("export-default-filter-index", fp.filterIndex); + var table = document.getElementById("notes-table"); + + + if(mode1=="complete" || ext.toUpperCase()=="XLS" || ext.toUpperCase()=="CSV") + { + table = this.createfiles(fp, table, ext); + } + + if(fp.file.exists()) + { + fp.file.remove(true); + } + + if(ext.toUpperCase()=="MD") + { + let markdown = ""; + if(mode1.includes("HTML") || mode2.includes("HTML")) + { + markdown = Io.turndownServiceFull.turndown(table.outerHTML).replace(/https\:\/\/zotero\//g, "zotero://"); + } + else + { + markdown = Io.turndownServiceMin.turndown(table.outerHTML).replace(/https\:\/\/zotero\//g, "zotero://"); + } + Zotero.File.putContentsAsync(fp.file, markdown); + } + else if(ext.toUpperCase()=="HTML") + { + Zotero.File.putContentsAsync(fp.file, table.outerHTML); + } + else if(ext.toUpperCase()=="DOC") + { + var html = table.outerHTML; + var style = ""; + + html = ""+style+""+html+"" + html = html.replace(/(background-color:#.{6})(.{2});\"/g, "$1;\""); + Zotero.File.putContentsAsync(fp.file, html); + } + else if(ext.toUpperCase()=="XLS") + { + var template = '{table}
'; + var base64 = function(s) { + return window.btoa(unescape(encodeURIComponent(s))) + } + var format = function(s, c) { + return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) + } + var ctx = {worksheet:"data" || 'Worksheet', table:table.outerHTML}; + xls = format(template, ctx); + Zotero.File.putContentsAsync(fp.file, xls); + } + else if(ext.toUpperCase()=="CSV") + { + var rows = table.querySelectorAll('tr'); + var csv = []; + var separator = ","; + for (var i = 0; i < rows.length; i++) { + var row = [], cols = rows[i].querySelectorAll('td, th'); + for (var j = 0; j < cols.length; j++) { + var data = cols[j].innerText.replace(/(\r\n|\n|\r)/gm, '').replace(/(\s\s)/gm, ' ') + data = data.replace(/"/g, '""'); + row.push('"' + data + '"'); + } + csv.push(row.join(separator)); + } + var csv_string = csv.join('\n'); + Zotero.File.putContentsAsync(fp.file, csv_string); + } + } + }) + }, + + createfiles(fp, table, fext="MD") + { + var folder = fp.file.parent.clone(); + var filename = fp.file.leafName; + var foldername = filename.substring(0, filename.lastIndexOf('.'))+"_files"; + folder.append(foldername); + if(!folder.exists()) + { + folder.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + } + + var i = 1; + table = table.cloneNode(true); + table.querySelectorAll("img").forEach(img=>{ + let fileparts = img.src.split(','); + let ext = fileparts[0].replace("data:image/", "").replace(";base64", ""); + let base64 = fileparts[1]; + let filename = foldername+"_"+String(i).padStart(3, '0')+"."+ext; + let src = foldername+"/"+filename; + let imagefile = folder.clone(); + imagefile.append(filename); + this.savebinary(imagefile, img.src); + img.src = src.split(" ").join("%20"); + + if(fext.toUpperCase()=="CSV") + { + let node = document.createTextNode(", image: "+src.split(" ").join("%20")+", "); + img.parentNode.insertBefore(node, img); + } + }); + + if(fext.toUpperCase()=="HTML") + { + var stylefile = folder.clone(); + stylefile.append("styles.css"); + this.savebinary(stylefile, "chrome://ze-notes/content/notes/notes.css"); + + var html = document.createElement("html"); + var head = document.createElement("head"); + var title = document.createElement("title"); + var body = document.createElement("body"); + body.style = "width:100%;overflow:auto!important;"; + + var style = document.createElement("link"); + style.href = foldername+"/styles.css"; + style.rel = "stylesheet"; + title.innerText = foldername; + html.appendChild(head); + html.appendChild(body); + head.appendChild(title); + head.appendChild(style); + body.appendChild(table); + return html; + } + return table; + }, + + initmd() + { + Io.turndownServiceFull = new TurndownService(); + Io.turndownPluginGfm.tables(Io.turndownServiceFull); + + Io.turndownServiceFull.keep(['div', 'hr', 'br', 'a', 'img']); + Io.turndownServiceFull.addRule('divs', { + filter: ['div'], + replacement: function (content, node) { + if(content.replace(/\s/g, '')){ + Zotero.warn(node.outerHTML); + if(node.style.cssText) + { + return "
"+content+"
"; + } + else + { + return "
"+content+"
"; + } + } + else { + return ""; + } + } + }); + + Io.turndownServiceFull.addRule('spans', { + filter: ['span'], + replacement: function (content, node) { + var td = node.closest("td"); + var annotation = node.closest(".annotation"); + if(node.className=="quotation-source") + { + var url = "https://zotero/open-pdf/library/items/"+annotation.dataset.attachmentkey+"?annotation="+annotation.dataset.annotationkey; + return "["+node.innerText+"]("+url+")"; + } + else + { + return node.innerText; + } + } + }); + + Io.turndownServiceFull.addRule('hrs', { + filter: ['hr'], + replacement: function (content, node) { + return "---"; + } + }); + + Io.turndownServiceMin = new TurndownService(); + Io.turndownPluginGfm.tables(Io.turndownServiceMin); + + Io.turndownServiceMin.keep(['br']); + Io.turndownServiceMin.addRule('links', { + filter: ['a'], + replacement: function (content, node) { + return "["+content+"]("+node.href+")"; + } + }); + + Io.turndownServiceMin.addRule('divs', { + filter: ['div'], + replacement: function (content, node) { + if(content.replace(/\s/g, '').replace(/ /g , "")) { + return "
"+content+"
"; + } + else + { + return "" + } + } + }); + + Io.turndownServiceMin.addRule('hrs', { + filter: ['hr'], + replacement: function (content, node) { + return "---"; + } + }); + }, + download(type, options=[]) { - if(type=="doc" || type=="html") + if(type=="doc" || type=="html" || type=="fullhtml") { - var data = document.getElementById("notes-table").outerHTML; - this.exporthtml(data, type); + var table = document.getElementById("notes-table"); + this.exporthtml(table, type); } else if(type=="csv") { @@ -36,8 +294,106 @@ Io = { } }, - exporthtml(html, type) + savebinary(file, url) + { + fetch(url) + .then(res => res.blob()) + .then(blob=>{ + Zotero.File.putContentsAsync(file, blob); + }); + }, + + fullexport(table, filter, extension, formatter) + { + var nsIFilePicker = Components.interfaces.nsIFilePicker; + var fp =Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); + fp.defaultString = "ZeNotes - "+Io.currentCollection+"."+extension; + fp.appendFilter(...filter); + fp.defaultExtension=extension; + fp.init(window, "Save to file", nsIFilePicker.modeSave); + fp.open(result=>{ + if (result == fp.returnOK || result == fp.returnReplace) { + var folder = fp.file.parent.clone(); + var filename = fp.file.leafName; + var foldername = filename.substring(0, filename.lastIndexOf('.'))+"_files"; + folder.append(foldername); + if(!folder.exists()) + { + folder.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + } + + var i = 1; + table = table.cloneNode(true); + + table.querySelectorAll("img").forEach(img=>{ + let fileparts = img.src.split(','); + let ext = fileparts[0].replace("data:image/", "").replace(";base64", ""); + let base64 = fileparts[1]; + let filename = foldername+"_"+String(i).padStart(3, '0')+"."+ext; + let src = foldername+"/"+filename; + let imagefile = folder.clone(); + imagefile.append(filename); + this.savebinary(imagefile, img.src); + img.src = src.split(" ").join("%20"); + }); + + if(fp.file.exists()) + { + fp.file.remove(true); + } + Zotero.File.putContentsAsync(fp.file, formatter(table)); + } + }) + }, + + formathtml(table) + { + return table.outerHTML; + }, + + formatmd(table, options) + { + if(options.includes("html")) + { + let markdown = Io.turndownServiceFull.turndown(table.outerHTML).replace(/https\:\/\/zotero\//g, "zotero://"); + return markdown; + } + else + { + let markdown = Io.turndownServiceMin.turndown(this.cleantable(table, options)).replace(/https\:\/\/zotero\//g, "zotero://"); + return markdown; + } + }, + + fullmarkdown(table, options) + { + this.fullexport( + table, + ["Markdown (*.md; *.MD)", "*.md; *.MD"], + "MD", + function(t){return Io.formatmd(t, options)} + ) + }, + + fullhtml(table) + { + this.fullexport( + table, + ["Web page (*.html; *.htm)", "*.html; *.htm"], + "html", + this.formathtml + ) + }, + + exporthtml(table, type) { + if(type=="fullhtml") + { + table = Io.fullhtml(table); + return; + } + + html = table.outerHTML; var style = ""; @@ -65,6 +421,7 @@ Io = { html = html.replace(/(background-color:#.{6})(.{2});\"/g, "$1;\""); } + else if(type=="html") { fp.defaultString = "ZeNotes - "+Io.currentCollection+".html"; @@ -137,11 +494,11 @@ Io = { markdown(table, options=[]) { - Io.turndownPluginGfm.tables(Io.turndownService); + Io.turndownPluginGfm.tables(Io.turndownService); var markdown = ""; if(options.includes("html")) { - Io.turndownService.keep(['div', 'hr', 'br', 'a']); + Io.turndownService.keep(['div', 'hr', 'br', 'a', 'img']); Io.turndownService.addRule('divs', { filter: ['div'], replacement: function (content, node) { @@ -165,13 +522,20 @@ Io = { } }); } + markdown = Io.turndownService.turndown(this.cleantable(table, options)).replace(/https\:\/\/zotero\//g, "zotero://"); - return markdown + return markdown }, exportmarkdown(table, options=[]) - { - var markdown = this.markdown(table, options) + { + if(options.includes("full")) + { + this.fullmarkdown(table, options); + return; + } + + var markdown = this.markdown(table, options) var nsIFilePicker = Components.interfaces.nsIFilePicker; var fp =Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); diff --git a/content/notes/menus.js b/content/notes/menus.js index 52cda14..c892cd4 100644 --- a/content/notes/menus.js +++ b/content/notes/menus.js @@ -187,11 +187,15 @@ Menus = { Menus.actions(key, options) }, items: { - "saveasxls": {name: "Export as xls...", icon: "fa-file-excel"}, - "saveascsv": {name: "Export as csv...", icon: "fa-file-csv"}, - "saveasdoc": {name: "Export as doc...", icon: "fa-file-word"}, - "saveashtml": {name: "Export as html...", icon: "fa-file-code"}, - "saveasmarkdown": {name: "Export as markdown", icon: "fa-markdown", items: { + "saveas": {name: "Export as...", icon: "fa-file"}, + // "saveasxls": {name: "Export as xls...", icon: "fa-file-excel"}, + // "saveascsv": {name: "Export as csv...", icon: "fa-file-csv"}, + // "saveasdoc": {name: "Export as doc...", icon: "fa-file-word"}, + // "saveashtml": {name: "Export as html...", icon: "fa-file-code"}, + // "saveasfullhtml": {name: "Export as html (complete)", icon: "fa-folder"}, + // "saveasfullmd": {name: "Export as markdown (complete)", icon: "fa-folder"}, + // "saveasfullmdhtml": {name: "Export as markdown (complete, with html)", icon: "fa-folder"}, + "saveasmarkdown": {name: "Export as markdown (legacy)", icon: "fa-markdown", items: { "saveasmarkdownnoicon": {name: "Simple", icon: "fa-markdown"}, "saveasmarkdown": {name: "With icon", icon: "fa-markdown"}, "sep": "---", @@ -456,6 +460,10 @@ Menus = { Find.show(); } + else if(key=="saveas" ){ + Io.export(); + } + else if(key=="saveasxls" ){ Io.download("xls"); } @@ -468,6 +476,18 @@ Menus = { Io.download("html"); } + else if(key=="saveasfullhtml"){ + Io.download("fullhtml"); + } + + else if(key=="saveasfullmdhtml"){ + Io.download("markdown", ["html", "style", "link-icon", "full"]); + } + + else if(key=="saveasfullmd"){ + Io.download("markdown", ["style", "link-icon", "full"]); + } + else if(key=="saveasmarkdownnoicon"){ Io.download("markdown", []); } diff --git a/content/notes/notes.js b/content/notes/notes.js index c58342d..a68628f 100644 --- a/content/notes/notes.js +++ b/content/notes/notes.js @@ -1,6 +1,7 @@ Notes = { init() { var fontsize = Zotero.ZeNotes.Prefs.get("font-size"); + this.tableutils = Zotero_Preferences.ZNTable; this.body = document.getElementById("zn-body"); this.infotags = ["id", "key", "title", "date", "journal", "author", "creators", "itemid", "filekey"]; @@ -9,6 +10,7 @@ Notes = { { this.body.style.fontSize = fontsize+"px"; } + this.fontsize = 1; }, @@ -347,11 +349,52 @@ Notes = { Zotero.ZeNotes.Prefs.set("scrolltop", $('#zn-body-wrapper').scrollTop()); Zotero.ZeNotes.Prefs.set("scrollleft", $('#zn-body-wrapper').scrollLeft()); }); + }, + + zoom(sign){ + this.fontsize = this.fontsize; + this.fontsize+=0.05*sign; + if(this.fontsize<=0) + { + this.fontsize = 1; + } + else if(this.fontsize>10) + { + this.fontsize = 10; + } + Notes.body.style.fontSize = this.fontsize+"em"; + let pxsize = parseFloat(getComputedStyle(Notes.body).fontSize); + Zotero.ZeNotes.Prefs.set("font-size", pxsize); } + } window.addEventListener("load", function(){ Notes.init(); Notes.loaddata(); Notes.initscroll(); +}) + +document.addEventListener("wheel", function(e){ + if(e.ctrlKey){ + let delta = e.deltaY; + delta = delta && delta / Math.abs(delta); + let sign = delta*-1; + Notes.zoom(sign); + } +}) + +document.addEventListener("keyup", function(e){ + if(e.ctrlKey){ + let sign = 1; + if(e.keyCode==107) + { + sign = 1; + } + else if(e.keyCode==109) + { + sign=-1; + } + Notes.zoom(sign); + } }) \ No newline at end of file diff --git a/content/notes/zotero.js b/content/notes/zotero.js index 7e876ae..0c1b00f 100644 --- a/content/notes/zotero.js +++ b/content/notes/zotero.js @@ -7,6 +7,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { E10SUtils: "resource://gre/modules/E10SUtils.jsm", Services: "resource://gre/modules/Services.jsm", setTimeout: "resource://gre/modules/Timer.jsm", + FileUtils: "resource://gre/modules/FileUtils.jsm" }); // Zotero.Utilities.Internal.openPreferences('zotero-prefpane-advanced'); diff --git a/core/format.js b/core/format.js index c1d079e..07e5194 100644 --- a/core/format.js +++ b/core/format.js @@ -236,7 +236,11 @@ Format = { comment = comment.split("\n").join("
\n"); - var contents = "‟"+annotationtext+"” ("+Format.creatorshort(item)+" "+Format.year(item)+", p. "+note["annotationPageLabel"]+")"; + // var url = "zotero://open-pdf/library/items/"+note["parentItem"].key+"?annotation="+note["key"]; + var ref = Format.creatorshort(item)+" "+Format.year(item)+", p. "+note["annotationPageLabel"]; + // ref = ""+ref+""; + + var contents = "‟"+annotationtext+"” ("+ref+")"; var img = ""; if(note.annotationType=="image") @@ -248,7 +252,7 @@ Format = { } var img = ""; - var contents = annotationtext+"
Source: "+Format.creatorshort(item)+" "+Format.year(item)+", p. "+note["annotationPageLabel"]+""; + var contents = annotationtext+"
Source: "+ref+""; } var annotationpage = JSON.parse(note["annotationPosition"])["pageIndex"]; diff --git a/install.rdf b/install.rdf index c03738f..036c8a5 100644 --- a/install.rdf +++ b/install.rdf @@ -5,7 +5,7 @@ zenotes@alefa.net ZeNotes - 0.7.8 + 0.7.9 true https://raw.githubusercontent.com/frianasoa/zenotes/main/zenote-update.json https://github.com/frianasoa/zenotes diff --git a/manifest.json b/manifest.json index 0151b26..b36d45e 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Ze Notes", - "version": "0.7.8", + "version": "0.7.9", "description": "Advanced notes manager", "homepage_url": "https://github.com/frianasoa/zenotes", "author": "Fanantenana Rianasoa Andriariniaina", diff --git a/zenote-update.json b/zenote-update.json index 1b7ce37..2a510ba 100644 --- a/zenote-update.json +++ b/zenote-update.json @@ -3,9 +3,9 @@ "zenotes@alefa.net": { "updates": [ { - "version": "0.7.8", - "update_link": "https://github.com/frianasoa/Ze-Notes/releases/download/v0.7.8/zenotes-v0.7.8.xpi", - "update_hash": "sha256:2d45d794a0b648399351af95f6990342d3c5ebb78188462a5880c8bc431215e2", + "version": "0.7.9", + "update_link": "https://github.com/frianasoa/Ze-Notes/releases/download/v0.7.9/zenotes-v0.7.9.xpi", + "update_hash": "sha256:67fda1e59dfad8c3bed4f17c9193685f57f12ae942d5159d28a69461d70ced2e", "applications": { "gecko": { "strict_min_version": "60.0" diff --git a/zenote-update.rdf b/zenote-update.rdf index b870d85..752de07 100644 --- a/zenote-update.rdf +++ b/zenote-update.rdf @@ -5,13 +5,13 @@ - 0.7.8 + 0.7.9 zotero@chnm.gmu.edu 5.0.0 6.* - https://github.com/frianasoa/Ze-Notes/releases/download/v0.7.8/zenotes-v0.7.8.xpi + https://github.com/frianasoa/Ze-Notes/releases/download/v0.7.9/zenotes-v0.7.9.xpi @@ -20,7 +20,7 @@ juris-m@juris-m.github.io 4.999 6.* - https://github.com/frianasoa/Ze-Notes/releases/download/v0.7.8/zenotes-v0.7.8.xpi + https://github.com/frianasoa/Ze-Notes/releases/download/v0.7.9/zenotes-v0.7.9.xpi