├── src ├── ts │ ├── background.ts │ ├── types.ts │ ├── tooltip.ts │ ├── popup.ts │ ├── rosy.ts │ ├── data.ts │ └── content.ts ├── icons │ ├── icon16.png │ ├── icon24.png │ ├── icon32.png │ ├── icon48.png │ └── icon128.png ├── css │ ├── content.css │ └── popup.css ├── manifest.json ├── html │ └── popup.html └── js │ └── buttons.js ├── .gitignore ├── logo ├── logo.png └── square.png ├── tslint.json ├── tsconfig.json ├── README.md ├── LICENSE ├── package.json └── webpack.config.js /src/ts/background.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | node_modules/ 3 | dist/ 4 | rosy.zip -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizsid/Rosy/HEAD/logo/logo.png -------------------------------------------------------------------------------- /logo/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizsid/Rosy/HEAD/logo/square.png -------------------------------------------------------------------------------- /src/icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizsid/Rosy/HEAD/src/icons/icon16.png -------------------------------------------------------------------------------- /src/icons/icon24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizsid/Rosy/HEAD/src/icons/icon24.png -------------------------------------------------------------------------------- /src/icons/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizsid/Rosy/HEAD/src/icons/icon32.png -------------------------------------------------------------------------------- /src/icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizsid/Rosy/HEAD/src/icons/icon48.png -------------------------------------------------------------------------------- /src/icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whizsid/Rosy/HEAD/src/icons/icon128.png -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended", 5 | "tslint-config-prettier" 6 | ], 7 | "jsRules": {}, 8 | "rules": { 9 | "no-console": false 10 | }, 11 | "rulesDirectory": [] 12 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "commonjs", 5 | "noImplicitAny": true, 6 | "removeComments": true, 7 | "preserveConstEnums": true, 8 | "sourceMap": true 9 | }, 10 | "files": [ 11 | "src/ts/background.ts" 12 | ] 13 | } -------------------------------------------------------------------------------- /src/ts/types.ts: -------------------------------------------------------------------------------- 1 | export type RosyPosition = { 2 | powerOfTen:number; 3 | noun: string; 4 | prefix: string; 5 | } 6 | 7 | 8 | export type RosyNumber = { 9 | number: number; 10 | noun: string; 11 | prefix: string; 12 | plusTenNoun: string; 13 | plusTenPrefix: string; 14 | productTenPrefix:string; 15 | productTenNoun:string; 16 | } -------------------------------------------------------------------------------- /src/css/content.css: -------------------------------------------------------------------------------- 1 | .rosy-tooltip { 2 | position: absolute; 3 | background: #4285f4; 4 | color:#fff; 5 | font-size:1.2em; 6 | font-weight: 600; 7 | box-shadow: 1px 1px 2px rgba(0,0,0,0.5); 8 | margin-top: -60px; 9 | padding:8px; 10 | border-radius: 2px; 11 | transition-delay: .5s; 12 | transition-duration: .6s 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | --- 4 | 5 |

6 | License: MIT 7 | Rosy current version 8 |

