├── yawas_off_128.png ├── yawas_on_128.png ├── yawas_error_128.png ├── .github └── FUNDING.yml ├── README.md ├── LICENSE ├── localedit.js ├── localedit.html ├── manifest.json ├── options.html ├── localsearch.html ├── options.js ├── localsearch.js ├── yawas-content-script.js └── yawas-background.js /yawas_off_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldenoue/yawas/HEAD/yawas_off_128.png -------------------------------------------------------------------------------- /yawas_on_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldenoue/yawas/HEAD/yawas_on_128.png -------------------------------------------------------------------------------- /yawas_error_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldenoue/yawas/HEAD/yawas_error_128.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ldenoue] 4 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=R9JRASMAABUUE&item_name=Yawas+Web+and+PDF+Highlighter¤cy_code=USD&source=appblit'] 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yawas 2 | 3 | Yawas (Yet Another Web Annotation System) is a browser extension to highlight web pages and PDF documents. 4 | It works both for Chrome and Firefox. 5 | 6 | As of August 2021, your annotations are stored in the built-in Chrome (or Firebox) Bookmarks. 7 | Since the chrome.bookmarks API only allows storing a URL and a title fields, Yawas stores your highlights inside the title field. 8 | To keep things clean, a new Yawas folder is created at the root of your Chrome Bookmarks. 9 | 10 | There is also a new option allowing you to import your online Google Bookmarks into Chrome Bookmarks: click on the Yawas icon in your toolbar to show that option. 11 | 12 | You can clone this repository and install the extension locally. 13 | 14 | You can also download it directly for [Chrome](https://chrome.google.com/webstore/detail/yawas-web-and-pdf-highlig/kjlghdmljfgngjdpeaiogebkiilpiimk?hl=en) or [Firefox](https://addons.mozilla.org/en-US/firefox/addon/yawas-web-and-pdf-highlighter/) 15 | 16 | # Contact me 17 | 18 | Ask me questions on [twitter](https://twitter.com/ldenoue) 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-present Yawas Contributors 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /localedit.js: -------------------------------------------------------------------------------- 1 | let r = new URLSearchParams(window.location.search); 2 | let docurl = r.get('url'); 3 | 4 | let item = null; 5 | let error = false 6 | let saveButton = document.getElementById('save') 7 | chrome.bookmarks.search({url:docurl},res => { 8 | if (res && res[0]) { 9 | item = res[0] 10 | let chunks = item.title.split('#__#') 11 | title.value = chunks[0] 12 | annotations.value = chunks[1] 13 | 14 | saveButton.addEventListener('click',(evt) => { 15 | evt.preventDefault() 16 | evt.stopPropagation() 17 | let newtitle = title.value.trim() + '#__#' + annotations.value.trim() 18 | chrome.bookmarks.update(item.id,{title:newtitle}, res => { 19 | window.close(); 20 | }); 21 | }) 22 | 23 | } else { 24 | error = true 25 | document.getElementById('error').textContent = "No bookmark for this page" 26 | saveButton.textContent = 'close' 27 | title.disabled = true 28 | annotations.disabled = true 29 | saveButton.addEventListener('click',(evt) => { 30 | evt.preventDefault() 31 | evt.stopPropagation() 32 | window.close(); 33 | }) 34 | 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /localedit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Yawas Edit 5 | 56 | 57 | 58 |
59 |
60 |

61 | 62 |

63 | 64 |
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Yawas", 3 | "author": "Laurent Denoue", 4 | "manifest_version": 3, 5 | "version": "7.4.1", 6 | "description": "Highlight Web pages; stored in your browser bookmarks, searchable, and recreated automatically when you revisit a page", 7 | "background": {"service_worker": "yawas-background.js"}, 8 | "content_scripts": [ 9 | { 10 | "matches": ["http://*/*", "https://*/*"], 11 | "exclude_matches": ["*://*.slack.com/*","*://web.whatsapp.com/*","*://www.google.com/bookmarks/*","*://accounts.google.com/*"], 12 | "js": ["yawas-content-script.js"], 13 | "all_frames": true 14 | } 15 | ], 16 | "web_accessible_resources": [{ 17 | "resources": ["localsearch.html","localedit.html"], 18 | "matches": [""] 19 | }], 20 | "permissions": ["activeTab","storage","contextMenus","tabs","bookmarks"], 21 | "action": { 22 | "default_title": "Yawas", 23 | "default_icon": "yawas_on_128.png", 24 | "default_popup": "options.html" 25 | }, 26 | "icons": { 27 | "128": "yawas_on_128.png" 28 | }, 29 | "commands": { 30 | "yawas-yellow": { 31 | "suggested_key": { 32 | "default": "Alt+Y", 33 | "mac": "Alt+Y" 34 | }, 35 | "description": "Highlight Yellow" 36 | }, 37 | "yawas-red": { 38 | "suggested_key": { 39 | "default": "Alt+R", 40 | "mac": "Alt+R" 41 | }, 42 | "description": "Highlight Red" 43 | }, 44 | "yawas-blue": { 45 | "suggested_key": { 46 | "default": "Alt+B", 47 | "mac": "Alt+B" 48 | }, 49 | "description": "Highlight Blue" 50 | }, 51 | "yawas-green": { 52 | "suggested_key": { 53 | "default": "Alt+G", 54 | "mac": "Alt+G" 55 | }, 56 | "description": "Highlight Green" 57 | }, 58 | "yawas-delete": { 59 | "description": "Delete Highlight/Comment" 60 | }, 61 | "yawas-comment": { 62 | "description": "Add Highlight/Comment" 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Yawas options 5 | 54 | 55 | 56 |

Yawas settings

57 | 59 | 60 | 63 | 64 | 65 | 66 |

Search

67 | Search your yawas highlights 68 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /localsearch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Yawas Search 5 | 87 | 88 | 89 |
Yawas Search
90 |
91 | 92 |
93 |
loading...
94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /options.js: -------------------------------------------------------------------------------- 1 | /*function loadOptions() { 2 | chrome.storage.sync.get({ 3 | saveLocally: false, 4 | saveChromeBookmarks: true 5 | }, function(items) { 6 | //document.getElementById('handlePDF').checked = items.handlePDF; 7 | document.getElementById('saveLocally').checked = items.saveLocally; 8 | document.getElementById('saveChromeBookmarks').checked = items.saveChromeBookmarks; 9 | }); 10 | } 11 | 12 | function saveOptions() { 13 | var saveLocally = document.getElementById("saveLocally").checked; 14 | var saveChromeBookmarks = document.getElementById("saveChromeBookmarks").checked; 15 | chrome.storage.sync.set({ 16 | saveLocally:saveLocally, 17 | saveChromeBookmarks:saveChromeBookmarks 18 | }, function() { 19 | //window.close(); 20 | }); 21 | } 22 | 23 | function restoreOptions() { 24 | chrome.storage.sync.set({ 25 | saveLocally: false, 26 | saveChromeBookmarks: true 27 | }, function() { 28 | //document.getElementById("handlePDF").checked = false; 29 | document.getElementById('saveLocally').checked = false; 30 | document.getElementById('saveChromeBookmarks').checked = true; 31 | }); 32 | }*/ 33 | 34 | /*function donateOptions() { 35 | chrome.tabs.create({ url: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=R9JRASMAABUUE&item_name=Yawas+Web+and+PDF+Highlighter¤cy_code=USD&source=yawasextension' }); 36 | }*/ 37 | 38 | //document.addEventListener('DOMContentLoaded', loadOptions); 39 | //document.querySelector('#save').addEventListener('click', saveOptions); 40 | //document.querySelector('#restore').addEventListener('click', restoreOptions); 41 | //document.querySelector('#donate').addEventListener('click', donateOptions); 42 | 43 | //let handlePDFElem = document.getElementById("handlePDF"); 44 | //handlePDFElem.addEventListener('change',saveOptions); 45 | //let saveLocallyElem = document.getElementById("saveLocally"); 46 | //saveLocallyElem.addEventListener('change',saveOptions); 47 | //let saveChromeBookmarksElem = document.getElementById("saveChromeBookmarks"); 48 | //saveChromeBookmarksElem.addEventListener('change',saveOptions); 49 | 50 | /*let importButton = document.getElementById('importChromeBookmarks'); 51 | importButton.addEventListener('click', () => { 52 | importButton.textContent = 'Importing' 53 | importButton.disabled = true 54 | chrome.runtime.sendMessage({ msg: "startImportFunc" }) 55 | });*/ 56 | 57 | let searchButton = document.getElementById('searchChromeBookmarks'); 58 | searchButton.addEventListener('click', () => { 59 | chrome.tabs.create({url:chrome.runtime.getURL('localsearch.html')}) 60 | window.close() 61 | }); 62 | 63 | chrome.runtime.onMessage.addListener(async function requestCallback(request, sender, sendResponse) { 64 | if (request.msg === 'importMessage') { 65 | if (request.error) { 66 | importmessage.textContent = request.error 67 | importButton.textContent = 'Import' 68 | importButton.disabled = false 69 | } 70 | else { 71 | importmessage.textContent = `imported ${request.n} bookmarks`; 72 | request.start ? importButton.disabled = true : importButton.disabled = false 73 | } 74 | } 75 | }); 76 | 77 | -------------------------------------------------------------------------------- /localsearch.js: -------------------------------------------------------------------------------- 1 | let all = [] 2 | chrome.bookmarks.search({}, res => { 3 | for (let item of res) { 4 | if (item.url > '') { 5 | item.title = item.title.toLowerCase() 6 | item.url = item.url.toLowerCase() 7 | all.push(item) 8 | } 9 | } 10 | all.sort((a,b) => b.dateAdded - a.dateAdded) 11 | results.innerHTML = '

search your yawas bookmarks by title, url, highlights and notes (' + all.length + ' urls)

' 12 | }) 13 | 14 | const leftMark = '<<';//'“' 15 | const rightMark = '>>';//'”' 16 | const lenQuote = rightMark.length; 17 | 18 | function annotationToArray(annotations) 19 | { 20 | if (!annotations) 21 | return []; 22 | if (annotations.trim().length === 0) 23 | return []; 24 | var chuncks = annotations.split(rightMark); 25 | var highlights = []; 26 | for (var i=0;i=0) 30 | { 31 | var comment = chuncks[i].substring(0,j).trim(); 32 | var highlight = chuncks[i].substring(j+lenQuote); 33 | var k = highlight.lastIndexOf('@'); 34 | var occurence = 0; 35 | var couleur = 'yellow'; 36 | var pagenumber = null; 37 | if (k > 0) 38 | { 39 | try { 40 | var string = highlight.substring(k+1); 41 | var c = highlight.indexOf('#',k); 42 | if (c != -1) 43 | { 44 | var trail = highlight.substring(k+1,c); 45 | var chk = trail.split(','); 46 | //occurence = parseInt(highlight.substring(k+1,c)); 47 | occurence = parseInt(chk[0]); 48 | if (chk.length===2) 49 | pagenumber = parseInt(chk[1]); 50 | couleur = highlight.substring(c+1); 51 | } 52 | else 53 | { 54 | //occurence = parseInt(highlight.substring(k+1)); 55 | var trail = highlight.substring(k+1); 56 | var chk = trail.split(','); 57 | occurence = parseInt(chk[0]); 58 | if (chk.length===2) 59 | pagenumber = parseInt(chk[1]); 60 | } 61 | highlight = highlight.substring(0,k); 62 | } catch (eint) { occurence = 0; } 63 | } 64 | else 65 | { 66 | var c = highlight.lastIndexOf('#'); 67 | if (c>0) 68 | { 69 | couleur = highlight.substring(c+1); 70 | highlight = highlight.substring(0,c); 71 | } 72 | } 73 | highlight = highlight.trim();//yawas_TrimString(highlight); 74 | var obj = {p:pagenumber,selection:highlight,n:occurence,color:couleur,comment:comment}; 75 | highlights.push(obj); 76 | } 77 | } 78 | return highlights; 79 | } 80 | 81 | function domain(url) { 82 | return new URL(url).hostname 83 | } 84 | 85 | function bold(text,query) { 86 | if (query > '') 87 | return text.replaceAll(query,'' + query + '') 88 | else 89 | return text; 90 | } 91 | function search(q) { 92 | let res = [] 93 | for (let item of all) { 94 | if (item.url.indexOf(q) !== -1 || item.title.indexOf(q) !== -1) 95 | res.push(item) 96 | } 97 | results.innerHTML = '

' + res.length + ' results

' 98 | for (let item of res) { 99 | let hit = document.createElement('div') 100 | let chunks = item.title.split('#__#') 101 | let title = chunks[0] 102 | let date = new Date(item.dateAdded).toLocaleDateString('en-US',{year:'numeric',month:'short',day:'numeric'}) 103 | let highlights = annotationToArray(chunks[1]); 104 | let html = [] 105 | for (let h of highlights) { 106 | if (h.selection.indexOf(q) !== -1) 107 | html.push(`${bold(h.selection,q)}`) 108 | else 109 | html.push(`${h.selection}`) 110 | } 111 | html = html.join('...') 112 | hit.innerHTML = `
${domain(item.url)} - ${date}
${html}
` 113 | results.appendChild(hit) 114 | } 115 | } 116 | 117 | /*document.getElementById('close').addEventListener('click',(evt) => { 118 | evt.preventDefault() 119 | evt.stopPropagation() 120 | window.close(); 121 | })*/ 122 | 123 | document.getElementById('form').onsubmit = (evt) => { 124 | evt.preventDefault() 125 | evt.stopPropagation() 126 | if (query.value.trim() > '') 127 | search(query.value.trim().toLowerCase()); 128 | } 129 | -------------------------------------------------------------------------------- /yawas-content-script.js: -------------------------------------------------------------------------------- 1 | var signedin = false; 2 | var highlightswrapper = document.querySelector('#yawas_highlightswrapper'); 3 | var forecolor = "#000000"; 4 | var currentColor = "yellow"; 5 | var hoverColor = 'lightgray';//'pink' 6 | var hoverElement = null; 7 | var lastHighlight = null; 8 | var leftMark = '<<';//'“' 9 | var rightMark = '>>';//'”' 10 | var lenquote = rightMark.length;//2; 11 | var googleColors = []; 12 | googleColors['yellow'] = '#FFFF66'; 13 | googleColors['blue'] = '#0df'; 14 | googleColors['red'] = '#ff9999'; 15 | googleColors['green'] = '#99ff99'; 16 | googleColors['white'] = 'transparent'; 17 | var notRemapped = []; 18 | 19 | function dragElement(div) 20 | { 21 | let draggedDiv = div; 22 | let left = 0; 23 | let top = 0; 24 | draggedDiv.querySelector('#dragheader').addEventListener('mousedown',dragMouseDown); 25 | 26 | function dragMouseDown(e) 27 | { 28 | e.preventDefault(); 29 | let rect = draggedDiv.getBoundingClientRect(); 30 | left = e.clientX - rect.left; 31 | top = e.clientY - rect.top; 32 | document.addEventListener('mouseup',closeDragElement); 33 | document.addEventListener('mousemove',elementDrag); 34 | }; 35 | 36 | function elementDrag(e) 37 | { 38 | e.preventDefault(); 39 | let x = e.clientX; 40 | let y = e.clientY; 41 | draggedDiv.style.right = 'unset'; 42 | draggedDiv.style.bottom = 'unset'; 43 | let draggedWidth = draggedDiv.offsetWidth + 24; 44 | let draggedHeight = draggedDiv.offsetHeight + 8; 45 | let newleft = (x - left); 46 | let newtop = (y - top); 47 | if (newleft < 8) 48 | newleft = 8; 49 | if (newleft > window.innerWidth - draggedWidth) 50 | newleft = window.innerWidth - draggedWidth; 51 | if (newtop < 8) 52 | newtop = 8; 53 | if (newtop > window.innerHeight - draggedHeight) 54 | newtop = window.innerHeight - draggedHeight; 55 | draggedDiv.style.left = newleft + 'px'; 56 | draggedDiv.style.top = newtop + 'px'; 57 | }; 58 | function closeDragElement(e) 59 | { 60 | document.removeEventListener('mouseup',closeDragElement); 61 | document.removeEventListener('mousemove',elementDrag); 62 | } 63 | } 64 | 65 | function contentScriptRequestCallback(request, sender, sendResponse) { 66 | //console.error('contentScriptRequestCallback',request); 67 | if (request.action) 68 | { 69 | if (request.action === 'signedin') 70 | { 71 | if (!signedin) 72 | { 73 | signedin = true; // this avoids loops: we declare we're signed first 74 | if (highlightswrapper) 75 | { 76 | setHighlightCaption('Yawas ➜ Refresh to view annotations'); 77 | highlightswrapper.style.display = 'block'; 78 | } 79 | } 80 | } 81 | else if (request.action === 'yawas_next_highlight') 82 | yawas_next_highlight(); 83 | else if (request.action === 'yawas_chrome') 84 | yawas_chrome(request.color); 85 | else if (request.action === 'yawas_delete_highlight') 86 | yawas_delete_highlight(); 87 | else if (request.action === 'yawas_copytoclipboard') 88 | yawas_copytoclipboard(request.payload); 89 | } 90 | if (sendResponse) 91 | { 92 | //console.log('sending response to background page'); 93 | sendResponse({reponse:'ok'}); 94 | } 95 | return true; // important in case we need sendResponse asynchronously 96 | } 97 | 98 | function yawas_copytoclipboard(html) { 99 | 100 | if(typeof ClipboardItem === "undefined") { 101 | alert("Sorry! ClipboardItem not available in your browser.") 102 | return; 103 | } 104 | 105 | const type = "text/html"; 106 | const blob = new Blob([html], {type}); 107 | const data = [new ClipboardItem({[type]: blob})]; 108 | 109 | navigator.clipboard.write(data).then(function () { 110 | alert('Yawas highlights copied to your clipboard!') 111 | }, function (e) { 112 | alert('error copying to clipboard') 113 | }); 114 | } 115 | 116 | /*function yawas_copytoclipboard(text) { 117 | console.log('yawas_copytoclipboard',text) 118 | var copyFrom = document.createElement("textarea"); 119 | copyFrom.textContent = text; 120 | var body = document.getElementsByTagName('body')[0]; 121 | body.appendChild(copyFrom); 122 | copyFrom.select(); 123 | document.execCommand('copy'); 124 | body.removeChild(copyFrom); 125 | }*/ 126 | 127 | //chrome.runtime.onMessage.addListener(contentScriptRequestCallback); 128 | chrome.runtime.onConnect.addListener((port) => { 129 | //console.log('onconnect to port',port); 130 | port.onMessage.addListener(contentScriptRequestCallback); 131 | }); 132 | 133 | let charactersUsed = 0; 134 | function setCharactersLeft(txt) 135 | { 136 | charactersUsed = txt; 137 | if (highlightswrapper && highlightswrapper.querySelector('#charactersleft')) 138 | highlightswrapper.querySelector('#charactersleft').textContent = txt + '/2048 chars'; 139 | 140 | } 141 | function setHighlightCaption(txt) 142 | { 143 | if (highlightswrapper && highlightswrapper.querySelector('#highlightcaption')) 144 | highlightswrapper.querySelector('#highlightcaption').textContent = txt; 145 | } 146 | 147 | function showNotFound(evt) 148 | { 149 | if (highlightsnotfoundtext.style.display === 'none') 150 | { 151 | var html = []; 152 | notRemapped.forEach(highlight => html.push(highlight.selection)); 153 | highlightsnotfoundtext.innerHTML = `
${html.join('
')}
`; 154 | highlightsnotfoundtext.style.display = 'block'; 155 | } 156 | else 157 | highlightsnotfoundtext.style.display = 'none'; 158 | } 159 | 160 | function setHighlightsNotFound(array) 161 | { 162 | if (highlightswrapper && highlightswrapper.querySelector('#highlightsnotfound')) 163 | { 164 | if (array.length > 0) 165 | { 166 | highlightswrapper.querySelector('#highlightsnotfound').textContent = array.length + ' missing'; 167 | } 168 | else 169 | highlightswrapper.querySelector('#highlightsnotfound').textContent = ''; 170 | } 171 | } 172 | 173 | function updateHighlightCaption() { 174 | if (highlightswrapper) 175 | { 176 | var nbNotRemapped = notRemapped.length; 177 | var nhighlights = document.getElementsByClassName('yawas-highlight').length; 178 | if (nhighlights > 0) 179 | { 180 | highlightswrapper.style.display = 'block'; 181 | if (nhighlights > 1) 182 | { 183 | var current = currentHighlight + 1; 184 | setHighlightCaption(current + '/' + nhighlights + ' highlights'); 185 | } 186 | else 187 | { 188 | setHighlightCaption(nhighlights + ' highlight'); 189 | } 190 | setHighlightsNotFound(notRemapped); 191 | } 192 | else 193 | { 194 | highlightswrapper.style.display = 'none'; 195 | } 196 | } 197 | 198 | } 199 | 200 | function purifyURL(href) 201 | { 202 | if (href && href.indexOf('https://mail.google') === 0) 203 | return href; 204 | var url = href; 205 | var pos = url.indexOf('#'); 206 | if (pos > 0) 207 | url = url.substring(0,pos); 208 | url = url.replace(/[?&]utm_.*/,''); 209 | url = url.replace(/[?&]WT\..*/,''); 210 | url = url.replace(/[?&]ns_.*/,''); 211 | url = url.replace(/[?&]rand=.*/,''); 212 | url = url.replace(/[?&]src=.*/,''); 213 | url = url.replace(/[?&]imm_mid=.*/,''); 214 | url = url.replace(/[?&]cmp=.*/,''); 215 | url = url.replace(/[?&]ncid=.*/,''); 216 | url = url.replace(/[?&]cps=.*/,''); 217 | url = url.replace(/[?&]mc_cid=.*/,''); 218 | url = url.replace(/[?&]mc_eid=.*/,''); 219 | url = url.replace(/[?&]mbid=.*/,''); 220 | if (url.indexOf('nytimes.com')!=-1) 221 | url = url.split('?')[0]; 222 | //console.log('purifyURL=',href,'=',url); 223 | return url; 224 | } 225 | 226 | function yawas_getGoodUrl(doc)//,url) 227 | { 228 | var url = null; 229 | if (doc && doc.location && doc.location.hostname.indexOf('readability.com') !== -1) 230 | { 231 | var origin = doc.querySelector('.entry-origin a'); 232 | if (origin) 233 | url = origin.href; 234 | } 235 | if (!url) 236 | url = doc.location.href; 237 | 238 | if (url.indexOf("q=cache:") > 0) 239 | { 240 | try { 241 | return purifyURL(doc.getElementsByTagName('base')[0].href); 242 | } catch (e) {} 243 | } 244 | else 245 | return purifyURL(url); 246 | } 247 | 248 | function needSignIn() 249 | { 250 | signedin = false; 251 | if (highlightswrapper) 252 | { 253 | setHighlightCaption('Signed out'); 254 | highlightswrapper.style.display = 'block'; 255 | } 256 | } 257 | 258 | function askForAnnotations(delay) 259 | { 260 | var url = yawas_getGoodUrl(document); 261 | var additionalInfo = { 262 | "fn": "yawas_getAnnotations", 263 | "title": document.title, 264 | "url": url 265 | }; 266 | if (!delay) 267 | delay = 0; 268 | setTimeout(function () { 269 | sendMessage(additionalInfo, function (res) { 270 | if (res && res.error) 271 | { 272 | if (res.signedout) 273 | { 274 | needSignIn(); 275 | if (res.url) 276 | { 277 | //window.open(res.url); 278 | //console.log('res.url=',res.url); 279 | } 280 | } 281 | } 282 | else if (res && res.noannotation) 283 | { 284 | signedin = true; 285 | hoverElement = null; 286 | updateHighlightCaption(); 287 | } 288 | else if (res && res.annotations) 289 | { 290 | signedin = true; 291 | hoverElement = null; 292 | setCharactersLeft(computeLength(res.annotations)); 293 | yawas_remapAnnotations(res.annotations); 294 | updateHighlightCaption(); 295 | } 296 | }); 297 | }, delay); 298 | } 299 | 300 | function yawas_signin() 301 | { 302 | var width = 400; 303 | var height = 360; 304 | var left = Math.floor( (screen.width - width) / 2); 305 | var top = Math.floor( (screen.height - height) / 2); 306 | window.open('https://www.google.com/accounts/ServiceLogin?service=toolbar&nui=1&hl=en&continue=http%3A%2F%2Ftoolbar.google.com%2Fcommand%3Fclose_browser','bkmk_popup','left='+left+',top='+top+',height='+height+'px,width='+width+'px,resizable=1'); 307 | } 308 | 309 | /*function isSavingLocally(cb) 310 | { 311 | chrome.storage.sync.get({saveLocally: false}, function(items) { 312 | if (items && items.saveLocally === true) 313 | cb(true); 314 | else 315 | cb(false); 316 | }); 317 | }*/ 318 | 319 | function yawas_undohighlight() 320 | { 321 | if (lastHighlight !== null) 322 | { 323 | var f = document.createDocumentFragment(); 324 | while(lastHighlight.firstChild) 325 | f.appendChild(lastHighlight.firstChild); 326 | lastHighlight.parentNode.replaceChild(f,lastHighlight); 327 | lastHighlight = null; 328 | } 329 | } 330 | 331 | function addHighlightsWrapper() 332 | { 333 | if (highlightswrapper === null) 334 | { 335 | highlightswrapper = document.createElement('div'); 336 | highlightswrapper.id = 'yawas_highlightswrapper'; 337 | highlightswrapper.style.userSelect = 'none'; 338 | highlightswrapper.style.display = 'none'; 339 | highlightswrapper.style.position = 'fixed'; 340 | highlightswrapper.style.zIndex = 200000; 341 | highlightswrapper.style.margin = '0px'; 342 | highlightswrapper.style.userSelect = 'none'; 343 | highlightswrapper.style.fontFamily = '"avenir next",Helvetica'; 344 | highlightswrapper.style.right = '8px'; 345 | highlightswrapper.style.bottom = '8px'; 346 | highlightswrapper.style.borderRadius = '8px'; 347 | highlightswrapper.style.boxShadow = '0 0 2px black'; 348 | highlightswrapper.style.color = 'white'; 349 | highlightswrapper.textContent = ''; 350 | highlightswrapper.style.textAlign = 'center'; 351 | //highlightswrapper.style.cursor = 'pointer'; 352 | 353 | highlightswrapper.style.fontSize = '14px'; 354 | highlightswrapper.style.fontWeight = 'bold'; 355 | highlightswrapper.style.color = 'white';//'black'; 356 | highlightswrapper.style.backgroundColor = '#190B33';//'#8a8'; 357 | //highlightswrapper.style.borderRadius = '32px'; 358 | highlightswrapper.style.padding = '8px 16px'; 359 | //highlightswrapper.textContent = ''; 360 | 361 | let dragheader = document.createElement('div'); 362 | dragheader.id = 'dragheader'; 363 | dragheader.style.textAlign = 'center'; 364 | dragheader.style.cursor = 'move'; 365 | dragheader.style.fontSize = '16px'; 366 | dragheader.style.color = '#8a8';//'#ccc'; 367 | dragheader.textContent = 'Yawas'; 368 | highlightswrapper.appendChild(dragheader); 369 | var highlightcaption = document.createElement('div'); 370 | highlightcaption.addEventListener('mousedown',(evt) => yawas_next_highlight(evt)); 371 | highlightcaption.title = 'Click to navigate in highlights'; 372 | highlightcaption.id = 'highlightcaption'; 373 | highlightcaption.style.cursor = 'pointer'; 374 | highlightcaption.style.userSelect = 'none'; 375 | highlightcaption.style.webkitUserSelect = 'none'; 376 | highlightcaption.textContent = ''; 377 | highlightswrapper.appendChild(highlightcaption); 378 | 379 | var highlightsnotfound = document.createElement('div'); 380 | highlightsnotfound.style.color = '#a22'; 381 | highlightsnotfound.style.cursor = 'pointer'; 382 | highlightsnotfound.addEventListener('click',showNotFound); 383 | highlightsnotfound.title = 'Click to show missing highlights'; 384 | highlightsnotfound.id = 'highlightsnotfound'; 385 | highlightswrapper.appendChild(highlightsnotfound); 386 | 387 | var highlightsnotfoundtext = document.createElement('div'); 388 | highlightsnotfoundtext.style.color = '#a22'; 389 | highlightsnotfoundtext.style.display = 'none'; 390 | highlightsnotfoundtext.id = 'highlightsnotfoundtext'; 391 | highlightswrapper.appendChild(highlightsnotfoundtext); 392 | 393 | /*var charactersleft = document.createElement('div'); 394 | charactersleft.style.color = '#000'; 395 | charactersleft.style.fontSize = '11px'; 396 | charactersleft.style.cursor = 'pointer'; 397 | charactersleft.title = 'Number of total characters used for this page in Google Bookmarks, out of 2048 possible'; 398 | charactersleft.addEventListener('click',function () { alert(charactersUsed + ' characters used out of 2048 allowed by Google Bookmarks for this page')},false); 399 | charactersleft.style.display = 'block'; 400 | charactersleft.id = 'charactersleft'; 401 | highlightswrapper.appendChild(charactersleft);*/ 402 | 403 | let close = document.createElement('div'); 404 | close.textContent = '✕'; 405 | close.style.position = 'absolute'; 406 | close.style.top = 0; 407 | close.style.left = 0; 408 | close.style.margin = '4px'; 409 | close.style.fontSize = '12px' 410 | close.style.padding = '4px'; 411 | close.style.color = '#ccc'; 412 | close.style.cursor = 'pointer' 413 | close.addEventListener('click',function() { highlightswrapper.style.display ='none'},false); 414 | highlightswrapper.appendChild(close); 415 | document.body.appendChild(highlightswrapper); 416 | dragElement(highlightswrapper); 417 | } 418 | } 419 | 420 | function yawas_storeHighlight(webUrl,title,highlight,occurence,couleur,addcommentwhendone) 421 | { 422 | var additionalInfo = { 423 | "fn": "addhighlight", 424 | "title": title, 425 | "url": webUrl, 426 | "selection": highlight, 427 | "occurence": occurence, 428 | "couleur": couleur 429 | }; 430 | sendMessage(additionalInfo, function (res) 431 | { 432 | if (res && res.addedhighlight) 433 | { 434 | signedin = true; 435 | setCharactersLeft(res.pureLen); 436 | updateHighlightCaption(); 437 | if (addcommentwhendone) 438 | { 439 | hoverElement = lastHighlight; 440 | recolor('note'); 441 | } 442 | } 443 | if (res && res.toobig) 444 | { 445 | yawas_undohighlight(); 446 | alert('Too many characters (>2048 even compacted)!'); 447 | } 448 | if (res && res.undohighlight || res.error) 449 | { 450 | if (res.signedout) 451 | alert('Yawas cannot store your highlight because you are signed out.\nPlease signin first and then refresh this page'); 452 | yawas_undohighlight(); 453 | } 454 | }); 455 | } 456 | 457 | function yawas_tryHighlight(wnd,addcommentwhendone) 458 | { 459 | if (!wnd) 460 | return false; 461 | var nselections = wnd.getSelection().rangeCount; 462 | if (nselections === 0) 463 | return false; 464 | var selection = wnd.getSelection().getRangeAt(0); 465 | var selectionstring = wnd.getSelection()+"";//selection.toString(); 466 | selectionstring = selectionstring.trim(); 467 | if (selectionstring.length === 0) 468 | return false; 469 | if (selectionstring.indexOf("\n") >= 0) 470 | { 471 | alert("Please select text without new lines"); 472 | return false; 473 | } 474 | var docurl = yawas_getGoodUrl(wnd.document); 475 | var occurence = -1; 476 | wnd.getSelection().removeAllRanges(); 477 | var found = false; 478 | while (!found && wnd.find(selectionstring,true,false)) 479 | { 480 | occurence += 1; 481 | var rng = wnd.getSelection().getRangeAt(0); 482 | if (selection.compareBoundaryPoints(Range.END_TO_START, rng) == -1 && selection.compareBoundaryPoints(Range.START_TO_END, rng) == 1) 483 | found = true; 484 | } 485 | if (!found) 486 | occurence = -1; 487 | if (occurence >= 0) 488 | { 489 | lastHighlight = highlightNowFirefox22(wnd.getSelection().getRangeAt(0),currentColor,forecolor,wnd.document,selectionstring,occurence); 490 | wnd.getSelection().removeAllRanges(); 491 | yawas_storeHighlight(docurl,wnd.document.title,selectionstring,occurence,currentColor,addcommentwhendone); 492 | return true; 493 | } 494 | else 495 | { 496 | alert('Sorry, [' + selectionstring + '] was not found.'); 497 | wnd.getSelection().removeAllRanges(); 498 | return false; 499 | } 500 | } 501 | 502 | function getWindowWithSelection(wnd) 503 | { 504 | //alert('hasSelection:' + wnd); 505 | if (wnd.getSelection().rangeCount>0) 506 | { 507 | //alert('found selection:' + wnd.getSelection()); 508 | return wnd; 509 | } 510 | else 511 | return null; 512 | } 513 | 514 | function recolor(color) 515 | { 516 | if (color === 'note') 517 | { 518 | let caption = hoverElement.dataset.comment!==null?hoverElement.dataset.comment:''; 519 | let newcomment = prompt('Enter note',caption); 520 | if (newcomment !== null) 521 | { 522 | newcomment = newcomment.trim(); 523 | if (newcomment.length === 0) 524 | { 525 | //hoverElement.style.borderBottom = 0; 526 | //delete hoverElement.dataset.yawasComment; 527 | delete hoverElement.dataset.comment; 528 | delete hoverElement.title; 529 | } 530 | else 531 | { 532 | //hoverElement.style.borderBottom = '1px dashed black'; 533 | //hoverElement.dataset.yawasComment = newcomment; 534 | hoverElement.dataset.comment = newcomment; 535 | hoverElement.title = newcomment; 536 | } 537 | updateHighlight(hoverElement,color,newcomment); 538 | hoverElement = null; 539 | } 540 | return; 541 | } 542 | else 543 | { 544 | hoverElement.dataset.yawasColor = googleColors[color]; 545 | hoverElement.style.backgroundColor = googleColors[color]; 546 | childrenToo(hoverElement,googleColors[color]); 547 | updateHighlight(hoverElement,color,null); 548 | // clear the selection (on Firefox we selected the text inside oncontextmenu) 549 | window.getSelection().removeAllRanges(); 550 | } 551 | } 552 | 553 | // from http://stackoverflow.com/questions/1482832/how-to-get-all-elements-that-are-highlighted/1483487#1483487 554 | function rangeIntersectsNode(range, node) { 555 | var nodeRange; 556 | if (range.intersectsNode) { 557 | return range.intersectsNode(node); 558 | } else { 559 | nodeRange = node.ownerDocument.createRange(); 560 | try { 561 | nodeRange.selectNode(node); 562 | } catch (e) { 563 | nodeRange.selectNodeContents(node); 564 | } 565 | 566 | return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 && 567 | range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1; 568 | } 569 | } 570 | 571 | function sendMessage(info,cb) 572 | { 573 | try { 574 | chrome.runtime.sendMessage(info, function response(res) { 575 | if (res && cb) 576 | cb(res); 577 | }); 578 | } catch(e) 579 | { 580 | if (cb) 581 | cb({error:e}); 582 | else 583 | console.error('sendMessage error' + e); 584 | } 585 | } 586 | 587 | function yawas_chrome(color) 588 | { 589 | if (color === 'email') 590 | { 591 | var info = { 592 | "title": document.title, 593 | "url": window.location.href, 594 | "fn": "emailhighlights", 595 | }; 596 | sendMessage(info); 597 | return; 598 | } 599 | if (color === 'copytoclipboard') 600 | { 601 | var info = { 602 | "title": document.title, 603 | "url": window.location.href, 604 | "fn": "copytoclipboard", 605 | }; 606 | sendMessage(info); 607 | return; 608 | } 609 | 610 | if (color && color !== 'note') 611 | { 612 | currentColor = color; 613 | } 614 | else 615 | { 616 | //currentColor = 'yellow'; 617 | } 618 | 619 | var elem = hoverElementOrSelection(); 620 | if (elem) 621 | { 622 | hoverElement = elem; 623 | recolor(color); 624 | } 625 | else 626 | { 627 | var wndWithSelection = getWindowWithSelection(window); 628 | if (color === 'note') 629 | yawas_tryHighlight(wndWithSelection,true); 630 | else 631 | yawas_tryHighlight(wndWithSelection); 632 | } 633 | } 634 | 635 | function yawas_remapAnnotations(highlights) 636 | { 637 | return highlightDoc(window,document,highlights); 638 | } 639 | 640 | function yawas_uncompact(wnd,highlights) 641 | { 642 | for (var i=0;i 0) 704 | previousRange = wnd.getSelection().getRangeAt(0); 705 | var scrollLeft = wnd.scrollX; 706 | var scrollTop = wnd.scrollY; 707 | nremapped = 0; 708 | notRemapped = []; 709 | yawas_uncompact(wnd,highlights); 710 | for (var i=0;i '') 753 | { 754 | baseNode.dataset.comment = comment; 755 | //baseNode.dataset.yawasComment = comment; 756 | baseNode.title = comment; 757 | //baseNode.style.borderBottom = '1px dashed black'; 758 | } 759 | baseNode.dataset.selection = selectionstring; 760 | baseNode.dataset.yawasOccurence = occurence; 761 | baseNode.dataset.yawasColor = googleColors[color]; 762 | 763 | let node = yawas_highlight222(selectionrng, baseNode, googleColors[color]); 764 | 765 | node.addEventListener('mouseover',function (e) { 766 | hoverElement = this; 767 | },false); 768 | 769 | node.addEventListener('mouseout',function (e) { 770 | hoverElement = null; 771 | },false); 772 | 773 | return node; 774 | } 775 | 776 | // on Firefox, we need to select the text before showing the context menu 777 | // on Chrome, somehow the current word is selected when the user right clicks over words 778 | window.oncontextmenu = function () { 779 | if (hoverElement !== null) 780 | { 781 | let selection = window.getSelection(); 782 | if (selection.rangeCount > 0) { 783 | selection.removeAllRanges(); 784 | } 785 | let range = document.createRange(); 786 | range.selectNode(hoverElement); 787 | selection.addRange(range); 788 | } 789 | } 790 | 791 | function childrenToo(docfrag,backgroundColor) 792 | { 793 | docfrag.childNodes.forEach(f => {if (f.style){f.style.backgroundColor = backgroundColor}}); 794 | } 795 | 796 | function yawas_highlight222(range, node,backgroundColor) 797 | { 798 | var startContainer = range.startContainer; 799 | var startOffset = range.startOffset; 800 | var endOffset = range.endOffset; 801 | var docfrag = range.extractContents(); 802 | childrenToo(docfrag,backgroundColor); 803 | var before = startContainer.splitText(startOffset); 804 | var parent = before.parentNode; 805 | node.appendChild(docfrag); 806 | parent.insertBefore(node, before); 807 | //if (whiteFont(startContainer.parentElement)) 808 | // make sure we can always read the text 809 | node.style.color = 'black' 810 | return node; 811 | } 812 | 813 | function updateHighlight(elt,color,newcomment) 814 | { 815 | if (elt) 816 | { 817 | sendMessage({action: "recolor_highlight", url: yawas_getGoodUrl(document), title: document.title, highlightString: hoverElement.dataset.selection, n:hoverElement.dataset.yawasOccurence, newcolor: color, comment:newcomment}, function (res){ 818 | if (res && res.error) 819 | { 820 | //console.error(res); 821 | yawas_undohighlight(); 822 | if (res.toobig) 823 | alert('Too many characters (>2048 even compacted)!'); 824 | } 825 | else 826 | { 827 | if (res && res.highlights) 828 | setCharactersLeft(computeLength(res.highlights)); 829 | } 830 | }); 831 | } 832 | } 833 | 834 | function hoverElementOrSelection() { 835 | if (hoverElement !== null) 836 | return hoverElement; 837 | var wndWithSelection = getWindowWithSelection(window); 838 | if (!wndWithSelection) 839 | return null; 840 | let rng = wndWithSelection.getSelection().getRangeAt(0); 841 | let elems = document.querySelectorAll('.yawas-highlight'); 842 | 843 | for (let i=0;i 0) 862 | annotation += '@' + highlights[i].n; 863 | if (highlights[i].color != 'yellow') 864 | annotation += '#' + highlights[i].color; 865 | annotation += rightMark + ' '; 866 | } 867 | return annotation.length; 868 | } 869 | 870 | function yawas_delete_highlight() { 871 | let elem = hoverElementOrSelection(); 872 | if (elem) 873 | { 874 | sendMessage({action: "delete_highlight", url: yawas_getGoodUrl(document), title: document.title, highlightString: elem.dataset.selection, n:elem.dataset.yawasOccurence }, function (res) 875 | { 876 | if (res && res.highlights) 877 | setCharactersLeft(computeLength(res.highlights)); 878 | }); 879 | childrenToo(elem,null); 880 | var f = document.createDocumentFragment(); 881 | while(elem.firstChild) 882 | f.appendChild(elem.firstChild); 883 | elem.parentNode.replaceChild(f,elem); 884 | hoverElement = null; 885 | updateHighlightCaption(); 886 | } 887 | } 888 | 889 | var currentHighlight = 0; 890 | 891 | function addStyle(doc,css) 892 | { 893 | var style = document.createElement('style'); 894 | style.innerHTML = css; 895 | doc.head.appendChild(style); 896 | } 897 | 898 | function yawas_next_highlight(evt) 899 | { 900 | // prevent text selection 901 | if (evt) { 902 | evt.preventDefault(); 903 | evt.stopPropagation(); 904 | } 905 | if (!signedin) 906 | return yawas_signin(); 907 | 908 | var highlights = document.getElementsByClassName('yawas-highlight'); 909 | if (highlights.length==0) 910 | return; 911 | currentHighlight = currentHighlight % highlights.length; 912 | updateHighlightCaption(); 913 | let h = highlights[currentHighlight]; 914 | h.style.transition = 'opacity 0.3s ease-in-out'; 915 | h.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"}); 916 | h.style.opacity = 0.2; 917 | currentHighlight += 1; 918 | setTimeout(function () { h.style.opacity=1.0; }, 300); 919 | } 920 | 921 | window.onhashchange = function (evt) { 922 | askForAnnotations(2000); 923 | }; 924 | 925 | if (document.location.hostname === 'toolbar.google.com' && document.location.pathname === '/command' && document.location.search && document.location.search.indexOf('close_browser') !== -1) 926 | { 927 | window.close(); 928 | sendMessage({fn: "yawas_toolbar_signed_in"}); 929 | } 930 | else if (document.location.href.indexOf('accounts.google.com/ServiceLogin') != -1) 931 | { 932 | //console.error('not asking getannotations for servicelogin window'); 933 | } 934 | else 935 | { 936 | //window.addEventListener("keydown", keyListener, false); 937 | if (window.top !== window) 938 | { 939 | //console.error('cookkie_handler not calling getannotations because not top window'); 940 | } 941 | else 942 | { 943 | //addScript("https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jspdf.debug.js"); 944 | addHighlightsWrapper(); 945 | addStyle(window.document,'.yawas-highlight:hover{opacity:0.6;}.yawas-highlight[data-comment]{border-bottom:1px dashed black}'); 946 | askForAnnotations(2000); 947 | } 948 | /*var elems = document.querySelectorAll('*'); 949 | for (var i=0;i { 18 | if (res.length > 0) { 19 | yawasBookmarkId = res[0].id; 20 | console.log('yawasBookmarkId=',yawasBookmarkId) 21 | } 22 | else { 23 | chrome.bookmarks.create({title:'Yawas'}, newfolder => { 24 | yawasBookmarkId = newfolder.id; 25 | console.log('yawasBookmarkId=',yawasBookmarkId) 26 | }) 27 | } 28 | }) 29 | 30 | function getFolderName(url,dateString) { 31 | let res = 'unknown' 32 | //if (dateString) 33 | //res = new Date(dateString).toLocaleDateString('en-US',{year:'numeric',month:'numeri'}) 34 | res = new Date(dateString).getFullYear() + '-' + String((new Date(dateString).getMonth()+1)).padStart(2,'0') 35 | //else { 36 | try { 37 | res = new URL(url).hostname.replace('www.','') 38 | } catch (e) { 39 | } 40 | //} 41 | return res 42 | } 43 | 44 | async function remove(id) { 45 | if (!id) 46 | return 47 | return new Promise((resolve,reject) => { 48 | chrome.bookmarks.remove(id,res => resolve(res)) 49 | }) 50 | } 51 | 52 | async function search(obj) { 53 | return new Promise((resolve,reject) => { 54 | chrome.bookmarks.search(obj,res => resolve(res)) 55 | }) 56 | } 57 | 58 | function subtree(res) { 59 | let result = [] 60 | if (res.url > '') 61 | result.push(res) 62 | if (res.children) { 63 | for (let c of res.children) { 64 | result = result.concat(subtree(c)) 65 | } 66 | } 67 | return result 68 | } 69 | 70 | async function getYawasBookmarks() { 71 | return new Promise((resolve,reject) => { 72 | console.log(yawasBookmarkId) 73 | chrome.bookmarks.getSubTree(yawasBookmarkId, res => { 74 | resolve(subtree(res[0])) 75 | }) 76 | }) 77 | } 78 | 79 | /* 80 | chrome.bookmarks.search({}, async res => { 81 | res.forEach((item, i) => { 82 | chrome.bookmarks.remove(item.id) 83 | });}) 84 | */ 85 | 86 | async function create(obj) { 87 | return new Promise((resolve,reject) => { 88 | chrome.bookmarks.create(obj,res => resolve(res)) 89 | }) 90 | } 91 | 92 | async function folderExists(name,parentId) { 93 | parentId = parentId ? parentId : yawasBookmarkId 94 | let res = await search({title:name}) 95 | return res.find(f => f.parentId === parentId) 96 | } 97 | 98 | async function createFolder(year,month) { 99 | let yearFolder = await folderExists(year) 100 | if (!yearFolder) 101 | yearFolder = await create({title:year,parentId: yawasBookmarkId}) 102 | let monthFolder = await folderExists(month,yearFolder.id) 103 | if (!monthFolder) 104 | monthFolder = await create({title:month,parentId: yearFolder.id}) 105 | return monthFolder 106 | } 107 | 108 | async function createBookmark(obj,date) { 109 | let year = ''+date.getFullYear() 110 | let month = date.toLocaleDateString('en-US',{month:'long'}) 111 | let folder = await createFolder(year,month) 112 | //console.log(year,month,folder) 113 | await create({title:obj.title,url:obj.url,parentId: folder.id}) 114 | } 115 | 116 | chrome.runtime.onMessage.addListener(requestCallback); 117 | 118 | chrome.storage.sync.get({ 119 | handlePDF: false, 120 | saveLocally: false, 121 | saveChromeBookmarks: true, 122 | }, function(items) { 123 | if (items) 124 | { 125 | handlePDF = items.handlePDF; 126 | saveLocally = items.saveLocally; 127 | saveChromeBookmarks = items.saveChromeBookmarks; 128 | } 129 | }); 130 | 131 | chrome.storage.onChanged.addListener(function(changes, namespace) { 132 | for (key in changes) 133 | { 134 | var storageChange = changes[key]; 135 | if (key === 'handlePDF') 136 | { 137 | handlePDF = storageChange.newValue; 138 | } 139 | if (key === 'saveLocally') 140 | { 141 | saveLocally = storageChange.newValue; 142 | } 143 | if (key === 'saveChromeBookmarks') 144 | { 145 | saveChromeBookmarks = storageChange.newValue; 146 | } 147 | } 148 | }); 149 | 150 | yawas_setStatusIcon("off"); 151 | 152 | var INSTAPAPER_PREFIX = "http://www.instapaper.com/text?u=" 153 | var VIEWTEXT_PREFIX = "http://viewtext.org/article?url=" 154 | var READABILITY_PREFIX = "http://www.readability.com/m?url=" 155 | 156 | function stripMobilizer(url) 157 | { 158 | var i = url.indexOf(VIEWTEXT_PREFIX); 159 | if (i == 0) 160 | return decodeURIComponent(url.substring(VIEWTEXT_PREFIX.length)); 161 | 162 | i = url.indexOf(INSTAPAPER_PREFIX); 163 | if (i == 0) 164 | return decodeURIComponent(url.substring(INSTAPAPER_PREFIX.length)); 165 | 166 | i = url.indexOf(READABILITY_PREFIX); 167 | if (i == 0) 168 | return decodeURIComponent(url.substring(READABILITY_PREFIX.length)); 169 | 170 | return url; 171 | } 172 | 173 | // dangerous deletes all your chrome.Bookmarks 174 | async function deleteYawasFolder() { 175 | let res = await search({title:'Yawas'}) 176 | for (let item of res) { 177 | await chrome.bookmarks.remove(item.id) 178 | } 179 | } 180 | 181 | /*async function moveBookmarksIntoFolders() { 182 | let res = await search({}) 183 | for (let item in res) { 184 | if (item.parentId === yawasBookmarkId) { 185 | let folder = getFolderName(item.url) 186 | let res = await search({title:folder}) 187 | if (res.length === 1) { 188 | chrome.bookmarks.move(item.id,{parentId:res.id}) 189 | } else { 190 | await createBookmark({title:item.title,url:item.url}) 191 | } 192 | chrome.bookmarks.remove(item.id) 193 | } 194 | } 195 | }*/ 196 | 197 | /*async function importAllBookmarks(callback) 198 | { 199 | //let existing = await search({}) 200 | let existing = await getYawasBookmarks() 201 | //existing = existing.filter(item => item.url > '') 202 | let existingUrls = {}; 203 | for (let item of existing) { 204 | existingUrls[item.url] = true; 205 | await remove(item.id); // will be recreated properly at the end 206 | } 207 | var start = 0; 208 | //var list = []; 209 | var loop = true; 210 | var n = 0; 211 | let max = 800; 212 | let importedUrls = new Set() 213 | 214 | // Laurent: note that output=xml returns ALL bookmarks along with tags 215 | while (loop)// && n <= max) 216 | { 217 | console.log('start=',start); 218 | let urlBookmarks = 'https://www.google.com/bookmarks/lookup?output=rss&start=' + start; 219 | let res = null 220 | try { 221 | res = await fetch(urlBookmarks); 222 | } catch (fetcherror) { 223 | return callback(n,'error retrieving online google bookmarks') 224 | } 225 | chrome.runtime.sendMessage({ msg: "importMessage", start: start, n: n }); 226 | let str = await res.text(); 227 | let xml = (new window.DOMParser()).parseFromString(str, "text/xml"); 228 | let items = xml.querySelectorAll('item'); 229 | for (let i of items) { 230 | let title = i.querySelector('title').textContent; 231 | let url = i.querySelector('link').textContent; 232 | let date = i.querySelector('pubDate')? new Date(i.querySelector('pubDate').textContent) : null; 233 | if (url.indexOf('file:///') === 0) { 234 | console.error('not importing file bookmark',url) 235 | } else if (!(url > '')) { 236 | console.error('url undefined',url) 237 | } 238 | else { 239 | importedUrls.add(url); 240 | //let labels = [...i.querySelectorAll('bkmk_label')].map(a => a.textContent).join(","); 241 | //console.log(labels) 242 | let annotations = i.querySelector('bkmk_annotation')?i.querySelector('bkmk_annotation').textContent.trim():''; 243 | n++; 244 | let newTitle = title; 245 | if (annotations > '') 246 | newTitle += '#__#' + annotations; 247 | if (existingUrls[url]) { 248 | await remove(existingUrls[url].id); 249 | } 250 | await createBookmark({title:newTitle, url:url},date) 251 | } 252 | } 253 | start += items.length; 254 | if (items.length === 0) 255 | loop = false; 256 | } 257 | // recreate the ones not imported from Google Bookmarks 258 | for (let item of existing) { 259 | if (!importedUrls.has(item.url)) 260 | { 261 | console.log('creating from existing',item.url) 262 | await createBookmark({title:item.title,url:item.url},new Date(item.dateAdded)) 263 | } 264 | } 265 | 266 | callback(n); 267 | }*/ 268 | 269 | //let getannotationscb = {}; 270 | //let storeannotationscb = {}; 271 | 272 | 273 | function isSignin(url) 274 | { 275 | let lowUrl = url.toLowerCase(); 276 | return lowUrl.indexOf('accounts.google') !== -1 && (lowUrl.indexOf('signin') !== -1 || lowUrl.indexOf('login') !== -1); 277 | } 278 | 279 | var abortTimerId = null; 280 | var requestTimeout = 1000 * 5; // 5 seconds 281 | 282 | function yawas_getElement(xml, elementname) 283 | { 284 | var e = xml.getElementsByTagName(elementname); 285 | if (e.length == 0) 286 | e = xml.getElementsByTagName('smh:' + elementname); 287 | if (e==null) 288 | console.log('yawas_getElement null for: ' + elementname); 289 | return e; 290 | } 291 | 292 | function clearAbortTimer() { 293 | if (abortTimerId != null) 294 | { 295 | window.clearTimeout(abortTimerId); 296 | abortTimerId = null; 297 | } 298 | } 299 | 300 | async function yawas_getAnnotations_chrome_bookmarks(webUrl,cb) 301 | { 302 | let url = purifyURL(webUrl); 303 | let result = await search({url:url}) 304 | //console.log(result) 305 | let annotations = ''; 306 | let labels = ''; 307 | result.forEach((item, i) => { 308 | let chunks = item.title.split('#__#'); 309 | if (chunks.length > 1) { 310 | annotations += chunks[1]; 311 | } 312 | }); 313 | if (annotations.length > 0) { 314 | yawas_remapAnnotations(webUrl,annotations,labels,cb); 315 | } 316 | } 317 | 318 | function yawas_getAnnotations_chrome_storage(webUrl,cb) { 319 | var keyName = purifyURL(webUrl).hashCode(); 320 | var obj = {}; 321 | obj[keyName] = null; 322 | chrome.storage.sync.get(obj,function(items) { 323 | if (items && items[keyName]) 324 | { 325 | yawas_remapAnnotations(webUrl,items[keyName].annotations,items[keyName].labels,cb); 326 | } 327 | }); 328 | } 329 | 330 | async function yawas_getAnnotations(webUrl,cb) 331 | { 332 | //if (saveLocally) 333 | // return yawas_getAnnotations_chrome_storage(webUrl) 334 | if (saveChromeBookmarks) 335 | return await yawas_getAnnotations_chrome_bookmarks(webUrl,cb); 336 | 337 | googleSignature = null; // invalidate signature 338 | 339 | webUrl = purifyURL(webUrl); 340 | //var url = "https://www.google.com/bookmarks/find?output=rss&q=" + encodeURIComponent(webUrl); 341 | // escape fixes issue https://github.com/ldenoue/yawas/issues/11 because url had ' in it 342 | var url = "https://www.google.com/bookmarks/find?output=rss&q=" + escape(webUrl); 343 | var xhr = new XMLHttpRequest(); 344 | //xhr.withCredentials = true; 345 | abortTimerId = window.setTimeout(function() { 346 | //console.error('aborted GET for url=',url); 347 | xhr.abort(); // synchronously calls onreadystatechange 348 | yawas_setStatusIcon("error"); 349 | cb({error:'network error'}); 350 | }, requestTimeout); 351 | 352 | xhr.open("GET",url,true); 353 | xhr.onerror = function(error) { 354 | //console.log('xhr error',error); 355 | console.log('xhr error'); 356 | clearAbortTimer(); 357 | } 358 | 359 | xhr.onreadystatechange = function () { 360 | if (xhr.readyState === 4) { 361 | if (xhr.status === 200) 362 | { 363 | clearAbortTimer(); 364 | if (xhr.responseURL === url) { 365 | //console.log('got what we wanted, hooray!'); 366 | } 367 | else { 368 | yawas_setStatusIcon("off"); 369 | if (xhr.responseURL.indexOf('/ServiceLogin') !== -1) 370 | { 371 | cb({error:'logged out',signedout:true,url:xhr.responseURL}); 372 | } 373 | else 374 | cb({error:'logged out',signedout:true}); 375 | 376 | return; 377 | } 378 | 379 | if (xhr.responseXML === null) 380 | { 381 | yawas_setStatusIcon("error"); 382 | cb({error:'no XML'}); 383 | return; 384 | } 385 | var signature = yawas_getElement(xhr.responseXML,'signature'); //xhr.responseXML.getElementsByTagName('signature'); 386 | var gooSignature = ''; 387 | if (signature.length>0) 388 | gooSignature = signature[0].childNodes[0].nodeValue; 389 | googleSignature = gooSignature; 390 | //console.log('setting googleSignature to ' + googleSignature); 391 | 392 | yawas_setStatusIcon("on"); 393 | 394 | var items = yawas_getElement(xhr.responseXML, 'item'); //xhr.responseXML.getElementsByTagName('item'); 395 | if (items.length === 0) 396 | { 397 | //console.error('items.length == 0'); 398 | return cb({noannotation:true}); 399 | } 400 | else if (items.length >= 1) 401 | { 402 | for (var i=0;i0) 408 | webAnnotation = annotation[0].childNodes[0].nodeValue; 409 | 410 | var labels = yawas_getElement(items[i],'bkmk_label'); //items[i].getElementsByTagName('bkmk_label'); 411 | var webLabels = ''; 412 | if (labels.length>0) 413 | { 414 | for (var x=0;x 0) 441 | { 442 | var tab = tabs[0]; 443 | //chrome.tabs.sendMessage(tab.id, json, function(response) {}); 444 | const port = chrome.tabs.connect(tab.id); 445 | port.onDisconnect = function (err) { console.error('disconnected',err)}; 446 | /*port.onMessage.addListener((response) => { 447 | console.error('port.onMessage response=',response); 448 | });*/ 449 | port.postMessage(json); 450 | } 451 | }); 452 | } 453 | 454 | String.prototype.hashCode = function() { 455 | var hash = 0, i, chr; 456 | if (this.length === 0) return hash; 457 | for (i = 0; i < this.length; i++) { 458 | chr = this.charCodeAt(i); 459 | hash = ((hash << 5) - hash) + chr; 460 | hash |= 0; // Convert to 32bit integer 461 | } 462 | return 'yawas'+hash; 463 | }; 464 | 465 | function yawas_remapAnnotations(url, annotations, labels,cb) 466 | { 467 | if (annotations === null || annotations === undefined) 468 | { 469 | //console.error('yawas_remapAnnotations called without annotations',url,annotations,labels); 470 | return cb({error:'annotations===null'}); 471 | } 472 | var url = purifyURL(url); 473 | cachedAnnotations[url] = annotations; 474 | cachedLabels[url] = labels; 475 | var highlights = annotationToArray(annotations); 476 | chrome.action.setBadgeText({'text':''+highlights.length}); 477 | chrome.action.setTitle({title:'Yawas'}); 478 | urls[url] = highlights.length; 479 | cb({annotations:highlights}); 480 | } 481 | 482 | function formatAnnotation(text) 483 | { 484 | var newtext = text.replace(/“/gi,leftMark).replace(/”/gi,rightMark); 485 | //newtext = newtext.replace(/“/gi,leftMark).replace(/”/gi,rightMark); 486 | //newtext = text.replace(/<>/gi,'”'); 487 | //newtext = newtext.replace(/“/gi,'“').replace(/”/gi,'”'); 488 | return newtext; 489 | } 490 | 491 | function yawas_compact(webAnnotation) 492 | { 493 | var highlights = annotationToArray(webAnnotation); 494 | for (var i=0;i= 16) 501 | { 502 | sel = len + '~~' + sel.substring(0,8) + sel.substring(len-8); 503 | highlights[i].selection = sel; 504 | } 505 | } 506 | return arrayToAnnotation(highlights) 507 | } 508 | 509 | function yawas_storeHighlight(webUrl,title,highlight,occurence,couleur,pagenumber,cb) 510 | { 511 | var qurl = purifyURL(webUrl); 512 | 513 | // new version uses cache and google signature 514 | var webAnnotation = cachedAnnotations[qurl]; 515 | var webLabels = cachedLabels[qurl]; 516 | if (!webAnnotation) 517 | { 518 | //console.error('no webannotation cached for',webUrl,qurl); 519 | webAnnotation = ''; 520 | } 521 | if (!webLabels) 522 | webLabels = ''; 523 | webAnnotation = formatAnnotation(webAnnotation); 524 | if (occurence === 0) 525 | { 526 | if (pagenumber) 527 | webAnnotation += leftMark + highlight + "@" + occurence + ',' + pagenumber; 528 | else 529 | webAnnotation += leftMark + highlight; 530 | } 531 | else 532 | { 533 | webAnnotation += leftMark + highlight + "@" + occurence; 534 | if (pagenumber) 535 | webAnnotation += ',' + pagenumber; 536 | } 537 | if (couleur != 'yellow') 538 | webAnnotation += '#' + couleur; 539 | webAnnotation += rightMark + " "; 540 | let pureLen = webAnnotation.length; 541 | if (!saveChromeBookmarks && webAnnotation.length > 2048) 542 | { 543 | console.log('too long so compacting annotations',qurl,webAnnotation.length); 544 | var compacted = yawas_compact(webAnnotation); 545 | if (compacted.length > 2048) 546 | { 547 | yawas_setStatusIcon('error'); 548 | return cb({toobig:true}); 549 | } 550 | else 551 | { 552 | console.log('compacted format len=',compacted.length); 553 | webAnnotation = compacted; 554 | } 555 | } 556 | yawas_storeHighlightsNow(qurl, title, webLabels, webAnnotation, googleSignature, function (res){ 557 | if (res.ok) 558 | { 559 | yawas_setStatusIcon('on'); 560 | var nannotations = webAnnotation.split(rightMark).length-1; 561 | chrome.action.setBadgeText({'text':''+nannotations}); 562 | chrome.action.setTitle({title:'Yawas'}); 563 | urls[qurl] = nannotations; 564 | cachedAnnotations[qurl] = webAnnotation; 565 | return cb({addedhighlight:true,pureLen:pureLen}); 566 | } 567 | else 568 | { 569 | yawas_setStatusIcon('error'); 570 | return cb(res); 571 | } 572 | }); 573 | } 574 | 575 | async function yawas_storeHighlightsNow(webUrl, title, labels, annotations, gooSignature, callback) 576 | { 577 | if (saveLocally) 578 | { 579 | var keyName = purifyURL(webUrl).hashCode(); 580 | var obj = {}; 581 | obj[keyName] = {title:title,labels:labels,annotations:annotations}; 582 | chrome.storage.sync.set(obj,function() { 583 | }); 584 | callback({ok:true}); 585 | return; 586 | } 587 | if (saveChromeBookmarks) 588 | { 589 | let url = purifyURL(webUrl) 590 | let obj = {url: url, title: title + '#__#' + annotations}; 591 | var folderName = new Date().toLocaleDateString('en-US',{year:'numeric',month:'short'}) 592 | //console.log('obj=',obj); 593 | let result = await search({url:url}) 594 | if (result && result.length > 0) 595 | { 596 | console.log('updating bookmark') 597 | chrome.bookmarks.update(result[0].id,obj); 598 | } else { 599 | console.log('creating bookmark') 600 | createBookmark(obj,new Date()) 601 | } 602 | callback({ok:true}); 603 | return; 604 | } 605 | 606 | 607 | if (gooSignature == null) 608 | { 609 | callback({error:'not signed in',signedout:true}); 610 | return; 611 | } 612 | webUrl = purifyURL(webUrl); 613 | var xhr = new XMLHttpRequest(); 614 | abortTimerId = window.setTimeout(function() { 615 | console.log('aborting yawas_storeHighlightsNow'); 616 | xhr.abort(); // synchronously calls onreadystatechange 617 | callback({error:'timeout'}); 618 | }, requestTimeout); 619 | 620 | var url = "https://www.google.com/bookmarks/mark?hl=en"; 621 | xhr.open("POST", url); 622 | xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 623 | xhr.onerror = function(error) {clearAbortTimer();} 624 | xhr.onreadystatechange = function () { 625 | if (xhr.readyState == 4) { 626 | if (xhr.status == 200) 627 | { 628 | clearAbortTimer(); 629 | if (xhr.responseURL === url) { 630 | //console.log('got what we wanted, hooray!'); 631 | } 632 | else { 633 | callback({error:'not signed in',signedout:true}); 634 | return; 635 | } 636 | 637 | var signin = xhr == null || xhr.responseText.indexOf('Sign in') != -1; 638 | if (signin) 639 | { 640 | callback({error:'not signed in',signedout:true}); 641 | } 642 | else 643 | { 644 | callback({ok:true}); 645 | } 646 | } 647 | else 648 | { 649 | clearAbortTimer(); 650 | } 651 | 652 | } 653 | }; 654 | var encoded = "sig=" + gooSignature; 655 | encoded += "&title=" + encodeURIComponent(title); 656 | encoded += "&bkmk=" + encodeURIComponent(webUrl); 657 | encoded += "&labels=" + encodeURIComponent(labels); 658 | encoded += "&annotation=" + encodeURIComponent(annotations); 659 | xhr.send(encoded); 660 | } 661 | 662 | function refreshBrowserAction(url) 663 | { 664 | var qurl = purifyURL(url); 665 | if (urls[qurl] != undefined) 666 | { 667 | chrome.action.setBadgeText({'text':''+urls[qurl]}); 668 | } 669 | else 670 | chrome.action.setBadgeText({'text':'0'}); 671 | chrome.action.setTitle({title:'Yawas'}); 672 | } 673 | 674 | function copyTextToClipboard(text) { 675 | sendMessageActiveTab({action:'yawas_copytoclipboard',payload: text}) 676 | } 677 | 678 | let googleColors = []; 679 | googleColors['yellow'] = '#FFFF66'; 680 | googleColors['blue'] = '#0df'; 681 | googleColors['red'] = '#ff9999'; 682 | googleColors['green'] = '#99ff99'; 683 | googleColors['white'] = 'transparent'; 684 | 685 | function yawas_copyclipboard(url,title, html = true) 686 | { 687 | url = purifyURL(url); 688 | if (title.trim().length == 0) 689 | title = 'no title'; 690 | var webAnnotation = cachedAnnotations[url]; 691 | var highlights = annotationToArray(webAnnotation); 692 | if (html) { 693 | var body = '' + title + '
'; 694 | for (var i=0;i>\n'; 699 | body += '' + highlights[i].selection + '
' 700 | } 701 | body += '
' 702 | copyTextToClipboard(body); 703 | 704 | } else { 705 | var body = title + '\n' + url + '\n'; 706 | for (var i=0;i>\n'; 711 | } 712 | copyTextToClipboard(body); 713 | } 714 | } 715 | 716 | function yawas_email(url,title) 717 | { 718 | url = purifyURL(url); 719 | if (title.trim().length == 0) 720 | title = 'no title'; 721 | var webAnnotation = cachedAnnotations[url]; 722 | var highlights = annotationToArray(webAnnotation); 723 | var body = title + '\n\n' + url + '\n\n'; 724 | for (var i=0;i>\n'; 729 | } 730 | var compose = "https://mail.google.com/mail/?extsrc=mailto&url=" + encodeURIComponent("mailto:?subject=" + title + "&body=" + body); 731 | /*var width = Math.min(screen.width-40,640); 732 | var height = Math.min(screen.height-40,480); 733 | var left = Math.floor( (screen.width - width) / 2); 734 | var top = Math.floor( (screen.height - height) / 2); 735 | window.open(compose,'email_popup','left='+left+',top='+top+',height='+height+'px,width='+width+'px,resizable=1');*/ 736 | chrome.tabs.create({url:compose}); 737 | } 738 | 739 | // deprecated in Chrome and does not work in Firefox 740 | /*chrome.tabs.onSelectionChanged.addListener(function(tabId, selectInfo) { 741 | chrome.tabs.query({active:true,currentWindow: true}, function(tabs) { 742 | if (tabs && tabs.length > 0 && tabs[0].url) 743 | { 744 | var tab = tabs[0]; 745 | refreshBrowserAction(tab.url); 746 | } 747 | }); 748 | });*/ 749 | 750 | chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { 751 | if (tab && tab.url) 752 | refreshBrowserAction(tab.url); 753 | }); 754 | 755 | function arrayToAnnotation(highlights) 756 | { 757 | var annotation = ''; 758 | for (var i=0;i 0) 766 | annotation += '@' + highlights[i].n; 767 | if (highlights[i].color != 'yellow') 768 | annotation += '#' + highlights[i].color; 769 | annotation += rightMark + ' '; 770 | } 771 | return annotation; 772 | } 773 | 774 | function annotationToArray(annotations) 775 | { 776 | if (!(annotations > '')) 777 | return []; 778 | if (annotations.trim().length === 0) 779 | return []; 780 | var chuncks = annotations.split(rightMark); 781 | var highlights = []; 782 | for (var i=0;i=0) 786 | { 787 | var comment = chuncks[i].substring(0,j).trim(); 788 | var highlight = chuncks[i].substring(j+lenQuote); 789 | var k = highlight.lastIndexOf('@'); 790 | var occurence = 0; 791 | var couleur = 'yellow'; 792 | var pagenumber = null; 793 | if (k > 0) 794 | { 795 | try { 796 | var string = highlight.substring(k+1); 797 | var c = highlight.indexOf('#',k); 798 | if (c != -1) 799 | { 800 | var trail = highlight.substring(k+1,c); 801 | var chk = trail.split(','); 802 | //occurence = parseInt(highlight.substring(k+1,c)); 803 | occurence = parseInt(chk[0]); 804 | if (chk.length===2) 805 | pagenumber = parseInt(chk[1]); 806 | couleur = highlight.substring(c+1); 807 | } 808 | else 809 | { 810 | //occurence = parseInt(highlight.substring(k+1)); 811 | var trail = highlight.substring(k+1); 812 | var chk = trail.split(','); 813 | occurence = parseInt(chk[0]); 814 | if (chk.length===2) 815 | pagenumber = parseInt(chk[1]); 816 | } 817 | highlight = highlight.substring(0,k); 818 | } catch (eint) { occurence = 0; } 819 | } 820 | else 821 | { 822 | var c = highlight.lastIndexOf('#'); 823 | if (c>0) 824 | { 825 | couleur = highlight.substring(c+1); 826 | highlight = highlight.substring(0,c); 827 | } 828 | } 829 | highlight = highlight.trim();//yawas_TrimString(highlight); 830 | var obj = {p:pagenumber,selection:highlight,n:occurence,color:couleur,comment:comment}; 831 | highlights.push(obj); 832 | } 833 | } 834 | return highlights; 835 | } 836 | 837 | /*async function startImport() { 838 | importAllBookmarks(function (n,error) { 839 | chrome.runtime.sendMessage({ msg: "importMessage", n: n, error: error }); 840 | }); 841 | }*/ 842 | 843 | function requestCallback(request, sender, sendResponse) 844 | { 845 | var tabURL = purifyURL(request.url); 846 | var tabTitle = request.title; 847 | //console.log('requestCallback',request); 848 | //if(request.msg == "startImportFunc") 849 | // startImport(); 850 | if (request.fn === 'yawas_getAnnotations') 851 | { 852 | yawas_getAnnotations(request.url,function(res){ 853 | signedin = !res.error; 854 | sendResponse(res); 855 | }); 856 | } 857 | /*else if (request.fn === 'yawas_donate') 858 | { 859 | chrome.tabs.create({ url: donate_url }); 860 | sendResponse({}); 861 | }*/ 862 | else if (request.fn === 'yawas_toolbar_signed_in') 863 | { 864 | signedin = true; 865 | sendResponse({}); 866 | } 867 | else if (request.fn === 'addhighlight') 868 | { 869 | yawas_storeHighlight(request.url,request.title,request.selection,request.occurence,request.couleur,null,function (res){ 870 | signedin = !res.error; 871 | sendResponse(res); 872 | }); 873 | } 874 | else if (request.fn === 'addhighlightpdf') 875 | { 876 | yawas_storeHighlight(request.url,request.title,request.selection,request.occurence,request.couleur,request.p,function(res){ 877 | sendResponse(res); 878 | }); 879 | } 880 | else if (request.fn === 'emailhighlights') 881 | { 882 | yawas_email(request.url,request.title); 883 | sendResponse({}); 884 | } 885 | else if (request.fn === 'copytoclipboard') 886 | { 887 | yawas_copyclipboard(request.url,request.title); 888 | sendResponse({}); 889 | } 890 | else if (request.action == 'delete_highlight') 891 | delHighlightNow(request.highlightString,request.n,tabURL,tabTitle,null,function (res){ 892 | sendResponse(res); 893 | }); 894 | else if (request.action == 'delete_pdf_highlight') 895 | delHighlightNow(request.highlightString,request.n,request.url,request.title,request.p,function(res){ 896 | sendResponse(res); 897 | }); 898 | else if (request.action == 'recolor_highlight') 899 | updateHighlight(request.highlightString,request.n,request.newcolor,request.comment,tabURL,tabTitle,null,function(res){ 900 | sendResponse(res); 901 | }); 902 | else if (request.action == 'recolor_pdf_highlight') 903 | updateHighlight(request.highlightString,request.n,request.newcolor,'',request.url,request.title,request.p,function(res){ 904 | sendResponse(res); 905 | }); 906 | 907 | // important: we want to use sendResponse asynchronously sometimes (e.g. fetching annotations using XHR) 908 | return true; 909 | 910 | } 911 | 912 | function updateHighlight(fragment, occurence, newcolor, comment, url, title,pagenumber,cb) 913 | { 914 | var qurl = url; 915 | var j = qurl.lastIndexOf('/index.htm'); 916 | if (j == -1) 917 | j = qurl.lastIndexOf('/index.html'); 918 | if (j != -1) 919 | qurl = qurl.substring(0,j+1); 920 | webAnnotation = cachedAnnotations[qurl]; 921 | if (!webAnnotation) 922 | { 923 | return cb({error:'no annotation found'}); 924 | } 925 | 926 | var highlights = annotationToArray(webAnnotation); 927 | var idx = -1; 928 | for (var i=0;i 2048) 957 | return cb({error:'too long',toobig:true}); 958 | yawas_storeHighlightsNow(url, title, webLabels, webAnnotation, googleSignature, function (res){ 959 | if (res.ok) 960 | { 961 | cachedAnnotations[qurl] = webAnnotation; 962 | 963 | cb({highlights:highlights}); 964 | } 965 | else 966 | cb(res); 967 | }); 968 | } 969 | 970 | } 971 | 972 | function delHighlightNow(fragment,occurence,url,title,pagenumber,cb) 973 | { 974 | var qurl = purifyURL(url); 975 | webAnnotation = cachedAnnotations[qurl]; 976 | if (!webAnnotation) 977 | { 978 | return cb({error:'no annotation found'}); 979 | } 980 | 981 | var highlights = annotationToArray(webAnnotation); 982 | var idx = -1; 983 | for (var i=0;i 1) 1035 | return decodeURIComponent(comps[1]) 1036 | else 1037 | return href; 1038 | } 1039 | 1040 | /*chrome.contextMenus.create({ 1041 | "id" : "donate", 1042 | "title" : "Donate💰", 1043 | "type" : "normal", 1044 | "contexts" : ["selection","page"], 1045 | "onclick" : getDonateHandler() 1046 | });*/ 1047 | 1048 | chrome.contextMenus.onClicked.addListener((info,tab) => { 1049 | if (info.menuItemId === 'delete') 1050 | sendMessageActiveTab({action:'yawas_delete_highlight'}) 1051 | else if (info.menuItemId === 'copyclipboard') { 1052 | let url = isPDF(tab.url); 1053 | let title = tab.title; 1054 | yawas_copyclipboard(url,title); 1055 | } else if (info.menuItemId === 'email') { 1056 | let url = isPDF(tab.url); 1057 | let title = tab.title; 1058 | yawas_email(url,title); 1059 | } else if (info.menuItemId === 'search') 1060 | chrome.tabs.create({url:chrome.runtime.getURL('localsearch.html')}); 1061 | else if (info.menuItemId === 'edit') { 1062 | let possiblePDFUrl = isPDF(info.pageUrl); 1063 | chrome.tabs.create({url:chrome.runtime.getURL('localedit.html?url=' + encodeURIComponent(purifyURL(possiblePDFUrl)))}); 1064 | } else { 1065 | let color = info.menuItemId; 1066 | sendMessageActiveTab({action:'yawas_chrome',color:color,url:info.pageUrl}); 1067 | } 1068 | }) 1069 | 1070 | chrome.contextMenus.create({ 1071 | "id" : "yellow", 1072 | "title" : "Yellow",// (Ctrl-Shift-Y)", 1073 | "type" : "normal", 1074 | "contexts" : ["selection"], 1075 | }); 1076 | chrome.contextMenus.create({ 1077 | "id" : "red", 1078 | "title" : "Red",// (Ctrl-Shift-R)", 1079 | "type" : "normal", 1080 | "contexts" : ["selection"], 1081 | }); 1082 | chrome.contextMenus.create({ 1083 | "id" : "blue", 1084 | "title" : "Blue",// (Ctrl-Shift-B)", 1085 | "type" : "normal", 1086 | "contexts" : ["selection"], 1087 | }); 1088 | chrome.contextMenus.create({ 1089 | "id" : "green", 1090 | "title" : "Green",// (Ctrl-Shift-G)", 1091 | "type" : "normal", 1092 | "contexts" : ["selection"], 1093 | }); 1094 | 1095 | chrome.contextMenus.create({ 1096 | "id" : "note", 1097 | "title" : "Comment",// (Ctrl-Shift-C)", 1098 | "type" : "normal", 1099 | "contexts" : ["selection"], 1100 | }); 1101 | 1102 | chrome.contextMenus.create({ 1103 | "id" : "delete", 1104 | "title" : "Delete",// (Ctrl-Shift-D)", 1105 | "type" : "normal", 1106 | "contexts" : ["selection"], 1107 | }); 1108 | 1109 | chrome.contextMenus.create({ 1110 | "id" : "copyclipboard", 1111 | "title" : "Copy", 1112 | "type" : "normal", 1113 | "contexts" : ["page"], 1114 | }); 1115 | 1116 | chrome.contextMenus.create({ 1117 | "id" : "email", 1118 | "title" : "Email", 1119 | "type" : "normal", 1120 | "contexts" : ["page"], 1121 | }); 1122 | 1123 | chrome.contextMenus.create({ 1124 | "id" : "search", 1125 | "title" : "Search", 1126 | "type" : "normal", 1127 | "contexts" : ["page"], 1128 | }); 1129 | 1130 | chrome.contextMenus.create({ 1131 | "id" : "edit", 1132 | "title" : "Edit", 1133 | "type" : "normal", 1134 | "contexts" : ["page"], 1135 | }); 1136 | 1137 | chrome.commands.onCommand.addListener(function(command) { 1138 | if (command === 'yawas-yellow') 1139 | sendMessageActiveTab({action:'yawas_chrome',color:'yellow'}); 1140 | else if (command === 'yawas-red') 1141 | sendMessageActiveTab({action:'yawas_chrome',color:'red'}); 1142 | else if (command === 'yawas-blue') 1143 | sendMessageActiveTab({action:'yawas_chrome',color:'blue'}); 1144 | else if (command === 'yawas-green') 1145 | sendMessageActiveTab({action:'yawas_chrome',color:'green'}); 1146 | else if (command === 'yawas-delete') 1147 | sendMessageActiveTab({action:'yawas_delete_highlight'}); 1148 | else if (command === 'yawas-note') 1149 | sendMessageActiveTab({action:'yawas_chrome',color:'note'}); 1150 | }); 1151 | 1152 | function purifyURL(href) 1153 | { 1154 | if (href && href.indexOf('https://mail.google') === 0) 1155 | return href; 1156 | 1157 | try { 1158 | var url = stripMobilizer(href); 1159 | var pos = url.indexOf('#'); 1160 | if (pos > 0) 1161 | url = url.substring(0,pos); 1162 | url = url.replace(/[?&]utm_.*/,''); 1163 | url = url.replace(/[?&]WT\..*/,''); 1164 | url = url.replace(/[?&]ns_.*/,''); 1165 | url = url.replace(/[?&]rand=.*/,''); 1166 | url = url.replace(/[?&]src=.*/,''); 1167 | url = url.replace(/[?&]imm_mid=.*/,''); 1168 | url = url.replace(/[?&]cmp=.*/,''); 1169 | url = url.replace(/[?&]ncid=.*/,''); 1170 | url = url.replace(/[?&]cps=.*/,''); 1171 | url = url.replace(/[?&]mc_cid=.*/,''); 1172 | url = url.replace(/[?&]mc_eid=.*/,''); 1173 | url = url.replace(/[?&]mbid=.*/,''); 1174 | if (url.indexOf('nytimes.com')!=-1) 1175 | url = url.split('?')[0]; 1176 | return url; 1177 | } catch (eurl) { return href; } 1178 | } 1179 | --------------------------------------------------------------------------------