Skip to content

Commit

Permalink
convert to TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
ku committed Dec 19, 2021
1 parent cc469a5 commit f565bbf
Show file tree
Hide file tree
Showing 21 changed files with 337 additions and 257 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.build/
.DS_Store
node_modules/
npm-debug.log
Expand Down
18 changes: 10 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@

NAME=createlink
EXTDIR=extension
VERSION=$(shell plutil -convert json -r -o - ./extension/manifest.json | "grep" '"version"' | "egrep" -o '\w(\.\w+)+')
DIRNAME=$(shell pwd)
CWD=$(shell pwd)
SRC="extension/js/popup.js"
EXTENSIONDIR=extension
EXT_DIRNAME=extension
EXT_DIR=$(CWD)/$(EXT_DIRNAME)

CRXMAKE_DIR=./crxmake
TMPFILELIST=/tmp/filelist
BUILD_DIR=$(CWD)/.build
TMPFILELIST=$(BUILD_DIR)/filelist

$(NAME)-$(VERSION).zip: $(SRC)
find "$(EXTENSIONDIR)" | sed 's/$(EXTENSIONDIR)/./' | grep -v .js.map > $(TMPFILELIST)
cd $(EXTENSIONDIR); cat $(TMPFILELIST) | zip -q $(DIRNAME)/$@ -@
'rm' $(BUILD_DIR)/$@
mkdir -p $(BUILD_DIR)
find "$(EXT_DIRNAME)" | sed 's/$(EXT_DIRNAME)/./' | grep -v .js.map > $(TMPFILELIST)
cd $(EXT_DIR); cat $(TMPFILELIST) | zip -q $(BUILD_DIR)/$@ -@

$(SRC):
./node_modules/.bin/webpack --mode=production
Expand All @@ -20,4 +22,4 @@ watch:
./node_modules/.bin/webpack -w --mode=development

clean:
rm $(NAME).crx $(NAME).zip
rm -rf .build/
11 changes: 0 additions & 11 deletions extension/background.html

This file was deleted.

11 changes: 6 additions & 5 deletions extension/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
switch (request.type) {
case 'ping':
return sendResponse('pong')
return sendResponse({type: 'pong'})
case 'showInputDialog':
const text = window.prompt("CreateLink needs your input");
return sendResponse(text);
return sendResponse({type: request.type, text});
case 'selectedText':
const s = document.getSelection()
return sendResponse(s ? s.toString() : '')
return sendResponse({ type: request.type, text: (s ? s.toString() : '')})
case 'evaluateFilter':
const f = new Function('s', request.code)
return sendResponse(f.call(null, request.string))
return sendResponse({ type: request.type, text: f.call(null, request.string)})
case 'copyToClipboard':
copyToClipboard(request.link)
return sendResponse({ type: request.type, text: copyToClipboard(request.link) })
}
});

Expand All @@ -34,4 +34,5 @@ function copyToClipboard(text) {
textarea.select()
document.execCommand("copy");
textarea.parentNode.removeChild(textarea)
return text
}
6 changes: 3 additions & 3 deletions extension/manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "Create Link",
"version": "0.5.0",
"version": "0.5.1",
"manifest_version": 3,
"background": {
"service_worker": "js/background.js"
"service_worker": "js/service-worker.js"
},
"action": {
"default_icon": "icon64.png",
Expand Down Expand Up @@ -36,5 +36,5 @@
"http://*/*",
"https://*/*"
],
"permissions": [ "tabs", "contextMenus", "storage", "clipboardWrite" ]
"permissions": [ "contextMenus", "storage", "clipboardWrite" ]
}
35 changes: 25 additions & 10 deletions extension/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<script src="./js/options.js"></script>
</head>
<body>
<div id="about">
<section id="about">
<div>
<img src="icon128.png" class="icon" width="128" height="128" />
</div>
Expand All @@ -22,19 +22,19 @@ <h1>Create Link</h1><h2 id="version"></h2>
<li>Icons by <a href="http://icontoaster.com/?free-icons-for-all">icontoaster.com</a></li>
</ul>
</div>
</div>
</section>