9 | 10 | --- 11 | 12 | > Don't tell lies. Rosy can translate any long number to sinhala. 13 | 14 | Rosy is a firefox addon to helping persons to translate their numerical values to sinhala words. It will automatically translating numeric values in webpages and display the result in a cool tooltip box. You can also translate your own numbers with the help of simple popup. 15 | 16 | ![Rosy preview](https://i.imgur.com/JR0hBuJ.png) 17 | 18 | ## License 19 | 20 | You can copy any codes/algorythm from this repository and give credits when copying. 21 | 22 | ## Contribution 23 | 24 | All PRs and issues are welcome. 25 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Rosy Number Translator", 3 | "version": "1.1", 4 | "description": "Numbers to sinhala words converter", 5 | "permissions": ["storage"], 6 | "short_name":"Rosy", 7 | "content_scripts": [{ 8 | "run_at": "document_end", 9 | "all_frames": true, 10 | "matches": [ 11 | "http://*/*", 12 | "https://*/*", 13 | "file://*/*" 14 | ], 15 | "js": [ 16 | "content.js" 17 | ], 18 | "css": [ 19 | "content.css" 20 | ] 21 | }], 22 | "icons": { 23 | "16": "icon16.png", 24 | "48": "icon48.png", 25 | "128": "icon128.png" 26 | }, 27 | "manifest_version": 2, 28 | "browser_action": { 29 | "default_icon": { 30 | "16": "icon16.png", 31 | "24": "icon24.png", 32 | "32": "icon32.png" 33 | }, 34 | "default_title": "Translate your numbers to sinhala with Rosy.", 35 | "default_popup": "popup.html" 36 | } 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 WhizSid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rosy", 3 | "version": "1.0.0", 4 | "description": "A firefox extension to translate numerical values to sinhala words.", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/whizsid/Rosy.git" 9 | }, 10 | "keywords": [ 11 | "firefox", 12 | "numbers", 13 | "sinhala", 14 | "words", 15 | "translate", 16 | "unicode", 17 | "mouseover", 18 | "tooltip", 19 | "convert", 20 | "numeric" 21 | ], 22 | "author": "whizsid", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/whizsid/Rosy/issues" 26 | }, 27 | "homepage": "https://github.com/whizsid/Rosy#readme", 28 | "scripts": { 29 | "build": "NODE_ENV=production node ./node_modules/webpack-cli/bin/cli.js && zip -FS -r -j rosy dist", 30 | "dev": "node ./node_modules/webpack-cli/bin/cli.js --watch", 31 | "lint": "eslint --ignore-pattern dist ." 32 | }, 33 | "devDependencies": { 34 | "@babel/core": "^7.4.4", 35 | "@babel/plugin-transform-classes": "^7.4.4", 36 | "babel-loader": "^8.0.5", 37 | "clean-webpack-plugin": "^1.0.0", 38 | "copy-webpack-plugin": "^4.6.0", 39 | "eslint": "^5.11.0", 40 | "ts-loader": "^6.0.4", 41 | "typescript": "^3.5.3", 42 | "webextension-polyfill-ts": "^0.9.1", 43 | "webpack": "^4.28.2", 44 | "webpack-cli": "^3.1.2" 45 | }, 46 | "dependencies": { 47 | "tslint-config-prettier": "^1.18.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ts/tooltip.ts: -------------------------------------------------------------------------------- 1 | class Tooltip { 2 | 3 | protected htmlElement:HTMLElement; 4 | 5 | protected top:number; 6 | 7 | protected left:number; 8 | 9 | protected display:boolean; 10 | 11 | protected content:string; 12 | 13 | constructor(){ 14 | const htmlElement = document.createElement('DIV'); 15 | htmlElement.setAttribute('class','rosy-tooltip'); 16 | 17 | this.htmlElement = htmlElement; 18 | this.top = 0; 19 | this.left = 0; 20 | this.display = false; 21 | this.content = ""; 22 | 23 | document.body.appendChild(htmlElement); 24 | } 25 | 26 | public move(x:number,y:number):void{ 27 | this.left = x; 28 | this.top = y; 29 | this.render(); 30 | } 31 | 32 | public changeContent(content:string):void{ 33 | this.content = content; 34 | } 35 | 36 | public show():void{ 37 | this.display = true; 38 | this.render(); 39 | } 40 | 41 | public hide():void{ 42 | this.display = false; 43 | this.render(); 44 | } 45 | 46 | protected render(){ 47 | let style:string = ""; 48 | 49 | if(this.display){ 50 | style = "opacity:1;top:"+this.top+'px;left:'+this.left+'px'; 51 | } else { 52 | style = 'opacity:0'; 53 | } 54 | 55 | this.htmlElement.innerHTML = this.content; 56 | this.htmlElement.setAttribute('style',style); 57 | } 58 | } 59 | 60 | export default Tooltip; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 3 | const CleanWebpackPlugin = require("clean-webpack-plugin"); 4 | 5 | const mode = 6 | process.env.NODE_ENV === "production" ? "production" : "development"; 7 | 8 | const config = { 9 | devtool: "inline-source-map", 10 | mode, 11 | entry: { 12 | background: "./src/ts/background.ts", 13 | content: "./src/ts/content.ts", 14 | popup: "./src/ts/popup.ts", 15 | }, 16 | output: { 17 | filename: "[name].js", 18 | path: path.resolve(__dirname, "dist") 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.js$/, 24 | exclude: /node_modules/, 25 | use: { 26 | loader: "babel-loader", 27 | options: { 28 | plugins: ["@babel/plugin-transform-classes"] 29 | } 30 | } 31 | }, 32 | { 33 | test: /\.ts$/, 34 | exclude: /node_modules/, 35 | loader: "ts-loader" 36 | } 37 | ] 38 | }, 39 | resolve: { 40 | extensions: [".js", ".ts"] 41 | }, 42 | plugins: [ 43 | new CleanWebpackPlugin(["dist"]), 44 | new CopyWebpackPlugin([ 45 | "src/manifest.json", 46 | "src/icons/*.png", 47 | "src/html/popup.html", 48 | "src/css/popup.css", 49 | "src/css/content.css", 50 | "src/js/buttons.js", 51 | "logo/square.png" 52 | ], { 53 | to: "dist" 54 | }) 55 | ] 56 | }; 57 | 58 | module.exports = config; 59 | -------------------------------------------------------------------------------- /src/ts/popup.ts: -------------------------------------------------------------------------------- 1 | import { browser } from "webextension-polyfill-ts"; 2 | import Rosy from "./rosy"; 3 | 4 | const inputElement = document.getElementById("quickInput") as HTMLInputElement; 5 | const rowElement = document.getElementById("quickRow") as HTMLElement; 6 | const displayElement = document.getElementById("quickResult") as HTMLElement; 7 | 8 | if (inputElement) { 9 | const callback = function(this: HTMLInputElement) { 10 | const value: string = this.value; 11 | 12 | if (value.length > 0) { 13 | rowElement.setAttribute("style", "display:unset"); 14 | 15 | // tslint:disable-next-line: radix 16 | displayElement.innerHTML = Rosy(parseInt(value)); 17 | } else { 18 | rowElement.setAttribute("style", "display:none"); 19 | } 20 | }; 21 | 22 | inputElement.addEventListener("change", callback); 23 | inputElement.addEventListener("keyup", callback); 24 | } 25 | 26 | const switchButton = document.getElementById("switch-blue") as HTMLInputElement; 27 | browser.storage.local 28 | .get(["rosyEnabled"]) 29 | .then(({ rosyEnabled }: { rosyEnabled?: string }) => { 30 | // tslint:disable-next-line: radix 31 | let enabled = parseInt(rosyEnabled); 32 | 33 | if (enabled) { 34 | switchButton.checked = true; 35 | } else { 36 | switchButton.checked = false; 37 | } 38 | 39 | switchButton.addEventListener("change", function(this) { 40 | enabled = enabled ? 0 : 1; 41 | 42 | if (enabled) { 43 | browser.storage.local.set({ rosyEnabled: "1" }).then(() => { 44 | switchButton.checked = true; 45 | }); 46 | } else { 47 | browser.storage.local.set({ rosyEnabled: "0" }).then(() => { 48 | switchButton.checked = false; 49 | }); 50 | } 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /src/html/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rosy Popup 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | Star 15 |
16 | 17 | 18 | 21 | 25 | 26 | 27 | 33 | 34 | 35 | 42 | 43 | 44 | 45 | 46 | 47 | 55 | 56 |
19 | 20 | 22 |

