├── .gitignore
├── LICENSE
├── README.md
├── background.js
├── build-ext.sh
├── icons
├── loriko-64.png
├── loriko.svg
└── penguin-mono.svg
├── lorify-ng.user.js
├── manifest.json
├── settings.html
└── settings.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | dist
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OpenA
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Расширение для сайта [linux.org.ru](https://www.linux.org.ru/)
2 | Это форк проекта [lorify](https://bitbucket.org/b0r3d0m/lorify) использующий новые возможности движка форума такие как WebSocket.
3 | Является полностью универсальным, можно использовать и всё расширение целиком и отдельный юзерскрипт.
4 |
5 | |
6 | ------------ | -------------
7 |
--------------------------------------------------------------------------------
/background.js:
--------------------------------------------------------------------------------
1 | /* lorify-ng background script */
2 | const defaults = Object.freeze({ // default settings
3 | 'Realtime Loader' : true,
4 | 'CSS3 Animation' : true,
5 | 'Delay Open Preview' : 50,
6 | 'Delay Close Preview' : 800,
7 | 'Desktop Notification' : 1,
8 | 'Preloaded Pages Count': 1,
9 | 'Picture Viewer' : 2,
10 | 'Scroll Top View' : true,
11 | 'Upload Post Delay' : 3,
12 | 'Code Block Short Size': 15,
13 | 'Code Highlight Style' : 0
14 | });
15 |
16 | let notes = 0, codestyles = null,
17 | wss = 0, need_login = false;
18 |
19 | const settings = Object.assign({}, defaults);
20 | const openPorts = new Set;
21 | const isFirefox = navigator.userAgent.includes('Firefox');
22 | const loadStore = isFirefox ?
23 | // load settings
24 | browser.storage.local.get() : new Promise(resolve => {
25 | chrome.storage.local.get(null, resolve)
26 | });
27 | loadStore.then(items => {
28 | for (const key in items) {
29 | settings[key] = items[key];
30 | }
31 | setNotifCheck();
32 | });
33 |
34 | chrome.notifications.onClicked.addListener(() => {
35 | const ismob = Number(settings['Desktop Notification']) === 2;
36 | openTab(`${ ismob ? '#' : 'lor://' }notifications`,
37 | ismob ? 'notes-show' : 'rel');
38 | });
39 | chrome.runtime.onConnect.addListener(port => {
40 | if (port.name === 'lory-wss') {
41 | chrome.alarms.clear('T-chk-notes');
42 | }
43 | port.onMessage.addListener(messageHandler);
44 | port.onDisconnect.addListener(() => {
45 | openPorts.delete(port);
46 | for(const p of openPorts) {
47 | if (p.name === 'lory-wss')
48 | return;
49 | }
50 | setNotifCheck(5e4);
51 | });
52 | openPorts.add(port);
53 | loadStore.then(() => port.postMessage({ action: 'connection-resolve', data: settings }));
54 | if (!codestyles)
55 | port.postMessage({ action: 'need-codestyles', data: null });
56 | });
57 |
58 | const setBadge = chrome.browserAction && chrome.browserAction.setBadgeText ? (
59 | badge => {
60 | if ('setBadgeTextColor' in badge) {
61 | badge.setBadgeTextColor({ color: '#ffffff' });
62 | }
63 | badge.setBadgeBackgroundColor({ color: '#3d96ab' });
64 | badge.getBadgeText({}, label => {
65 | if (label > 0)
66 | notes = Number(label);
67 | });
68 | return opts => {
69 | if ('text' in opts) badge.setBadgeText(opts); else
70 | if ('color' in opts) badge.setBadgeBackgroundColor(opts);
71 | }
72 | }
73 | )(chrome.browserAction) : () => void 0;
74 |
75 | chrome.alarms.onAlarm.addListener(getNotifications);
76 |
77 | function setNotifCheck(sec = 4e3) {
78 | if (settings['Desktop Notification']) {
79 | chrome.alarms.create('T-chk-notes', {
80 | when: Date.now() + sec, periodInMinutes: 4
81 | });
82 | }
83 | }
84 |
85 | const queryScheme = isFirefox
86 | ? url => browser.tabs.query({ url })
87 | : url => new Promise(res => void chrome.tabs.query({ url }, res))
88 |
89 | const matchEmptyOr = (tabs, m_uri = '', m_rx = '') => new Promise(resolve => {
90 |
91 | const empty_Rx = new RegExp(m_rx ? m_rx : '^'+
92 | (isFirefox ? 'about:(?:newtab|home)$' : 'chrome://.*(?:newtab|startpage)') +
93 | '');
94 |
95 | let tab_id = -1;
96 | for (const { id, url } of tabs) {
97 | if (m_uri && url.includes(m_uri)) {
98 | tab_id = id;
99 | break;
100 | } else if (empty_Rx.test(url))
101 | tab_id = id;
102 | }
103 | resolve(tab_id);
104 | });
105 |
106 | function openTab(uri = '', action = '') {
107 |
108 | let schi = uri.search(/\?|\#/);
109 | if (schi === -1)
110 | schi = uri.length;
111 |
112 | const lor = uri.startsWith('lor:/') ? 5 : 0,
113 | path = uri.substring(lor, schi),
114 | orig = lor ? '://www.linux.org.ru'+ path : chrome.runtime.getURL('/settings.html'),
115 | href = lor ? 'https' + orig + uri.substring(schi) : orig + uri;
116 |
117 | for (const port of openPorts) {
118 | const { url, id } = port.sender.tab || '';
119 | if (url && url.includes(orig)) {
120 | chrome.tabs.update(id, { active: true });
121 | if (action === 'rel') {
122 | chrome.tabs.reload(id);
123 | } else
124 | port.postMessage({ action, data: uri.substring(lor) });
125 | return;
126 | }
127 | }
128 | const fin = tab_id => {
129 | if (tab_id !== -1 ) {
130 | chrome.tabs.update(tab_id, { active: true, url: href });
131 | chrome.tabs.reload(tab_id);
132 | } else
133 | chrome.tabs.create({ active: true, url: href });
134 | }
135 | if (lor) {
136 | queryScheme(['*://www.linux.org.ru/', '*'+ orig +'*']).then(
137 | tabs => matchEmptyOr(tabs, orig, '^https?://www.linux.org.ru/?$')
138 | ).then(lor_id => {
139 | if(lor_id !== -1) {
140 | fin(lor_id);
141 | } else
142 | queryScheme().then(matchEmptyOr).then(fin);
143 | });
144 | } else {
145 | queryScheme().then(tabs => matchEmptyOr(tabs, orig)).then(fin);
146 | }
147 | }
148 |
149 | function messageHandler({ action, data }, port) {
150 | // check
151 | switch (action) {
152 | case 'l0rNG-setts-reset':
153 | changeSettings(defaults);
154 | break;
155 | case 'l0rNG-setts-change':
156 | changeSettings(data, port);
157 | break;
158 | case 'l0rNG-notes-chk':
159 | chrome.alarms.clear('Q-chk-notes');
160 | chrome.alarms.create('Q-chk-notes', {
161 | when: Date.now() + 1e3
162 | });
163 | break;
164 | case 'l0rNG-codestyles':
165 | if(!codestyles)
166 | codestyles = data;
167 | break;
168 | case 'l0rNG-open-tab':
169 | openTab(data, 'scroll-to-comment');
170 | break;
171 | case 'l0rNG-notes-set':
172 | if ( notes < data || notes > data ) {
173 | chrome.alarms.clear('Q-chk-notes');
174 | updNoteStatus(data);
175 | }
176 | break;
177 | case 'l0rNG-extra-sets':
178 | if (codestyles)
179 | port.postMessage({ action: 'code-styles-list', data: codestyles });
180 | if (notes)
181 | port.postMessage({ action: 'notes-count-update', data: notes });
182 | }
183 | }
184 |
185 | function getNotifications(alm) {
186 |
187 | fetch('https://www.linux.org.ru/notifications-count', {
188 | credentials: 'same-origin',
189 | method: 'GET'
190 | }).then(
191 | response => {
192 | if (response.ok) {
193 | response.json().then( count => {
194 | if ( notes < count || notes > count )
195 | updNoteStatus(count);
196 | });
197 | } else if (response.status === 403) {
198 | chrome.alarms.clearAll();
199 | need_login = true;
200 | }
201 | }
202 | );
203 | }
204 |
205 | function updNoteStatus(count = 0) {
206 | if ( count > notes && settings['Desktop Notification'] ) {
207 | chrome.notifications.create('lorify-ng', {
208 | type : 'basic',
209 | title : 'LINUX.ORG.RU',
210 | message : count +' новых сообщений',
211 | iconUrl : 'icons/penguin-mono.svg'
212 | });
213 | }
214 | setBadge({ text: (notes = count) ? count.toString() : '' });
215 | for (const port of openPorts) {
216 | port.postMessage({ action: 'notes-count-update', data: count });
217 | }
218 | }
219 | function changeSettings(newSetts, exclupe = null) {
220 | let hasChecks = 0;
221 | for (const port of openPorts) {
222 | hasChecks += Number(port.name === 'lory-wss');
223 | if ( port !== exclupe )
224 | port.postMessage({ action: 'settings-change', data: newSetts });
225 | }
226 | Object.assign(settings, newSetts);
227 | if (!hasChecks)
228 | setNotifCheck();
229 | chrome.storage.local.set(newSetts);
230 | }
231 |
--------------------------------------------------------------------------------
/build-ext.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | SRC="lorify-ng.user.js settings.html settings.js background.js LICENSE icons/*"
4 | ARC="dist/lorify-ng.zip"
5 | VER="2"
6 |
7 | for i in "$@"; do
8 | case $i in
9 | --help | -h)
10 | echo ""
11 | echo " build-ext.sh [option]\n"
12 | echo " -v3 Manifest V3"
13 | echo " -v2 Manifest V2 (default)"
14 | echo ""
15 | exit
16 | ;;
17 | -v3)
18 | VER="3"
19 | ;;
20 | *)
21 | # unknown option
22 | ;;
23 | esac
24 | done
25 |
26 | mkdir -p dist
27 | echo "\nbuilding:\033[0;9$VER;49m WebExt Manifest V$VER \033[0m"
28 |
29 | if [ "$VER" = "2" ]; then
30 | sed -e 's/"manifest_version":.*3/"manifest_version": 2/' \
31 | -e 's/"action"/"browser_action"/' \
32 | -e 's/"service_worker"/"scripts"/' \
33 | -e 's/"background.js"/["background.js"], "persistent": true/' \
34 | -e 's/\],.*"permissions":.*\[/,/' \
35 | -e 's/"host_permissions"/"permissions"/' \
36 | manifest.json > dist/manifest.json
37 | zip -9 -jm $ARC dist/manifest.json
38 | else
39 | SRC="manifest.json $SRC"
40 | fi
41 |
42 | zip -9 -T $ARC $SRC
43 |
--------------------------------------------------------------------------------
/icons/loriko-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenA/lorify-ng/2aae9c11020631b8479b55925000e99d2d8cca7b/icons/loriko-64.png
--------------------------------------------------------------------------------
/icons/loriko.svg:
--------------------------------------------------------------------------------
1 |
2 |
132 |
--------------------------------------------------------------------------------
/icons/penguin-mono.svg:
--------------------------------------------------------------------------------
1 |
2 |
93 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "lorify-ng",
4 | "version": "3.3.8",
5 | "description": "Расширение для сайта linux.org.ru поддерживающее загрузку комментариев через технологию WebSocket, а так же уведомления об ответах через системные оповещения и многое другое.",
6 | "options_ui": {
7 | "page": "settings.html"
8 | },
9 | "action": {
10 | "default_icon": "icons/loriko-64.png",
11 | "default_popup": "settings.html"
12 | },
13 | "background": {
14 | "service_worker": "background.js"
15 | },
16 | "icons": {
17 | "64": "icons/loriko-64.png"
18 | },
19 | "content_scripts": [{
20 | "run_at": "document_start",
21 | "matches": ["*://www.linux.org.ru/*"],
22 | "js": ["lorify-ng.user.js"]
23 | }],
24 | "host_permissions": [
25 | "*://www.linux.org.ru/*"
26 | ], "permissions": [
27 | "notifications",
28 | "storage",
29 | "alarms",
30 | "tabs"
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/settings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |