From 7766f4c5bc91391465f01a5aeab67e7ea11d062d Mon Sep 17 00:00:00 2001 From: Kebo Date: Sat, 21 Jun 2025 16:51:02 -0500 Subject: [PATCH] initial commit - everything works!! --- .DS_Store | Bin 0 -> 6148 bytes README.md | 31 +++++++++++++++ content.js | 15 ++++++++ manifest.json | 15 ++++++++ moffsoft_64.png | Bin 0 -> 1860 bytes popup.html | 59 +++++++++++++++++++++++++++++ popup.js | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 219 insertions(+) create mode 100644 .DS_Store create mode 100644 README.md create mode 100644 content.js create mode 100644 manifest.json create mode 100644 moffsoft_64.png create mode 100644 popup.html create mode 100644 popup.js diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..557268ef575afa2b0358ee2d712d03e49479ea20 GIT binary patch literal 6148 zcmeHK!A`nhnNr_BE@yH@iT^IWA*89K?En zJ|`3t1w?`0tpIWZ$|pH~29 zHk-d=QCd+z6c7bg3h@3Az!^h_xka;epy7jo;~pa$p3Pr>VBq?p!`vbwFlDJgOI7ZQ zq1??uSvumO8=qUWbW)}=^0>;%-B6Ufc&NY(Cly+hRum8g$_mtNx5elG#rFGunIt_? zKos~_3Men?M?Eabovmw&;|F!>{3 MWROM__)!JE01MliaR2}S literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e2ea05 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Sharefeed Entry Tool + +A silly little Chrome extension I created to more easily create Sharefeed entries while I am on the web. + +When you click the moffsoft icon, a popup will show up with all of the possible fields, with some or even most of them auto-populated with +data from the page. Clicking "generate" will generate the JSON code in a field you can easily copy and paste from. + +This is just a tool I made for myself, but if you want to use it for your own implementation of the Sharefeed... I mean....... I guess? + +## Supported Sites + +You can technically use this extension on *any* site, as it will always automatically fill in the URL and date accessed fields for you. +However, I've built in additional autofill support for certain websites. + +- Medium: Title, author, and date published fields are autofilled from the handy [`NewsArticle` JSON-LD schema](https://schema.org/NewsArticle) +included on every page. + +## Wait, what is this even for? + +I have a page on my site called the [sharefeed](https://eleboog.com/sharefeed) where I share links to things I think are interesting. +To make the implementation of this slightly easier on myself, I've stored all the links in a JSON file where each entry is a single object +in the following format: + +```typescript +url: string // The URL of the linked page. +title: string // The title of the entry. Usually analogous to the title of the linked page or article. +author: string? // The author of the linked page. +publishedDate: Date? // The date in which the linked page was **originally published**. +accessedDate: Date // The date in which the linked page was **accessed by me** (i.e. the date of this entry itself). +note: string // A message to be attached to this link, usually detailing why I decided to share it. +``` \ No newline at end of file diff --git a/content.js b/content.js new file mode 100644 index 0000000..5a258c6 --- /dev/null +++ b/content.js @@ -0,0 +1,15 @@ +//================================================== +// sharefeed entry tool - by kebokyo +// content.js - examines the content of the page +// for data autofill +// created june 21, 2025 +//================================================== + + +let entry = { + title: "" +}; + +// if schema.org JSON-LD is present, read that data + +return entry; \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..701cc71 --- /dev/null +++ b/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "Sharefeed Entry Tool", + "description": "Creates a sharefeed entry based on the current webpage.", + "version": "1.0", + "manifest_version": 3, + "action": { + "default_icon": "moffsoft_64.png", + "default_popup": "popup.html" + }, + "permissions": [ + "storage", + "activeTab", + "scripting" + ] +} diff --git a/moffsoft_64.png b/moffsoft_64.png new file mode 100644 index 0000000000000000000000000000000000000000..01f4701803d08838436e7df5196a75d3aa099377 GIT binary patch literal 1860 zcmV-K2fO%*P)huZ^;F=fEFz{nKD{Rs>ak(2(4 zQeFbHNym7AUcg*iMM=&8V@xMtH*jf+Vh;m@MdY-%uvBNjouq3#KyP4%zpzvRnBPuv z@AntPUINCLa-frcN+l)7>G>*6nH~~gsEF)w3(BJOHpWx~PXSYa0Z!Rj-~`YFGy{8q zpMW3Szar8SVVJD1lp+~mI`Hhdk_cz=Fx0b(dIp~W-a~ic+B{=)LxD_s?&sp2v4SCB zXG#j}aKNatj4`{NjTh9hs)+;u8iuB=$Unep)Io8_`KSN@2p-QcEC9270=5EOow_R2 zS04t92Oa`m1U3PGg;1sicoP_fMsS4y;I*g#-0c&vK4ia6z%{@^bZ5_{)V~EXUVw(E z0F3hq_&H{9a~ZG%_@jWv265jg0Q4j~DjO091V>&4FbCLEL;$9VNH|YWM2@1TfS0C* z=^G&LjEKAn3V-8hQyhO4gt|cGfgX z0f4;(?pkg$Ie8W{-y5%~^v zaJ;nG7}Gg{65|u}O-jS#+y~4@k3^Shfrk?)QA_{^#$C5>&~_~~0sUgg=}%$Tyr18W zdn2?E4Q5=r09Y17&h;_JPm2b`0zgFe0gHXbGmJ5}L=;ub=?`(2fHQqNqc^wSDRlt2 zFoKL9Dg3l=;|nCZ7B|M+gq{lC)YBRf$$PD3jOhU!@%3#FY6bSAY4zXH5d8@1e(y}f zi(5@LvUh~&H`&rJ4c-I14%`b|0d&c!M+yLRBRi^V1bXG^_e@F#9|zU~GjT%PNctxQ zz%ix{IO|hvU7kK05*XbAtaoNnMIznP0KhTm1&gAA!4hyVhT$iH=ZNqGCLgDk8#Kmr zL4%_ap5o)c7!mo+*S0tMuqiB^0#*XcMeinZ=~0QU{n5Z6G$mFAyeuM{vpS@Oqyyww zvP0$_KxbdO+X(;FH<0Ixqgmu9(U)TW@pCT8D8^-gKEPn0GDo{bmcf_#+AIiRcq1@5 z$2b+heb_Hs{|3HBUH0YZWll|G0MKxEDVpZ;BYO;}M~|CsKA<-F+O`5yJZ;|1WBlg? z?C3x_@F1|yCx4qW3&sFtp+9WEAeW=*tgzGc5Sr~NbNcroJB@Y}=*^X{V{v*@n1p$Va0ndBnHKPg6&~A4q z0MN{D9K*f*3(&`rx7bo2bpAgQV0=#pShw9qQalE|1I;A(Mb=q#Q&|hVf;u6&(q7xq zH@s!&dd>&hk^tie0lzwJ&H!`MGQ14{=#8)K8EOl#3TN$FrsXt(lN-yOcGYMw5@dWm zkg>?G<@`o>2pE^P@m>JdSQKjpCgv5Ommwco`kzK0C4p{Ur!8$72slzXpD8{_f=w=j z{`4bzvuY(oMCTFG75n>vm7X^DSn4O`kU7TzA$AHbECBN?ioFv<&}g!E_XBwH2I&6b z*1go8hD8Cuwky}g6!w`#)+kSX1?std0dz06@gkZW9YkAdBK|9_jh3>(?mwbQ(QzU& zUPPK>j*%+4{=JtDh)AogaR}OHDeD_kRzWC(Zh&5j^mNWg<(9Jl63Ay;ro73PvWA$l3PKt56!6ocoWHWLkTGT=aHX$!w|JhVmLg?< z4``d_0=?OJG^`Nk3GFINRegNb3PjHN?B@Obalm6C&i+ei8>72?3@0=$Wp<6ILVr+T z3s$xOH{-jcKr_2fqZt-kzr5g;0-w7THpbMUzs;)%m@G|btbYpi9flB`)~W}liYvU7 z$@f*KV~^UR@D@CGgr&~e1^Oy9?n};9KzXr^N(6udLU-T^g4eg7cK{QLZA=`_nHe(1 yTm(!4Zbxs5`=ZIv!)U5`3-CU$OL7S+Vfr7VGH%}?Eam0^0000 + + + +

new sharefeed entry

+

Required fields are in bold with a red asterisk. *

+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+

+
+ + + + \ No newline at end of file diff --git a/popup.js b/popup.js new file mode 100644 index 0000000..6b504d9 --- /dev/null +++ b/popup.js @@ -0,0 +1,99 @@ +function parsePage() { + let entry = { + url: window.location.href, + title: "", + publishedDate: "" + }; + + // fix the URL's of certain websites + if (entry.url.startsWith('https://freedium.cfd/')) { // extract original article link from freedium url + entry.url = entry.url.replace('https://freedium.cfd/', ''); + } + + // if schema.org JSON-LD is present, read that data + const jsonLDElem = document.querySelector('script[type="application/ld+json"]'); + if (jsonLDElem) { + const jsonLDObj = JSON.parse(jsonLDElem.textContent); + if (jsonLDObj['@type'] == 'NewsArticle') { + entry.publishedDate = jsonLDObj.datePublished?.slice(0, 19) ?? ""; + entry.author = jsonLDObj.author?.name ?? "" + entry.title = jsonLDObj.headline ?? "" + } + } + + return entry; +} + +// thanks stack overflow: https://stackoverflow.com/questions/12413243/javascript-date-format-like-iso-but-local +function dateToISOLikeButLocal(date) { + const offsetMs = date.getTimezoneOffset() * 60 * 1000; + const msLocal = date.getTime() - offsetMs; + const dateLocal = new Date(msLocal); + const iso = dateLocal.toISOString(); + const isoLocal = iso.slice(0, 19); + return isoLocal; +} + +function fillForm(entry) { + const curDate = new Date(); + + document.getElementById('url').value = entry.url; + document.getElementById('title').value = entry.title ?? ""; + document.getElementById('author').value = entry.author ?? ""; + document.getElementById('publishedDate').value = entry.publishedDate ?? ""; + document.getElementById('accessedDate').value = dateToISOLikeButLocal(curDate); +} + +function runContentScript(tabId) { + chrome.scripting.executeScript({ + target: { tabId: tabId}, + func: parsePage, + }).then((injectionResults) => { + fillForm(injectionResults[0].result); + }); +} + +function generateJSON(event) { + event.preventDefault(); + + const urlField = document.getElementById('url'); + const titleField = document.getElementById('title'); + const authorField = document.getElementById('author'); + const publishedDateField = document.getElementById('publishedDate'); + const accessedDateField = document.getElementById('accessedDate'); + const noteField = document.getElementById('note'); + + const errorElem = document.getElementById('error'); + const JSONElem = document.getElementById('generated'); + + // first, make sure all of our required fields are in place + if ((urlField.value == null || urlField.value == "") || + (titleField.value == null || titleField.value == "") || + (accessedDateField.value == null || accessedDateField.value == "") || + (noteField.value == null || noteField.value == "")) { + errorElem.textContent = "ERROR: Missing required field." + return; + } + + errorElem.textContent = "" + JSONElem.value = `{ + "url": "${urlField.value}", + "title": "${titleField.value}", + "author": "${authorField.value}", + "publishedDate": "${publishedDateField.value.replace('T', ' ')}", + "accessedDate": "${accessedDateField.value.replace('T', ' ')}", + "note": "${noteField.value}",\n},` +} + +window.addEventListener('DOMContentLoaded', () => { + chrome.tabs.query({ + active: true, + currentWindow: true + }, tabs => { + runContentScript(tabs[0].id); + }); + + const form = document.getElementById('sharefeed') + form.addEventListener('submit', generateJSON); +}); +