<div>
<section>
<h2>Default Format</h2>
<p>Select the format from the table below that should be used when the keyboard shortcut is activated.</p>
<!-- when table row is selected, show selection below -->
<p>Default is currently <select id="current-default-format"></select></p>
<p>
<a href="#" id="configure-shortcut">Configure shortcut</a>
</p>
</div>
</section>

<div id="configuration">
<section id="configuration">
<h2>Formats</h2>
<table id="cocoatable" class="cocoatable">
<thead>
Expand All @@ -52,9 +52,9 @@ <h2>Formats</h2>
<div id="cocoatable-button-minus" class="button minus">-</div>
<div style="clear:both"><!-- --></div>
</div>
</div>
</section>

<div>
<section>
<h2>Variables</h2>
<div>
Create Link recognizes some <a href="https://addons.mozilla.org/en/firefox/addon/142">Make Link</a> compatible variables.
Expand All @@ -77,15 +77,30 @@ <h2>Variables</h2>
<dt>%input%</dt>
<dd>Shows a dialog to input text.</dd>
</dl>
</div>
</section>

<div>
<section>
<h2>Meta Characters</h2>
<div>
CreateLink supports following meta characters to put special characters.
</div>
<dl id="metachars">
<dt>\t</dt>
<dd>Tab character.</dd>
<dt>\n</dt>
<dd>New line. 0x0A Line Feed.</dd>
<dt>\r</dt>
<dd>0x0D Carriage Return.</dd>
</dl>
</section>

<section>
<h2>Filter</h2>
<div>
<p>Filter must be specified in the following format (perl-like substitution operator): <code>s/<i>&lt;pattern&gt;</i>/<i>&lt;replacement&gt;</i>/<i>[&lt;flags&gt;]</i></code><br>
Pattern is regular expression and flags are either "g", "i", "m" or "y".</p>
<p>For example, <code>s/\[at\]/@/gi</code> will replace all occurrences (g) of "[at]/[AT]/[At]/[aT]" case-insensitively (i) into "@".</p>
</div>
</div>
</section>
</body>
</html>
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"devDependencies": {
"@types/chrome": "0.0.171",
"gutil": "^1.6.4",
"ts-loader": "^9.2.6",
"typescript": "^4.5.4",
"webpack": "^5.0.0",
"webpack-cli": "^4.9.1"
}
Expand Down
34 changes: 19 additions & 15 deletions src/context-menu-handler.js → src/context-menu-handler.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
const CreateLink = require('./createlink')
const utils = require('./utils')
const fmt = require('./formats')
import fmt, { FormatDefinition } from "./formats";
import { MessageBroker } from './message-broker'
import {CreateLink } from './createlink'
import { sendMessageToTab } from './utils'