Rosy

23 |
24 |
28 | 32 |
48 | 49 |
50 | 51 | 52 | 53 |
54 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/ts/rosy.ts: -------------------------------------------------------------------------------- 1 | import {numbers,positions} from "./data"; 2 | 3 | const Rosy = (translateMe:number,lastPrefix:boolean=false):string=>{ 4 | 5 | let translated:string =""; 6 | 7 | const powers:number[]= Object.keys(positions) 8 | // tslint:disable-next-line: radix 9 | .map((a:string):number=>parseInt(a)) 10 | .sort((a:number,b:number)=>b-a); 11 | 12 | let lastModedIndex:number=0; 13 | 14 | while(translateMe>0){ 15 | const position = positions[powers[lastModedIndex]]; 16 | 17 | const divided=Math.floor(translateMe/Math.pow(10,position.powerOfTen)); 18 | const moded:number = translateMe % Math.pow(10,position.powerOfTen); 19 | 20 | const prefix = lastPrefix? true : moded!==0||position.powerOfTen!==0; 21 | translateMe = moded; 22 | 23 | 24 | if(divided>99){ 25 | translated += Rosy(divided,true); 26 | } else if(divided>19){ 27 | 28 | const dividedByTen = Math.floor(divided/10); 29 | const modedByTen = divided%10; 30 | 31 | if(modedByTen>=1){ 32 | translated += numbers[dividedByTen].productTenPrefix+' '; 33 | } else { 34 | if(prefix){ 35 | translated += numbers[dividedByTen].productTenPrefix+' '; 36 | } else { 37 | translated += numbers[dividedByTen].productTenNoun; 38 | } 39 | } 40 | 41 | if(modedByTen>=1){ 42 | if(prefix){ 43 | translated += numbers[modedByTen].prefix+' '; 44 | } else { 45 | translated += numbers[modedByTen].noun; 46 | } 47 | } 48 | } else if (divided>9) { 49 | const modedByTen = divided%10; 50 | 51 | if(prefix) { 52 | translated += numbers[modedByTen].plusTenPrefix+' '; 53 | } else { 54 | translated += numbers[modedByTen].plusTenNoun; 55 | } 56 | 57 | } else if(divided>0&&!(divided===1&&position.powerOfTen!=0 &&moded===0&&translated==='')) { 58 | 59 | if(prefix){ 60 | translated += numbers[divided].prefix+' '; 61 | } else { 62 | translated += numbers[divided].noun; 63 | } 64 | 65 | } 66 | 67 | if(divided>0){ 68 | if(moded>0){ 69 | translated += position.prefix+' '; 70 | } else { 71 | translated += position.noun; 72 | } 73 | } 74 | lastModedIndex++; 75 | } 76 | 77 | return translated; 78 | } 79 | 80 | export default Rosy; 81 | -------------------------------------------------------------------------------- /src/css/popup.css: -------------------------------------------------------------------------------- 1 | body{ 2 | max-width: 460px; 3 | } 4 | 5 | h4 { 6 | margin: 0; 7 | } 8 | 9 | td { 10 | padding-left: 1em; 11 | padding-top: 0.2em; 12 | } 13 | 14 | tr { 15 | margin-top: 1em; 16 | } 17 | 18 | label { 19 | font-size: 0.7em; 20 | color: #404040; 21 | } 22 | .wrapper { 23 | position: relative; 24 | } 25 | 26 | .gh-button { 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | } 31 | 32 | .button-switch { 33 | font-size: 1.5em; 34 | height: 1em; 35 | margin-bottom: 0.625em; 36 | position: relative; 37 | width: 2.4em; 38 | } 39 | 40 | .button-switch .lbl-off, 41 | .button-switch .lbl-on { 42 | cursor: pointer; 43 | display: block; 44 | font-size: 0.9em; 45 | font-weight: bold; 46 | line-height: 1em; 47 | position: absolute; 48 | top: 0.5em; 49 | transition: opacity 0.25s ease-out 0.1s; 50 | text-transform: uppercase; 51 | } 52 | 53 | .button-switch .lbl-off { 54 | right: 0.4375em; 55 | } 56 | 57 | .button-switch .lbl-on { 58 | color: #fefefe; 59 | opacity: 0; 60 | left: 0.4375em; 61 | } 62 | 63 | .button-switch .switch { 64 | -webkit-appearance: none; 65 | -moz-appearance: none; 66 | appearance: none; 67 | height: 0; 68 | font-size: 1em; 69 | left: 0; 70 | line-height: 0; 71 | outline: none; 72 | position: absolute; 73 | top: 0; 74 | width: 0; 75 | } 76 | 77 | .button-switch .switch:before, 78 | .button-switch .switch:after { 79 | content: ""; 80 | font-size: 1em; 81 | position: absolute; 82 | } 83 | 84 | .button-switch .switch:before { 85 | border-radius: 1.25em; 86 | background: #bdc3c7; 87 | height: 1em; 88 | left: -0.25em; 89 | transition: background-color 0.25s ease-out 0.1s; 90 | width: 2.4em; 91 | } 92 | 93 | .button-switch .switch:after { 94 | box-shadow: 0 0.0625em 0.375em 0 #666; 95 | border-radius: 50%; 96 | background: #fefefe; 97 | height: 1em; 98 | transform: translate(0, 0); 99 | transition: transform 0.25s ease-out 0.1s; 100 | width: 1em; 101 | } 102 | 103 | .button-switch .switch:checked:after { 104 | transform: translate(1em, 0); 105 | } 106 | 107 | .button-switch .switch:checked ~ .lbl-off { 108 | opacity: 0; 109 | } 110 | 111 | .button-switch .switch:checked ~ .lbl-on { 112 | opacity: 1; 113 | } 114 | 115 | .button-switch .switch#switch-orange:checked:before { 116 | background: #e67e22; 117 | } 118 | 119 | .button-switch .switch#switch-blue:checked:before { 120 | background: #3498db; 121 | } 122 | -------------------------------------------------------------------------------- /src/ts/data.ts: -------------------------------------------------------------------------------- 1 | import { RosyNumber, RosyPosition } from "./types"; 2 | 3 | export const positions: { [x: number]: RosyPosition } = { 4 | 0: { 5 | noun: "", 6 | powerOfTen: 0, 7 | prefix: "" 8 | }, 9 | 2: { 10 | noun: "සියය", 11 | powerOfTen: 2, 12 | prefix: "සිය" 13 | }, 14 | 3: { 15 | noun: "දහස", 16 | powerOfTen: 3, 17 | prefix: "දහස්" 18 | }, 19 | 5: { 20 | noun: "ලක්ෂය", 21 | powerOfTen: 5, 22 | prefix: "ලක්ෂ" 23 | }, 24 | 6: { 25 | noun: "මිලියනය", 26 | powerOfTen: 6, 27 | prefix: "මිලියන" 28 | }, 29 | 7: { 30 | noun: "කෝටිය", 31 | powerOfTen: 7, 32 | prefix: "කෝටි" 33 | }, 34 | 9: { 35 | noun: "බිලියනය", 36 | powerOfTen: 9, 37 | prefix: "බිලියන" 38 | }, 39 | 12: { 40 | noun: "ත්‍රිලියනය", 41 | powerOfTen: 12, 42 | prefix: "ත්‍රිලියන" 43 | }, 44 | 15: { 45 | noun: "ක්වාඩ්‍රිලියනය", 46 | powerOfTen: 15, 47 | prefix: "ක්වාඩ්‍රිලියන" 48 | }, 49 | 18: { 50 | noun: "ක්වින්ටිලියනය", 51 | powerOfTen: 18, 52 | prefix: "ක්වින්ටිලියන" 53 | }, 54 | 21: { 55 | noun: "සෙක්ස්ටිලියනය", 56 | powerOfTen: 21, 57 | prefix: "සෙක්සිටිලියන" 58 | }, 59 | 24: { 60 | noun: "සෙප්ටිලියනය", 61 | powerOfTen: 24, 62 | prefix: "සෙප්ටිලියන" 63 | }, 64 | 27: { 65 | noun: "ඔක්ටිලියනය", 66 | powerOfTen: 27, 67 | prefix: "ඔක්ටිලියන" 68 | }, 69 | 30: { 70 | noun: "නොලියනය", 71 | powerOfTen: 30, 72 | prefix: "නොලියන" 73 | }, 74 | 33: { 75 | noun: "ඩෙසිලියනය", 76 | powerOfTen: 33, 77 | prefix: "ඩෙසිලියන" 78 | } 79 | }; 80 | 81 | export const numbers: { [x: number]: RosyNumber } = { 82 | 0: { 83 | noun: "බිංදුව", 84 | number: 0, 85 | plusTenNoun: "දහය", 86 | plusTenPrefix: "දස", 87 | prefix: "", 88 | productTenNoun: "බිංදුව", 89 | productTenPrefix: "" 90 | }, 91 | 1: { 92 | noun: "එක", 93 | number: 1, 94 | plusTenNoun: "එකොළහ", 95 | plusTenPrefix: "එකොළොස්", 96 | prefix: "එක්", 97 | productTenNoun: "දහය", 98 | productTenPrefix: "දස" 99 | }, 100 | 2: { 101 | noun: "දෙක", 102 | number: 2, 103 | plusTenNoun: "දොළහ", 104 | plusTenPrefix: "දොළොස්", 105 | prefix: "දෙ", 106 | productTenNoun: "විස්ස", 107 | productTenPrefix: "විසි" 108 | }, 109 | 3: { 110 | noun: "තුන", 111 | number: 3, 112 | plusTenNoun: "දහතුන", 113 | plusTenPrefix: "දහතුන්", 114 | prefix: "තුන්", 115 | productTenNoun: "තිහ", 116 | productTenPrefix: "තිස්" 117 | }, 118 | 4: { 119 | noun: "හතර", 120 | number: 4, 121 | plusTenNoun: "දහ හතර", 122 | plusTenPrefix: "දහ හතර", 123 | prefix: "හාර", 124 | productTenNoun: "හතළිහ", 125 | productTenPrefix: "හතළිස්" 126 | }, 127 | 5: { 128 | noun: "පහ", 129 | number: 5, 130 | plusTenNoun: "පහළොව", 131 | plusTenPrefix: "පහළොස්", 132 | prefix: "පන්", 133 | productTenNoun: "පනහ", 134 | productTenPrefix: "පනස්" 135 | }, 136 | 6: { 137 | noun: "හය", 138 | number: 6, 139 | plusTenNoun: "දහසය", 140 | plusTenPrefix: "දහසය", 141 | prefix: "හය", 142 | productTenNoun: "හැට", 143 | productTenPrefix: "හැට" 144 | }, 145 | 7: { 146 | noun: "හත", 147 | number: 7, 148 | plusTenNoun: "දහ හත", 149 | plusTenPrefix: "දහ හත්", 150 | prefix: "හත්", 151 | productTenNoun: "හැත්තෑව", 152 | productTenPrefix: "හැත්තෑ" 153 | }, 154 | 8: { 155 | noun: "අට", 156 | number: 8, 157 | plusTenNoun: "දහ අට", 158 | plusTenPrefix: "දහ අට", 159 | prefix: "අට", 160 | productTenNoun: "අසූව", 161 | productTenPrefix: "අසූ" 162 | }, 163 | 9: { 164 | noun: "නවය", 165 | number: 9, 166 | plusTenNoun: "දහ නවය", 167 | plusTenPrefix: "දහ නව", 168 | prefix: "නව", 169 | productTenNoun: "අනූව", 170 | productTenPrefix: "අනූ" 171 | } 172 | }; 173 | -------------------------------------------------------------------------------- /src/ts/content.ts: -------------------------------------------------------------------------------- 1 | import { browser } from "webextension-polyfill-ts"; 2 | import Rosy from "./rosy"; 3 | import Tooltip from "./tooltip"; 4 | 5 | interface IFoundWord { 6 | word: string; 7 | start: number; 8 | rect: ClientRect | DOMRect; 9 | } 10 | 11 | /** 12 | * Returning the word by x,y coordinates 13 | * 14 | * @link https://jsfiddle.net/abrady0/ggr5mu7o/ 15 | */ 16 | const getWordByCoordinates = ( 17 | parentElt: ChildNode, 18 | x: number, 19 | y: number 20 | ): IFoundWord | null => { 21 | if (parentElt.nodeName !== "#text") { 22 | return null; 23 | } 24 | 25 | const range: Range = document.createRange(); 26 | const words: string[] = parentElt.textContent.split(" "); 27 | let start: number = 0; 28 | let end: number = 0; 29 | 30 | const isInRect = ( 31 | rects: ClientRectList | DOMRectList 32 | ): ClientRect | DOMRect | null => { 33 | for (const r of rects) { 34 | if (r.left < x && r.right > x && r.top < y && r.bottom > y) { 35 | return r; 36 | } 37 | } 38 | return null; 39 | }; 40 | 41 | for (const word of words) { 42 | end = start + word.length; 43 | 44 | range.setStart(parentElt, start); 45 | 46 | range.setEnd(parentElt, end); 47 | 48 | // not getBoundingClientRect as word could wrap 49 | const rects = range.getClientRects(); 50 | 51 | const clickedRect = isInRect(rects); 52 | if (clickedRect) { 53 | return { 54 | rect: clickedRect, 55 | start, 56 | word 57 | }; 58 | } 59 | start = end + 1; 60 | } 61 | 62 | return null; 63 | }; 64 | 65 | // const pattern = /(\d+)/g; 66 | 67 | const tags: string[] = [ 68 | "p", 69 | "a", 70 | "b", 71 | "i", 72 | "h1", 73 | "h2", 74 | "h3", 75 | "h4", 76 | "h5", 77 | "h6", 78 | "span", 79 | "em", 80 | "div", 81 | "strong", 82 | "td", 83 | "th", 84 | "li", 85 | "code", 86 | "q", 87 | "s", 88 | "pre", 89 | "small", 90 | "textarea", 91 | "title" 92 | ]; 93 | 94 | const pattern = /^([0-9\,\)\(]+[0-9]+[0-9\,\)\(]+)$/; 95 | 96 | const tooltip = new Tooltip(); 97 | 98 | for (const tag of tags) { 99 | const elmnts = document.getElementsByTagName(tag) as HTMLCollectionOf< 100 | HTMLElement 101 | >; 102 | 103 | for (const elmnt of elmnts) { 104 | let child: ChildNode | undefined; 105 | 106 | const textNodes: ChildNode[] = []; 107 | 108 | for (child = elmnt.firstChild; child !== null; child = child.nextSibling) { 109 | if (child.nodeName === "#text") { 110 | textNodes.push(child); 111 | } 112 | } 113 | 114 | if (textNodes.length) { 115 | elmnt.addEventListener("mousemove", function( 116 | this: Element, 117 | e: MouseEvent 118 | ) { 119 | 120 | browser.storage.local 121 | .get(["rosyEnabled"]) 122 | .then(({ rosyEnabled }: { rosyEnabled?: string }) => { 123 | // tslint:disable-next-line: radix 124 | if (parseInt(rosyEnabled)) { 125 | let hoveredWord: IFoundWord | null = null; 126 | 127 | const x: number = e.pageX; 128 | const y: number = e.pageY; 129 | 130 | tooltip.move(x, y); 131 | 132 | for (const textNode of textNodes) { 133 | if (!hoveredWord) { 134 | hoveredWord = getWordByCoordinates( 135 | textNode, 136 | e.clientX, 137 | e.clientY 138 | ); 139 | } 140 | } 141 | 142 | if (hoveredWord) { 143 | if (pattern.test(hoveredWord.word)) { 144 | tooltip.changeContent( 145 | // tslint:disable-next-line: radix 146 | Rosy(parseInt(hoveredWord.word.replace(/([^0-9]+)/g, ""))) 147 | ); 148 | tooltip.show(); 149 | } 150 | } 151 | } 152 | }); 153 | }); 154 | 155 | elmnt.addEventListener("mouseleave", () => { 156 | tooltip.hide(); 157 | }); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/js/buttons.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * github-buttons v2.2.10 3 | * (c) 2019 なつき 4 | * @license BSD-2-Clause 5 | */ 6 | !function(){"use strict";var e=window.document,t=e.location,o=window.encodeURIComponent,r=window.decodeURIComponent,n=window.Math,a=window.HTMLElement,i=window.XMLHttpRequest,l="https://buttons.github.io/buttons.html",c=i&&i.prototype&&"withCredentials"in i.prototype,d=c&&a&&a.prototype.attachShadow&&!a.prototype.attachShadow.prototype,s=function(e,t,o){e.addEventListener?e.addEventListener(t,o):e.attachEvent("on"+t,o)},u=function(e,t,o){e.removeEventListener?e.removeEventListener(t,o):e.detachEvent("on"+t,o)},h=function(e,t,o){var r=function(n){return u(e,t,r),o(n)};s(e,t,r)},f=function(e,t,o){var r=function(n){if(t.test(e.readyState))return u(e,"readystatechange",r),o(n)};s(e,"readystatechange",r)},p=function(e){return function(t,o,r){var n=e.createElement(t);if(o)for(var a in o){var i=o[a];null!=i&&(null!=n[a]?n[a]=i:n.setAttribute(a,i))}if(r)for(var l=0,c=r.length;l'},eye:{width:16,height:16,path:''},star:{width:14,height:16,path:''},"repo-forked":{width:10,height:16,path:''},"issue-opened":{width:14,height:16,path:''},"cloud-download":{width:16,height:16,path:''}},w={},x=function(e,t,o){var r=p(e.ownerDocument),n=e.appendChild(r("style",{type:"text/css"}));n.styleSheet?n.styleSheet.cssText=m:n.appendChild(e.ownerDocument.createTextNode(m));var a,l,d=r("a",{className:"btn",href:t.href,target:"_blank",innerHTML:(a=t["data-icon"],l=/^large$/i.test(t["data-size"])?16:14,a=(""+a).toLowerCase().replace(/^octicon-/,""),{}.hasOwnProperty.call(v,a)||(a="mark-github"),'"),"aria-label":t["aria-label"]||void 0},[" ",r("span",{},[t["data-text"]||""])]);/\.github\.com$/.test("."+d.hostname)?/^https?:\/\/((gist\.)?github\.com\/[^\/?#]+\/[^\/?#]+\/archive\/|github\.com\/[^\/?#]+\/[^\/?#]+\/releases\/download\/|codeload\.github\.com\/)/.test(d.href)&&(d.target="_top"):(d.href="#",d.target="_self");var u,h,g,x,y=e.appendChild(r("div",{className:"widget"+(/^large$/i.test(t["data-size"])?" lg":"")},[d]));/^(true|1)$/i.test(t["data-show-count"])&&"github.com"===d.hostname&&(u=d.pathname.replace(/^(?!\/)/,"/").match(/^\/([^\/?#]+)(?:\/([^\/?#]+)(?:\/(?:(subscription)|(fork)|(issues)|([^\/?#]+)))?)?(?:[\/?#]|$)/))&&!u[6]?(u[2]?(h="/repos/"+u[1]+"/"+u[2],u[3]?(x="subscribers_count",g="watchers"):u[4]?(x="forks_count",g="network"):u[5]?(x="open_issues_count",g="issues"):(x="stargazers_count",g="stargazers")):(h="/users/"+u[1],g=x="followers"),function(e,t){var o=w[e]||(w[e]=[]);if(!(o.push(t)>1)){var r=b(function(){for(delete w[e];t=o.shift();)t.apply(null,arguments)});if(c){var n=new i;s(n,"abort",r),s(n,"error",r),s(n,"load",function(){var e;try{e=JSON.parse(n.responseText)}catch(e){return void r(e)}r(200!==n.status,e)}),n.open("GET",e),n.send()}else{var a=this||window;a._=function(e){a._=null,r(200!==e.meta.status,e.data)};var l=p(a.document)("script",{async:!0,src:e+(/\?/.test(e)?"&":"?")+"callback=_"}),d=function(){a._&&a._({meta:{}})};s(l,"load",d),s(l,"error",d),l.readyState&&f(l,/de|m/,d),a.document.getElementsByTagName("head")[0].appendChild(l)}}}.call(this,"https://api.github.com"+h,function(e,t){if(!e){var n=t[x];y.appendChild(r("a",{className:"social-count",href:t.html_url+"/"+g,target:"_blank","aria-label":n+" "+x.replace(/_count$/,"").replace("_"," ").slice(0,n<2?-1:void 0)+" on GitHub"},[r("b"),r("i"),r("span",{},[(""+n).replace(/\B(?=(\d{3})+(?!\d))/g,",")])]))}o&&o(y)})):o&&o(y)},y=window.devicePixelRatio||1,C=function(e){return(y>1?n.ceil(n.round(e*y)/y*2)/2:n.ceil(e))||0},F=function(e,t){e.style.width=t[0]+"px",e.style.height=t[1]+"px"},k=function(t,r){if(null!=t&&null!=r)if(t.getAttribute&&(t=function(e){for(var t={href:e.href,title:e.title,"aria-label":e.getAttribute("aria-label")},o=["icon","text","size","show-count"],r=0,n=o.length;r