// run in service worker context.
module.exports = class ContextMenuHandler {
constructor(broker) {
this.contextMenuIdList = {};
export class ContextMenuHandler {
broker: MessageBroker
contextMenuIdList: { [name: string]: number }

constructor(broker: MessageBroker) {
this.broker = broker
this.contextMenuIdList = {};
}

initialize(formats) {
initialize(formats: FormatDefinition[]) {
this.updateContextMenus(formats)

chrome.contextMenus.onClicked.addListener(this.onMenuItemClicked.bind(this))
chrome.runtime.onMessage.addListener(this.onMessage.bind(this))
}

onMessage(request, sender, sendResponse) {
onMessage(request: any, sender: chrome.runtime.MessageSender, sendResponse: ((response?: any) => void)) {
if (request.request == 'updateFormats') {
// options page requests updating the items
this.updateContextMenus(request.formats)
}
}

formatIndexOfMenuItemId(menuItemId) {
return this.contextMenuIdList[menuItemId]
formatIndexOfMenuItemId(menuItemId: string|number): number {
return this.contextMenuIdList[String(menuItemId)]
}

// callback function for contextMenus.onClicked cannot be an async function.
onMenuItemClicked(info, tab) {
utils.sendMessageToTab(tab.id, { type: 'ping' }).then(async (response) => {
if (response !== "pong") {
onMenuItemClicked(info: chrome.contextMenus.OnClickData, tab: chrome.tabs.Tab) {
sendMessageToTab(tab.id, { type: 'ping' }).then(async (response) => {
if (response && response.type !== "pong") {
// Reload the tab. The tab might be opened before this extension is installed.
// It should respond once it is reloaded.
chrome.tabs.reload(tab.id)
Expand All @@ -41,12 +45,12 @@ module.exports = class ContextMenuHandler {
const def = fmt.format(formatIndex)
const cl = new CreateLink()
cl.formatInTab(def, info, tab).then(link => {
utils.sendMessageToTab(tab.id, { type: 'copyToClipboard', link })
sendMessageToTab(tab.id, { type: 'copyToClipboard', link })
})
})
}

updateContextMenus(formats) {
updateContextMenus(formats: FormatDefinition[]) {
chrome.contextMenus.removeAll();

if (formats.length == 1) {
Expand Down
48 changes: 25 additions & 23 deletions src/createlink.js → src/createlink.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@

const utils = require('./utils')
const fmt = require('./formats')
import {showInputDialog, sendMessageToTab} from './utils'
import fmt, { FormatDefinition } from './formats'

class CreateLink {
interface ClickContext {
selectionText?: string
pageUrl: string
linkUrl?: string
srcUrl?: string
mediaType?: string
}


export class CreateLink {
constructor() {
}

applyFilter(tabId, def, data) {
applyFilter(tabId: number, def: FormatDefinition, data: string): Promise<string> {
if (def.filter) {
var m = def.filter.match(/^s\/(.+?)\/(.*?)\/(\w*)$/);
if (m) {
var r = new RegExp(m[1], m[3]);
data = data.replace(r, m[2]);
} else {
return utils.sendMessageToTab(tabId, {type: 'evaluateFilter', code: def.filter, string: data})
return sendMessageToTab(tabId, { type: 'evaluateFilter', code: def.filter, string: data }).then( response => response.text )
}
}
return Promise.resolve(data);
}

formatLinkText(def, url, text, title, inputs) {
formatLinkText(def: FormatDefinition, url: string, text: string, title: string, inputs: string[]): string {
text = text || ''

var data = def.format.
Expand All @@ -41,18 +50,18 @@ class CreateLink {
})
}

getInputs(def, tabId) {
getInputs(def: FormatDefinition, tabId: number): Promise<string[]> {
const m = def.format.match(/%input%/g)
if (m) {
return Promise.all( m.map( () => {
return utils.showInputDialog(tabId)
}) )
return Promise.all(m.map(() => {
return showInputDialog(tabId).then(response => response.text)
}))
} else {
return Promise.resolve([])
}
}

async formatInTab(def, info, tab) {
async formatInTab(def: FormatDefinition, info: ClickContext, tab: chrome.tabs.Tab): Promise<string> {
var url;
if (info.mediaType === 'image') {
url = info.srcUrl;
Expand All @@ -65,13 +74,13 @@ class CreateLink {
return this.formatString(tab.id, def, url, text, title)
}

async formatString(tabId, def, url, text, title) {
async formatString(tabId: number, def: FormatDefinition, url: string, text: string, title: string): Promise<string> {
const inputs = await this.getInputs(def, tabId)
const linkText = this.formatLinkText(def, url, text, title, inputs)
return this.applyFilter(tabId, def, linkText)
}

indexOfFormatByLabel(label) {
indexOfFormatByLabel(label: string): number {
const formats = fmt.getFormats();
for (var i = 0, len = formats.length; i < len; i++) {
var item = formats[i];
Expand All @@ -84,21 +93,14 @@ class CreateLink {
};
}

function escapeHTML(text) {
function escapeHTML(text: string): string {
return text ? text.replace(/[&<>'"]/g, convertHTMLChar) : text;
}
function convertHTMLChar(c) { return charMap[c]; }
var charMap = {
function convertHTMLChar(c: string): string { return charMap[c]; }
const charMap: { [name: string]: string } = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
"'": '&apos;',
'"': '&quot;'
};
function showPrompt(text, pos, subject) {
var msg = "Please enter the input text for \n" + subject;
var s = window.prompt(msg);
return (s === null) ? "" : s;
}

module.exports = CreateLink
Loading

0 comments on commit f565bbf

Please # to comment.