├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── _build ├── README.md └── merge.bg.v3.bat └── extension ├── LICENSE ├── README.md ├── _locales ├── en │ └── messages.json └── ru │ └── messages.json ├── env ├── css │ ├── core.css │ ├── coreMobile.css │ ├── dark.css │ ├── darkRecorderPopup.css │ ├── recorder.css │ ├── recorderDownloader.css │ ├── recorderPopup.css │ ├── single.css │ ├── singleMobile.css │ └── white.css ├── dynamic │ └── dispetcher.js ├── html │ ├── additions │ │ ├── menu-item.html │ │ ├── page.html │ │ ├── profile-selector-recorder.html │ │ └── profile-selector.html │ ├── bookmarksParser │ │ └── parser-form.html │ ├── options.html │ ├── recorderDownloader.html │ ├── recorderPopup.html │ ├── update.html │ └── update_default.html ├── img │ ├── arial-yellow.png │ ├── cup.png │ ├── heartr.png │ ├── icon128x128.png │ ├── icon32x32.png │ ├── icon44x44.png │ └── icon50x50.png └── init │ ├── background.js │ ├── commonAbout.js │ ├── recorderDownloader.js │ ├── recorderFront.js │ └── recorderPopup.js ├── lib ├── kellyAdditionsForm.js ├── kellyCAbout.js ├── kellyDispetcher.js ├── kellyDispetcherNetRequest.js ├── kellyFastSave.js ├── kellyFavItems.js ├── kellyGrabber.js ├── kellyLoc.js ├── kellyNradiowaveBg.js ├── kellyOptions.js ├── kellyStorageManager.js ├── kellyThreadWork.js ├── kellyToolbar.js ├── kellyTools.js ├── profiles │ ├── default.js │ └── recorder.js └── recorder │ ├── filters │ ├── 9gag.js │ ├── README.md │ ├── _validators.js │ ├── artstation.js │ ├── bsky.js │ ├── deviantart.js │ ├── discord.js │ ├── ehentai.js │ ├── flickr.js │ ├── hfoundry.js │ ├── instagram.js │ ├── joyreactor.js │ ├── kemonoparty.js │ ├── pikabu.js │ ├── pixiv.js │ ├── reddit.js │ ├── twitter.js │ ├── vk.js │ └── yandex.js │ ├── kellyDPage.js │ ├── kellyEDRecorder.js │ ├── kellyLoadDocControll.js │ └── kellyPageWatchdog.js ├── manifest.v2.all.json ├── manifest.v3.all.json └── widget ├── kellyImageView.js ├── kellyTileGrid.js └── kellyTooltip.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/.github/FUNDING.yml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

KellyC Image Downloader

2 | 3 |

Due to low interest in the project, the project has been removed from all extension stores and left for personal use as an open source project. The project remains free and open source, any forks \ modifications are welcome

4 |

Из за низкого интереса к проекту, проект убран со всех магазинов расширений и оставлен для личного использования в качестве открытого проекта. Проект остается бесплатным и открытым, любые изменения под себя приветствуются

5 | 6 | 9 | 10 |

11 | 14 |

Manual install

15 |

How to install manualy from source code or from releases

16 |

17 |

18 |

EN | Description

19 |

A browser extension for batch downloading artworks and images from sites. It allows you to download all images on the page filtering by size or other settings.

20 |

Some of the sites (priority to Pixiv, Pinterest, Twitter, Joyreactor) support extended features for collect "Original" version of images from preview automatically without use filters by size manually.

21 |

"Load related document" - feature allows you to download additional images from related documents of preview, if original image is not actually placed on current page. (may be helpful in some cases, check if site is supported)

22 | 23 | 24 |

25 |

RU | Описание

26 |

Расширение для пакетного скачивания картинок \ творчества с любых сайтов. Поддерживаются фильтры по пропорциям, строке url, загрузка дочерних документов.

27 |

28 |

Некоторые сайты поддерживают дополнительный функционал, позволяющий быстро захватить оригиналы изображений без дополнительной фильтрации в ручную.

29 |

Функция "Загрузка дочерних документов" используется если на основной странице доступно только изображение с низким разрешением и ссылка ведет на страницу с оригиналом. (функционал поумолчанию может не всегда сработать, см. страницу поддержки сайтов)

30 |

31 |

32 | 33 |

34 |

License

35 |
36 | GNU General Public License v3 37 |
38 |
-------------------------------------------------------------------------------- /_build/README.md: -------------------------------------------------------------------------------- 1 |

KellyC Image Downloader

2 |

3 | merge.bg.v3.bat is used to merge all js files required for background service worker in to one background.js if you are using manifest v3 4 |

5 | Use manifest.v[2-3].all.json from main extension folder as main manifest.json file, remove optional libs if needed 6 |

7 | Optional libs :

8 | "lib/profiles/joyreactor.js", "lib/profiles/joyreactor.unlock.js", "lib/profiles/topjoyreactor.js", "env/init/joyreactorFront.js", "lib/profiles/joyreactor.unlock.d.js" -------------------------------------------------------------------------------- /_build/merge.bg.v3.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | pushd "%~1" 3 | 4 | SET background=..\extension\background.js 5 | 6 | break>%background% 7 | 8 | for %%x in ( 9 | "%~dp0..\extension\lib\kellyTools.js" 10 | "%~dp0..\extension\lib\kellyDispetcher.js" 11 | "%~dp0..\extension\lib\kellyDispetcherNetRequest.js" 12 | "%~dp0..\extension\lib\profiles\joyreactor.unlock.d.js" 13 | "%~dp0..\extension\lib\recorder\kellyEDRecorder.js" 14 | "%~dp0..\extension\env\init\background.js" 15 | ) do ( 16 | 17 | @echo.>> %background% 18 | @echo.>> %background% 19 | copy %background% + "%%~x" %background% 20 | ) 21 | 22 | 23 | @echo.>> %background% 24 | @echo.>> %background% 25 | @echo.>> %background% 26 | @echo.>> %background% 27 | 28 | popd 29 | ::pause -------------------------------------------------------------------------------- /extension/README.md: -------------------------------------------------------------------------------- 1 |

KellyC Image Downloader

2 |
3 |
4 |

Ready to use extension build for browsers with support manifest v2, v3

-------------------------------------------------------------------------------- /extension/env/css/coreMobile.css: -------------------------------------------------------------------------------- 1 | /* mobile version */ 2 | 3 | @media (max-width: 1040px) { 4 | 5 | .__BASECLASS__-mobile .__BASECLASS__-tab { 6 | padding-top : 0; 7 | padding-left : 0; 8 | } 9 | 10 | .__BASECLASS__-mobile .__BASECLASS__-pointer-arrow { 11 | display : none; 12 | } 13 | 14 | .__BASECLASS__-mobile .__BASECLASS__-tooltipster-wrap { 15 | padding: 12px; 16 | } 17 | 18 | .__BASECLASS__-mobile .__BASECLASS__-tab-list, .__BASECLASS__-tab { 19 | padding: 0; 20 | padding-bottom : 0px; 21 | } 22 | 23 | .__BASECLASS__-mobile .__BASECLASS__-tab-list ul li { 24 | width : auto; 25 | border-radius: 0; 26 | line-height: 32px; 27 | height: 32px; 28 | padding : 0 16px; 29 | } 30 | 31 | .__BASECLASS__-mobile .__BASECLASS__-tab-list ul li a { 32 | padding-top: 0; 33 | } 34 | 35 | .__BASECLASS__-mobile .__BASECLASS__-copyright-info { 36 | font-size: 15px; 37 | } 38 | 39 | .__BASECLASS__-mobile .__BASECLASS__-copyright-info a { 40 | font-size: 15px; 41 | } 42 | 43 | .__BASECLASS__-mobile .__BASECLASS__-DBItem-name { 44 | min-width : unset; 45 | } 46 | 47 | .__BASECLASS__-mobile .__BASECLASS__-DBItem-size { 48 | min-width : unset; 49 | } 50 | 51 | .__BASECLASS__-mobile .__BASECLASS__-DBItem a.__BASECLASS__-right { 52 | margin-right : 4px; 53 | } 54 | 55 | .__BASECLASS__-mobile .__BASECLASS__-DBItem.active:after { 56 | display : none; 57 | } 58 | 59 | .__BASECLASS__-mobile .__BASECLASS__-DBItem a, .__BASECLASS__-DBItem span { 60 | margin-right : 8px; 61 | } 62 | 63 | .__BASECLASS__-mobile .__BASECLASS__-ModalBox .__BASECLASS__-ModalBox-content { 64 | min-height : 0px; 65 | } 66 | 67 | .__BASECLASS__-mobile .__BASECLASS__-exporter-buttons a { 68 | margin-right: 16px; 69 | display: block; 70 | } 71 | } 72 | 73 | @media (max-width: 800px) { 74 | 75 | .__BASECLASS__-mobile .__BASECLASS__-copyright-info { 76 | display: block; 77 | font-size: 15px; 78 | /* font-weight: bold; */ 79 | float: none; 80 | margin-left: 14px; 81 | margin-bottom: 25px; 82 | margin-top: 0; 83 | } 84 | 85 | .__BASECLASS__-mobile #copyright-name { 86 | font-size: 18px; 87 | } 88 | 89 | .__BASECLASS__-mobile #copyright-software a, #copyright-software { 90 | text-align : left; 91 | } 92 | } 93 | 94 | @media (max-width: 590px) { 95 | 96 | .__BASECLASS__-mobile .__BASECLASS__-tab td { 97 | display: block; 98 | width: 100%!important; 99 | margin: 0; 100 | } 101 | 102 | .__BASECLASS__-mobile .__BASECLASS__-tab-list, .__BASECLASS__-tab { 103 | padding: 0; 104 | padding-bottom : 0; 105 | } 106 | 107 | .__BASECLASS__-mobile .__BASECLASS__-DBItem span { 108 | display : block; 109 | } 110 | 111 | .__BASECLASS__-mobile .__BASECLASS__-DBItem { 112 | height : auto; 113 | } 114 | 115 | .__BASECLASS__-mobile .__BASECLASS__-tab-list ul li { 116 | display: block; 117 | margin-bottom: 4px; 118 | margin-right : 0px; 119 | } 120 | 121 | .__BASECLASS__-mobile .__BASECLASS__-OptionsSave-wrap { 122 | margin-top : 16px; 123 | } 124 | 125 | .__BASECLASS__-mobile .__BASECLASS__-displayed-info { 126 | margin-left: 6px; 127 | padding-left: 4px; 128 | padding-right: 4px; 129 | margin-right: 6px; 130 | margin-top: 18px; 131 | display : block; 132 | } 133 | } 134 | 135 | @media (max-width: 390px) { 136 | .__BASECLASS__-mobile .__BASECLASS__-displayed-info { 137 | display: block; 138 | margin-top: 12px; 139 | margin-right: 6px; 140 | } 141 | } 142 | 143 | .__BASECLASS__-mobile .__BASECLASS__-section-header-inline { 144 | margin-left: 0; 145 | } 146 | 147 | .__BASECLASS__-mobile .__BASECLASS__-ModalBox { 148 | border-radius: 0; 149 | border-radius: 0; 150 | } 151 | 152 | .__BASECLASS__-mobile .__BASECLASS__-sidebar-wrap .__BASECLASS__-ModalBox { 153 | margin-left: 0; 154 | margin-right: 0; 155 | } 156 | 157 | .__BASECLASS__-mobile .kelly-jr-ui-FavItem-grid-first, 158 | .__BASECLASS__-mobile .kelly-jr-ui-FavItem-grid-first .__BASECLASS__-FavItem-download-state-holder { 159 | padding-left: 0; 160 | } 161 | 162 | .__BASECLASS__-mobile .kelly-jr-ui-FavItem-grid-last, 163 | .__BASECLASS__-mobile .kelly-jr-ui-FavItem-grid-last .__BASECLASS__-FavItem-download-state-holder { 164 | padding-right: 0; 165 | } 166 | 167 | .__BASECLASS__-mobile .content { 168 | width : 100%; 169 | } 170 | 171 | .__BASECLASS__-mobile .__BASECLASS__-sidebar-wrap { 172 | padding-bottom: 0; 173 | } 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /extension/env/css/dark.css: -------------------------------------------------------------------------------- 1 | body.__BASECLASS__-dark { 2 | background-color: #222; 3 | color: #cdcdcd; 4 | } 5 | 6 | .__BASECLASS__-dark button { 7 | color: #e5e5e5; 8 | } 9 | 10 | .__BASECLASS__-dark input[type=submit] { 11 | background: #e63d3d; 12 | border: 0px; 13 | padding-left: 30px; 14 | cursor: pointer; 15 | color: #fff; 16 | font-weight: bold; 17 | height: 40px; 18 | padding-right: 30px; 19 | } 20 | 21 | .__BASECLASS__-dark .__BASECLASS__-FavItem-small .__BASECLASS__-preview-wrap { 22 | border: 3px solid rgb(102 102 102); 23 | } 24 | 25 | .__BASECLASS__-dark .__BASECLASS__-FavItem-tmp-bounds .__BASECLASS__-preview-wrap, 26 | .__BASECLASS__-dark .__BASECLASS__-FavItem-tmp-bounds .__BASECLASS__-preview { 27 | background: #2f2f2f; 28 | } 29 | 30 | .__BASECLASS__-dark .__BASECLASS__-displayed-info { 31 | background: #585858; 32 | } 33 | 34 | .__BASECLASS__-dark .__BASECLASS__-tooltipster-container { 35 | background: rgb(53, 53, 53); 36 | color: #fff; 37 | border-radius: 0; 38 | } 39 | 40 | .__BASECLASS__-dark .__BASECLASS__-tooltipster-close { 41 | background: rgb(53, 53, 53); 42 | color: #fff; 43 | border-radius: 0; 44 | } 45 | 46 | .__BASECLASS__-dark .page-content { 47 | padding: 50px; 48 | padding-top: 20px; 49 | padding-bottom: 50px; 50 | } 51 | 52 | .__BASECLASS__-dark #submenu { 53 | background: #2f2f2f; 54 | border-bottom: 1px solid #5a5a5a; 55 | } 56 | 57 | .__BASECLASS__-dark .__BASECLASS__-MainMenuItem.__BASECLASS__-MainMenuItem-fav a, 58 | .__BASECLASS__-dark .__BASECLASS__-MainMenuItem.__BASECLASS__-MainMenuItem-fav.active a { 59 | background : transparent; 60 | } 61 | 62 | .__BASECLASS__-dark .__BASECLASS__-MainMenuItem a { 63 | background: #646464; 64 | font-size: 14px; 65 | font-weight: bold; 66 | color: #fff; 67 | } 68 | 69 | .__BASECLASS__-dark .__BASECLASS__-MainMenuItem-fav.active .__BASECLASS__-icon { 70 | background-color: #ff7878; 71 | } 72 | 73 | .__BASECLASS__-dark .__BASECLASS__-MainMenuItem.active a { 74 | background: #e63d3d; 75 | color : #fff; 76 | } 77 | 78 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a { 79 | color : #000; 80 | } 81 | 82 | .__BASECLASS__-dark a, .__BASECLASS__-dark a:hover { 83 | color: rgb(255, 255, 255, 0.68); 84 | } 85 | 86 | .__BASECLASS__-dark .__BASECLASS__-tab-list ul li:hover { 87 | background : #858585; 88 | 89 | } 90 | 91 | .__BASECLASS__-dark .__BASECLASS__-tab { 92 | line-height : normal; 93 | padding-top: 8px; 94 | padding-left: 2px; 95 | padding-bottom: 0; 96 | } 97 | 98 | .__BASECLASS__-dark .__BASECLASS__-tab-list ul { 99 | padding : 0; 100 | } 101 | 102 | .__BASECLASS__-dark .__BASECLASS__-tab-list ul:after { 103 | content : ""; 104 | clear : both; 105 | display: block; 106 | } 107 | 108 | .__BASECLASS__-dark .__BASECLASS__-tab-list ul li { 109 | border-top-left-radius: 0; 110 | border-top-right-radius: 0; 111 | background: #646464; 112 | margin: 0; 113 | width: 25%; 114 | height: 40px; 115 | float: left; 116 | font-weight: bold; 117 | line-height: 40px; 118 | } 119 | 120 | .__BASECLASS__-dark .__BASECLASS__-tab-list ul li a, 121 | .__BASECLASS__-dark .__BASECLASS__-tab-list ul li.active a:hover { 122 | color : #fff; 123 | padding : 0; 124 | } 125 | 126 | .__BASECLASS__-dark .__BASECLASS__-tab-list ul li.active { 127 | background: #e63d3d; 128 | } 129 | 130 | .__BASECLASS__-dark .__BASECLASS__-options-table > tbody > tr > td[colspan="2"] { 131 | width: 100%; 132 | height : 22px; 133 | } 134 | 135 | .__BASECLASS__-dark .__BASECLASS__-options-table > tbody > tr > td[colspan="2"].__BASECLASS__-hidden { 136 | height : 0px; 137 | } 138 | 139 | .__BASECLASS__-dark .__BASECLASS__-options-table > tbody > tr > td { 140 | height : 44px; 141 | } 142 | 143 | /* 144 | .__BASECLASS__-dark .__BASECLASS__-radioselect input[type="radio"] {display : none;} 145 | .__BASECLASS__-dark .__BASECLASS__-radioselect input[type="radio"]:checked+label{ font-weight: bold; } 146 | */ 147 | 148 | .__BASECLASS__-dark .__BASECLASS__-DBItem { 149 | background-color: #4e4e4e; 150 | padding: 6px; 151 | } 152 | 153 | .__BASECLASS__-dark .__BASECLASS__-DBItem, .__BASECLASS__-dark .__BASECLASS__-DBItem a { 154 | color: #fff; 155 | border-radius : 0; 156 | } 157 | 158 | .__BASECLASS__-dark .__BASECLASS__-downloader-controll-rangeSwitch.active, 159 | .__BASECLASS__-dark .__BASECLASS__-DBItem.active { 160 | background-color: #797979; 161 | } 162 | 163 | .__BASECLASS__-dark .__BASECLASS__-sidebar-wrap { 164 | width : 280px; 165 | } 166 | 167 | .__BASECLASS__-dark .__BASECLASS__-ModalBox { 168 | border: 0px; 169 | } 170 | 171 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-controll-buttons a { 172 | padding-left: 2px; 173 | padding-right: 2px; 174 | } 175 | 176 | .__BASECLASS__-dark .__BASECLASS__-tab h3 { 177 | font-size : 18px; 178 | } 179 | 180 | .__BASECLASS__-dark ::placeholder, 181 | .__BASECLASS__-dark input[type=text], 182 | .__BASECLASS__-dark select, 183 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-message, 184 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-header, 185 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-content input { 186 | background-color: #8d8d8d!important; 187 | color: #fff!important; 188 | border: 1px solid #9f9f9f; 189 | } 190 | 191 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-header a, 192 | .__BASECLASS__-dark ::placeholder { 193 | border : 0; 194 | color: #fff; 195 | } 196 | 197 | .__BASECLASS__-dark a.__BASECLASS__-FavItem-collection, 198 | .__BASECLASS__-dark a.__BASECLASS__-FavItem-overlay-button, 199 | .__BASECLASS__-dark .__BASECLASS__-preview-text { 200 | background: #5d5d5d; 201 | } 202 | 203 | .__BASECLASS__-dark .__BASECLASS__-extension-additions { 204 | display: inline-block; 205 | vertical-align: middle; 206 | margin-right: 16px; 207 | background: #878787; 208 | border-radius: 6px; 209 | padding: 4px; 210 | } 211 | 212 | .__BASECLASS__-dark .__BASECLASS__-extension-additions .__BASECLASS__-fast-save, 213 | .__BASECLASS__-dark .__BASECLASS__-extension-additions .__BASECLASS__-post-addtofav { 214 | display: inline-block; 215 | float: left; 216 | vertical-align: middle; 217 | font-weight : normal; 218 | padding: 0; 219 | margin: 0; 220 | color: #ffffff; 221 | margin-left: 3px; 222 | margin-right: 3px; 223 | } 224 | 225 | .__BASECLASS__-dark .__BASECLASS__-extension-additions .__BASECLASS__-post-addtofav { 226 | border-bottom: #fff solid 3px; 227 | text-decoration: none; 228 | } 229 | 230 | .__BASECLASS__-dark .__BASECLASS__-extension-additions .kelly-jr-ui-post-addtofav:after { 231 | display : none; 232 | } 233 | 234 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-content { 235 | background: #303030!important; 236 | } 237 | 238 | .__BASECLASS__-dark .__BASECLASS__-ModalBox { 239 | background: transparent!important; 240 | } 241 | 242 | .__BASECLASS__-dark .__BASECLASS__-filters-AdditionButtons a { 243 | background-color : transparent; 244 | } 245 | 246 | 247 | .__BASECLASS__-dark a.__BASECLASS__-pagination-item:hover, 248 | .__BASECLASS__-dark a.__BASECLASS__-pagination-item:focus, 249 | .__BASECLASS__-dark .__BASECLASS__-pagination-item.active { 250 | background: #666666; 251 | color: #fff; 252 | border: 3px solid #8d8d8d; 253 | } 254 | 255 | .__BASECLASS__-dark a.__BASECLASS__-pagination-item { 256 | border: 3px solid #666666; 257 | } 258 | 259 | .__BASECLASS__-dark .__BASECLASS__-FavFilter { 260 | font-size : 12px; 261 | } 262 | 263 | .__BASECLASS__-dark .__BASECLASS__-FavItem-grid-empty, 264 | .__BASECLASS__-dark .__BASECLASS__-FavItem-download-number, 265 | .__BASECLASS__-dark .__BASECLASS__-preview-dimensions, 266 | .__BASECLASS__-dark .__BASECLASS__-message.__BASECLASS__-success, 267 | .__BASECLASS__-dark .__BASECLASS__-OptionsMessage, 268 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons p, 269 | .__BASECLASS__-dark .__BASECLASS__-section-header-inline, 270 | .__BASECLASS__-dark a.__BASECLASS__-pagination-item, 271 | .__BASECLASS__-dark .__BASECLASS__-FavFilter.active, 272 | .__BASECLASS__-dark .__BASECLASS__-FavEditButton.active { 273 | background: #666666; 274 | color: #cdcdcd; 275 | } 276 | 277 | .__BASECLASS__-dark .__BASECLASS__-FavContainer { 278 | background-clip: border-box; 279 | border: 0px solid rgba(0,0,0,.125); 280 | border-radius: 0; 281 | margin: 0px auto; 282 | } 283 | 284 | .__BASECLASS__-dark .__BASECLASS__-FiltersMenu > li > button { 285 | color : #000; 286 | padding-left: 3px; 287 | padding-right: 3px; 288 | } 289 | 290 | .__BASECLASS__-dark .__BASECLASS__-section-header-inline a, .__BASECLASS__-dark .__BASECLASS__-section-header-inline a:hover { 291 | 292 | color : #cdcdcd; 293 | } 294 | 295 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button.selected, 296 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button:hover { 297 | 298 | background-color: #a9a9a9; 299 | } 300 | 301 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-save_as_json, 302 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-save_as_txt, 303 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-save_log, 304 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a { 305 | background-color: #919191; 306 | color : #fff; 307 | } 308 | 309 | .__BASECLASS__-dark .__BASECLASS__-Main-download-btn, 310 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-init { 311 | background-color: #e63d3d; 312 | } 313 | 314 | .__BASECLASS__-dark .__BASECLASS__-Main-download-btn:hover, 315 | .__BASECLASS__-dark .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a:hover { 316 | background-color : #e95757; 317 | } 318 | 319 | .__BASECLASS__-dark .__BASECLASS__-buttons button { 320 | background-color: #919191; 321 | font-weight : bold; 322 | color : #fff; 323 | } 324 | 325 | .__BASECLASS__-dark .__BASECLASS__-buttons button.__BASECLASS__-related-links-options { 326 | background-color: #4e4e4e; 327 | } 328 | 329 | .__BASECLASS__-dark .__BASECLASS__-OptionsMessage, 330 | .__BASECLASS__-dark .__BASECLASS__-message.__BASECLASS__-success, 331 | .__BASECLASS__-dark .__BASECLASS__-message.__BASECLASS__-error { 332 | margin-bottom: 6px; 333 | margin-top : 6px; 334 | padding : 12px; 335 | } 336 | 337 | .__BASECLASS__-dark .__BASECLASS__-message.__BASECLASS__-error { 338 | background: #ff7878; 339 | color : #fff; 340 | } 341 | 342 | .__BASECLASS__-dark button.__BASECLASS__-Main-download-btn { 343 | color : #fff; 344 | } 345 | 346 | .__BASECLASS__-dark .__BASECLASS__-FiltersMenu > li > button, 347 | .__BASECLASS__-dark .__BASECLASS__-downloader-progressbar, 348 | .__BASECLASS__-dark .__BASECLASS__-downloader-progressbar-line:after { 349 | 350 | border-radius : 0; 351 | } 352 | 353 | .__BASECLASS__-dark .profile-selector { 354 | background: #7c7c7c; 355 | } 356 | 357 | .__BASECLASS__-dark .profile-selector-selected { 358 | color: #ffffff; 359 | } 360 | 361 | .__BASECLASS__-dark li.__BASECLASS__-additions-menu-item-li.kelly-jr-ui-additions-active a { 362 | background: #979797; 363 | } 364 | 365 | .__BASECLASS__-dark .__BASECLASS__-active.__BASECLASS__-additions-page-item { 366 | min-height: 250px; 367 | padding: 6px; 368 | background: #5c5c5c; 369 | } 370 | 371 | .__BASECLASS__-dark .__BASECLASS__-active.__BASECLASS__-additions-page-item ul li a { 372 | background: #797979; 373 | padding: 5px; 374 | margin-bottom: 6px; 375 | display: block; 376 | color: rgb(255 255 255); 377 | } 378 | 379 | .__BASECLASS__-dark a.__BASECLASS__-additions-menu-item { 380 | background: #5c5c5c; 381 | } 382 | 383 | .__BASECLASS__-dark .__BASECLASS__-toolbar.__BASECLASS__-toolbar-main.__BASECLASS__-toolbar-tiny, 384 | .__BASECLASS__-dark .__BASECLASS__-toolbar.__BASECLASS__-toolbar-main.__BASECLASS__-toolbar-collapsed, 385 | .__BASECLASS__-dark .__BASECLASS__-toolbar.__BASECLASS__-toolbar-helper-container.__BASECLASS__-toolbar-tiny.__BASECLASS__-toolbar-collapsed { 386 | background: transparent; 387 | } 388 | 389 | .__BASECLASS__-dark .__BASECLASS__-toolbar-collapse { 390 | background: #919191; 391 | } 392 | 393 | .__BASECLASS__-dark .__BASECLASS__-toolbar.__BASECLASS__-toolbar-helper-container.__BASECLASS__-toolbar-tiny, 394 | .__BASECLASS__-dark .__BASECLASS__-toolbar.__BASECLASS__-toolbar-main { 395 | background: rgb(77, 77, 77, 0.84); 396 | color: #cdcdcd; 397 | } 398 | 399 | .__BASECLASS__-dark .__BASECLASS__-toolbar a { 400 | color: #ddd; 401 | } 402 | 403 | .__BASECLASS__-dark .__BASECLASS__-toolbar svg { 404 | color: #000; 405 | } 406 | 407 | .__BASECLASS__-dark .__BASECLASS__-toolbar svg path { 408 | 409 | background: #fff; 410 | stroke: #dddddd; 411 | fill: #dddddd; 412 | } 413 | 414 | .__BASECLASS__-dark .__BASECLASS__-toolbar-theme:before { 415 | display : none; 416 | } 417 | 418 | .__BASECLASS__-dark .__BASECLASS__-toolbar-theme { 419 | border: 4px solid #919191; 420 | background : #fff; 421 | } 422 | 423 | .__BASECLASS__-dark .__BASECLASS__-FavItem-download-enabled:checked+label:before { 424 | 425 | background-color: #8b8b8b; 426 | } 427 | 428 | .__BASECLASS__-pointer-up { 429 | border-bottom: 2em solid #d4d4d4; 430 | } 431 | 432 | @media (max-width: 1040px) { 433 | .__BASECLASS__-mobile .page-content { 434 | padding: 4px; 435 | } 436 | } 437 | 438 | @media (max-width: 590px) { 439 | 440 | .__BASECLASS__-mobile.__BASECLASS__-dark .page-content { 441 | padding : 0 6px; 442 | } 443 | 444 | .__BASECLASS__-mobile.__BASECLASS__-dark .__BASECLASS__-tab-list ul li { 445 | margin-top : 2px; 446 | margin-bottom : 2px; 447 | width: auto; 448 | float: unset; 449 | } 450 | 451 | .__BASECLASS__-mobile.__BASECLASS__-dark .__BASECLASS__-options-table > tbody > tr > td[colspan="2"], 452 | .__BASECLASS__-mobile.__BASECLASS__-dark .__BASECLASS__-options-table > tbody > tr > td { 453 | height : auto; 454 | } 455 | 456 | .__BASECLASS__-mobile.__BASECLASS__-dark .__BASECLASS__-options-table > tbody > tr > td br { 457 | display : inline-block; 458 | margin-left : 4px; 459 | } 460 | 461 | .__BASECLASS__-mobile.__BASECLASS__-dark input[type=submit] { 462 | width: 100%; 463 | margin-top: 12px; 464 | } 465 | 466 | /*.__BASECLASS__-mobile.__BASECLASS__-dark .__BASECLASS__-sidebar-wrap .__BASECLASS__-ModalBox.__BASECLASS__-Main-download-btn-ModalBox { 467 | display : none; 468 | }*/ 469 | } 470 | -------------------------------------------------------------------------------- /extension/env/css/darkRecorderPopup.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #363838; 3 | border: 6px solid #fff; 4 | } 5 | 6 | button { 7 | background: #444646; 8 | color: #fff; 9 | border: 0; 10 | border-radius : 0; 11 | } 12 | 13 | button:hover { 14 | background-color: #5d5d5d; 15 | } 16 | 17 | .popup-page-button-support_project { 18 | background-color: #56afdf; 19 | } 20 | 21 | .popup-page-button-support_project:hover { 22 | background-color: #79c2ea; 23 | } 24 | 25 | .popup-page-recorded-block { 26 | color: #bfbfbf; 27 | background: #4b4b4b; 28 | } 29 | 30 | .__BASECLASS__-recorded-block a { 31 | float : right; 32 | color: #bfbfbf; 33 | } 34 | 35 | .__BASECLASS__-dark.__BASECLASS__-with-extra .__BASECLASS__-button-download_tab_extra { 36 | border-right: 2px solid; 37 | } 38 | 39 | .__BASECLASS__-dark .__BASECLASS__-button-download_right { 40 | border-left: 2px solid; 41 | } 42 | 43 | .__BASECLASS__-dark.__BASECLASS__-with-extra .__BASECLASS__-button-download_tab_extra, 44 | .__BASECLASS__-dark .__BASECLASS__-button-download_right { 45 | border-color : #363838; 46 | } 47 | 48 | .popup-page-button-download_recorded { 49 | background: #e63d3d; 50 | } 51 | 52 | .popup-page-button-download_recorded:hover { 53 | background: #ff7878; 54 | } 55 | 56 | .popup-page-button-download_record span:before { 57 | background: #e63d3d; 58 | } 59 | 60 | #copyright-software { 61 | color: #bfbfbf; 62 | background: #4b4b4b; 63 | padding: 11px 12px; 64 | } 65 | 66 | #copyright-software a { 67 | color: #bfbfbf; 68 | } -------------------------------------------------------------------------------- /extension/env/css/recorder.css: -------------------------------------------------------------------------------- 1 | #__UNIQID__:after { 2 | content: " "; 3 | width: 24px; 4 | height: 24px; 5 | position: absolute; 6 | left: 4px; 7 | background: #ff0b0b; 8 | overflow: hidden; 9 | border-radius: 16px; 10 | /* bottom: 4px; */ 11 | animation: __UNIQID__blink 0.8s cubic-bezier(.5, 0, 1, 1) infinite alternate; 12 | box-shadow: 0px 0px 17px 0px rgb(217 59 59 / 75%); 13 | } 14 | 15 | #__UNIQID__ { 16 | position: fixed; 17 | right: 12px; 18 | z-index: 1000011; 19 | bottom: 12px; 20 | color: #ff0b0b; 21 | font-weight: bold; 22 | line-height: 24px; 23 | background: rgb(255, 255, 255, 0.85); 24 | padding-left: 34px; 25 | padding-top: 5px; 26 | padding-bottom: 36px; 27 | padding-right: 6px; 28 | border-radius: 4px; 29 | border: 1px solid rgb(0, 0, 0, 0.44); 30 | 31 | font-family: 'Open Sans',Sans-serif; 32 | font-size: 15px; 33 | 34 | cursor : pointer; 35 | } 36 | 37 | #__UNIQID__-num { 38 | position: absolute; 39 | bottom: 0; 40 | border-radius: 3px; 41 | padding: 6px; 42 | min-width: 44px; 43 | right: 0; 44 | text-align: center; 45 | padding: 0; 46 | width: 100%; 47 | } 48 | 49 | #__UNIQID__-notice { 50 | position: absolute; 51 | display: none; 52 | right: 0; 53 | background: rgba(0,0,0,0.29); 54 | color: #fff; 55 | padding: 6px; 56 | min-width: 255px; 57 | text-align: left; 58 | bottom: 88px; 59 | } 60 | 61 | @keyframes __UNIQID__blink { 62 | from { opacity: 1; } 63 | to { opacity: 0; } 64 | } -------------------------------------------------------------------------------- /extension/env/css/recorderDownloader.css: -------------------------------------------------------------------------------- 1 | .__BASECLASS__-TypeFiltersContainer { 2 | display : none; 3 | } 4 | 5 | .__BASECLASS__-td-opt-1 { 6 | display : none; 7 | } 8 | 9 | .__BASECLASS__-ModalBox { 10 | min-height : 0; 11 | } 12 | 13 | .__BASECLASS__-FavItem { 14 | cursor : pointer; 15 | } 16 | 17 | .__BASECLASS__-preview { 18 | border-radius : 0; 19 | } 20 | 21 | .__BASECLASS__-FavItem-download-number { 22 | border-radius : 0; 23 | } 24 | 25 | .__BASECLASS__-section-header-inline.__BASECLASS__-section-sidebar_section_extra_progress { 26 | display : none; 27 | } 28 | 29 | .__BASECLASS__-preview-dimensions { 30 | position: absolute; 31 | bottom: 6px; 32 | display: block; 33 | background: #ffa73acc; 34 | line-height: 20px; 35 | text-decoration : none; 36 | height: 22px; 37 | padding: 0px 4px; 38 | } 39 | 40 | .__BASECLASS__-FavItem-small .__BASECLASS__-preview-wrap { 41 | border: 3px solid rgba(255, 121, 26, 0.45); 42 | } 43 | 44 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons p { 45 | background: #ffc78e; 46 | color : #000; 47 | padding: 6px; 48 | margin: 0; 49 | margin-bottom: 6px; 50 | } 51 | 52 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button.selected, 53 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button:hover { 54 | background-color: #ffc78e; 55 | } 56 | 57 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button { 58 | border: 0; 59 | height: 32px; 60 | display: inline-block; 61 | margin-right: 2%; 62 | background-color: #ff8509; 63 | width: 49%; 64 | float: left; 65 | cursor : pointer; 66 | } 67 | 68 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons.__BASECLASS__-buttons-single button { 69 | width : 100%; 70 | } 71 | 72 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button:last-child { 73 | margin-right : 0; 74 | } 75 | 76 | .__BASECLASS__-ModalBox-addition input[type=text], .__BASECLASS__-ModalBox-addition select { 77 | width: 100%; 78 | height: 32px; 79 | margin-bottom: 6px; 80 | } 81 | 82 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-downloader-progressbar { 83 | width: 100%; 84 | } 85 | 86 | .__BASECLASS__-ModalBox-addition, .__BASECLASS__-ModalBox-addition .__BASECLASS__-ModalBox-content { 87 | min-height: unset; 88 | } 89 | 90 | .__BASECLASS__-related-links-options { 91 | padding-top : 12px; 92 | } 93 | 94 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-downloader-statistic.hidden, 95 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-downloader-errors.hidden, 96 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-downloader-progressbar.hidden { 97 | display : none; 98 | } 99 | 100 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-url-exclude label, 101 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-url label { 102 | display: block; 103 | } 104 | 105 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-downloader-statistic, 106 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-downloader-progressbar, 107 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons, 108 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-url-exclude label, 109 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-url label { 110 | margin-bottom: 6px; 111 | } 112 | 113 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons-last { 114 | margin-bottom : 0; 115 | } 116 | 117 | .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll tr.__BASECLASS__-downloader-quality-tr, 118 | .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll tr.__BASECLASS__-downloader-controll-extended-grabber_anim_format { 119 | display : none; 120 | } 121 | 122 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-bounds input { 123 | width: 59px; 124 | height: 32px; 125 | } 126 | 127 | .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons:after { 128 | content: ' '; 129 | clear: both; 130 | display: block; 131 | } 132 | 133 | .__BASECLASS__-process-docLoader-work .__BASECLASS__-buttons-filter, 134 | .__BASECLASS__-process-docLoader-work .__BASECLASS__-extra-sort, 135 | .__BASECLASS__-process-docLoader-work .__BASECLASS__-section-sidebar_section_extra_sort, 136 | .__BASECLASS__-process-docLoader-work .__BASECLASS__-bounds, 137 | .__BASECLASS__-process-docLoader-work .__BASECLASS__-url-exclude, 138 | .__BASECLASS__-process-docLoader-work .__BASECLASS__-related-links-options-wrap, 139 | .__BASECLASS__-process-docLoader-work .__BASECLASS__-url { 140 | 141 | display : none; 142 | } 143 | 144 | .__BASECLASS__-displayed-info { 145 | 146 | font-size: 12px; 147 | 148 | background: #ffefc985; 149 | display: inline-block; 150 | height: 30px; 151 | vertical-align: middle; 152 | line-height: 30px; 153 | padding-left: 12px; 154 | padding-right: 12px; 155 | } 156 | 157 | .__BASECLASS__-buttons button.__BASECLASS__-proportions, 158 | .__BASECLASS__-buttons button.__BASECLASS__-related-links-options, 159 | .__BASECLASS__-buttons button.__BASECLASS__-related-links { 160 | background-color: #ffd6a4; 161 | } 162 | 163 | .__BASECLASS__-buttons button.__BASECLASS__-related-links-options { 164 | float: right; 165 | height: 32px; 166 | width: 18%; 167 | display: block; 168 | background-position: center; 169 | } 170 | 171 | .__BASECLASS__-buttons button.__BASECLASS__-related-links { 172 | width : 80%; 173 | } 174 | 175 | .__BASECLASS__-checkmark { 176 | display:inline-block; 177 | width: 22px; 178 | height:22px; 179 | transform: rotate(45deg); 180 | vertical-align: middle; 181 | } 182 | 183 | .__BASECLASS__-checkmark:before { 184 | position: absolute; 185 | width:3px; 186 | height:9px; 187 | content:""; 188 | background-color:#5fff0d; 189 | left:11px; 190 | top:6px; 191 | } 192 | 193 | .__BASECLASS__-checkmark:after { 194 | position: absolute; 195 | content:""; 196 | width:3px; 197 | height:3px; 198 | background-color:#5fff0d; 199 | left:8px; 200 | top:12px; 201 | } 202 | 203 | .__BASECLASS__-Main-download-btn { 204 | display: block; 205 | text-align: center; 206 | width: 100%; 207 | height: 100%; 208 | padding: 7px; 209 | font-size: 16px; 210 | text-decoration: none; 211 | background: #ff6900; 212 | } 213 | 214 | .__BASECLASS__-Main-download-btn-ModalBox.__BASECLASS__-hidden { 215 | margin-bottom : 0; 216 | margin-top : 0; 217 | } 218 | 219 | .__BASECLASS__-Main-download-btn-ModalBox { 220 | border : 0; 221 | } -------------------------------------------------------------------------------- /extension/env/css/recorderPopup.css: -------------------------------------------------------------------------------- 1 | body { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | width: 248px; 6 | font-size: 12px; 7 | font-family: 'Open Sans',Sans-serif; 8 | background: #ffffff; 9 | border: 6px solid #fff; 10 | } 11 | 12 | * { 13 | box-sizing: border-box; 14 | } 15 | 16 | button { 17 | background: #f4f4f4; 18 | color: #000; 19 | border: 1px solid #9f9f9f; 20 | border-radius: 2px; 21 | width: 100%; 22 | margin-bottom: 2px; 23 | padding: 10px 16px; 24 | font-weight: bold; 25 | cursor: pointer; 26 | position: relative; 27 | font-size: 14px; 28 | font-family: 'Open Sans',Sans-serif; 29 | /* border-bottom: 1px solid #000; */ 30 | } 31 | 32 | button:hover { 33 | background-color: #b7b7b7; 34 | } 35 | 36 | .__BASECLASS__-button-support_project:hover { 37 | background-color : #56afdf; 38 | } 39 | 40 | .__BASECLASS__-button-support_project { 41 | background-color: #3cc0ff; 42 | color: #fff; 43 | border-color: #717171; 44 | } 45 | 46 | button span.__BASECLASS__-icon { 47 | display: inline-block; 48 | vertical-align: middle; 49 | margin : 0; 50 | } 51 | 52 | button span.__BASECLASS__-icon-cup { 53 | background-image: url(../../env/img/cup.png); 54 | background-size: 28px; 55 | width: 28px; 56 | height: 20px; 57 | background-repeat: no-repeat; 58 | background-position: center; 59 | } 60 | 61 | button span.__BASECLASS__-text { 62 | vertical-align: middle; 63 | margin-left: 4px; 64 | } 65 | 66 | .__BASECLASS__-recorded-block { 67 | width: 100%; 68 | padding: 6px; 69 | color: #000000; 70 | margin-bottom: 2px; 71 | padding: 12px 7px; 72 | } 73 | 74 | .__BASECLASS__-recorded-block a { 75 | float : right; 76 | color: #ff1515; 77 | } 78 | 79 | .__BASECLASS__-with-extra .__BASECLASS__-button-download_current_tab { 80 | width : 80%; 81 | border-bottom-left-radius: 0; 82 | border-top-left-radius: 0; 83 | } 84 | 85 | .__BASECLASS__-with-extra .__BASECLASS__-button-download_tab_extra { 86 | width : 20%; 87 | float : left; 88 | border-right: 0px; 89 | border-bottom-right-radius: 0; 90 | border-top-right-radius: 0; 91 | } 92 | 93 | .__BASECLASS__-button-download_right { 94 | border-bottom-left-radius: 0; 95 | border-top-left-radius: 0; 96 | border-left: 0px; 97 | } 98 | 99 | .__BASECLASS__-button-download_left { 100 | border-bottom-right-radius: 0; 101 | border-top-right-radius: 0; 102 | } 103 | 104 | .__BASECLASS__-button-download_right, .__BASECLASS__-button-download_left { 105 | width: 50%; 106 | } 107 | 108 | .__BASECLASS__-recording button { 109 | opacity : 0.1; 110 | pointer-events: none; 111 | } 112 | 113 | .__BASECLASS__-recording button.__BASECLASS__-button-download_record { 114 | opacity : 1; 115 | pointer-events: unset; 116 | } 117 | 118 | .__BASECLASS__-button-download_recorded { 119 | background: #4bb9ff; 120 | border-color: #4b8db7; 121 | color: #fff; 122 | margin-bottom: 3px; 123 | } 124 | 125 | .__BASECLASS__-button-download_recorded:hover { 126 | background: #70bff2; 127 | } 128 | 129 | .__BASECLASS__-button-download_record span:before { 130 | display: inline-block; 131 | content: " "; 132 | width: 12px; 133 | height: 12px; 134 | background: #e63d3d; 135 | border-radius: 6px; 136 | margin-right: 6px; 137 | overflow: hidden; 138 | vertical-align: middle; 139 | } 140 | 141 | #copyright-software { 142 | text-align: right; 143 | color: #242424; 144 | background: #ffffff; 145 | padding: 4px 3px; 146 | } 147 | 148 | #copyright-software a:first-child { 149 | float: left; 150 | } 151 | 152 | #copyright-software a { 153 | color: #242424; 154 | } -------------------------------------------------------------------------------- /extension/env/css/single.css: -------------------------------------------------------------------------------- 1 | /* css for display extension as separate page */ 2 | 3 | html { 4 | overflow-y : scroll; 5 | } 6 | 7 | body, html { 8 | margin: 0px; 9 | padding: 0px; 10 | } 11 | 12 | body { 13 | background-color : #fff; 14 | min-width : 320px; 15 | 16 | font-family: "Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; 17 | font-weight: 400; 18 | line-height: 1.5; 19 | font-size : 14px; 20 | text-align: left; 21 | } 22 | 23 | a { 24 | color: #212121; 25 | font-size: 14px; 26 | } 27 | 28 | ul { 29 | margin: 0px; 30 | padding: 0px; 31 | } 32 | 33 | *, *:before, *:after { 34 | -webkit-box-sizing: border-box; 35 | -moz-box-sizing: border-box; 36 | box-sizing: border-box; 37 | } 38 | 39 | *,:after,:before { 40 | box-sizing: border-box; 41 | } 42 | 43 | input[type=submit] { 44 | background: #fb0; 45 | border: 0px; 46 | padding: 6px; 47 | cursor: pointer; 48 | } 49 | 50 | .page-content { 51 | padding : 50px; 52 | padding-top : 20px; 53 | } 54 | 55 | .page-content > div { 56 | float : left; 57 | } 58 | 59 | .content { 60 | width : 75%; 61 | min-height : 100px; 62 | } 63 | 64 | .page-content > div.clear { 65 | clear : both; 66 | float: none; 67 | } 68 | 69 | #sidebar { 70 | width : 25%; 71 | min-height : 100px; 72 | } 73 | 74 | #submenu { 75 | padding: 50px; 76 | padding-bottom: 20px; 77 | padding-top: 20px; 78 | background-color: #ffd6a4; 79 | min-height : 72px; 80 | border-bottom: 1px solid #ffa429; 81 | } 82 | 83 | .__BASECLASS__-imagesBlock-container { 84 | width : 100%; 85 | overflow : hidden; 86 | margin-bottom : 16px; 87 | } 88 | 89 | .__BASECLASS__-MainMenuItem { 90 | display : inline-block; 91 | } 92 | 93 | .__BASECLASS__-MainMenuItem a { 94 | background: #ffefc9; 95 | height: 30px; 96 | display: inline-block; 97 | vertical-align: middle; 98 | padding-left: 12px; 99 | padding-right: 12px; 100 | line-height: 28px; 101 | text-decoration: none; 102 | font-size: 14px; 103 | } 104 | 105 | .__BASECLASS__-FavFilter.active { 106 | padding : 6px; 107 | } 108 | 109 | .__BASECLASS__-FavItemsCount.__BASECLASS__-FavItemsCount-1 { 110 | border-radius : 6px; 111 | } 112 | 113 | .__BASECLASS__-FavItem-tmp-bounds .__BASECLASS__-preview-wrap, 114 | .__BASECLASS__-FavItem-tmp-bounds .__BASECLASS__-preview { 115 | background: #efefef; 116 | } 117 | 118 | .__BASECLASS__-copyright-info a { 119 | font-size : 18px; 120 | } 121 | 122 | .options_page .__BASECLASS__-ModalBox { 123 | margin-top: 6px; 124 | margin-left: 16px; 125 | margin-bottom : 6px; 126 | } 127 | 128 | .__BASECLASS__-copyright-info { 129 | font-size: 18px; 130 | float: right; 131 | line-height: normal; 132 | margin-top: -4px; 133 | } 134 | 135 | #copyright-software a, #copyright-software { 136 | font-size : 12px; 137 | text-align: right; 138 | } 139 | 140 | /* additions section */ 141 | 142 | a.__BASECLASS__-additions-menu-item { 143 | display: block; 144 | /* height: 20px; */ 145 | width: 100%; 146 | padding: 6px; 147 | margin-bottom: 4px; 148 | text-decoration : none; 149 | } 150 | 151 | li.__BASECLASS__-additions-menu-item-li { 152 | /* display: inline-block; */ 153 | margin-right: 6px; 154 | list-style: none; 155 | } 156 | 157 | .__BASECLASS__-active.__BASECLASS__-additions-page-item { 158 | min-height: 250px; 159 | padding: 6px; 160 | } 161 | 162 | .__BASECLASS__-active.__BASECLASS__-additions-page-item ul { 163 | margin: 0px; 164 | padding: 0px; 165 | margin-left: 6px; 166 | } 167 | 168 | .__BASECLASS__-active.__BASECLASS__-additions-page-item ul li { 169 | display : block; 170 | list-style : none; 171 | } 172 | 173 | .__BASECLASS__-active.__BASECLASS__-additions-page-item ul li a { 174 | padding: 3px; 175 | margin-bottom: 6px; 176 | display: block; 177 | color: #000; 178 | text-decoration: underline; 179 | } 180 | 181 | .__BASECLASS__-additions-additions_menu { 182 | float: left; 183 | width: 20%; 184 | } 185 | 186 | .__BASECLASS__-additions-pages { 187 | float: right; 188 | width: 80%; 189 | min-height: 250px; 190 | } 191 | 192 | .__BASECLASS__-active.__BASECLASS__-additions-page-item h2 { 193 | margin: 0; 194 | margin-bottom: 34px; 195 | padding: 0 12px; 196 | font-weight: normal; 197 | } 198 | 199 | .__BASECLASS__-active.__BASECLASS__-additions-page-item p { 200 | margin: 0; 201 | margin-bottom: 6px; 202 | font-size: 16px; 203 | padding : 0 12px; 204 | } 205 | 206 | .__BASECLASS__-active.__BASECLASS__-additions-page-item li a { 207 | text-decoration : none; 208 | } 209 | 210 | .__BASECLASS__-active.__BASECLASS__-additions-page-item a { 211 | font-size: 16px; 212 | } 213 | 214 | .__BASECLASS__-additions-page:after { 215 | content: ""; 216 | clear: both; 217 | display: block; 218 | } 219 | 220 | .profile-selector { 221 | padding: 6px; 222 | margin-bottom: 6px; 223 | } 224 | 225 | .profile-selector-hostlist { 226 | font-size: 12px; 227 | margin-top: 14px; 228 | } 229 | 230 | .profile-selector-selected { 231 | display : none; 232 | } 233 | 234 | .profile-selector-selected.active { 235 | display : inline-block; 236 | } 237 | 238 | .profile-selector { 239 | background: #ffefc9; 240 | } 241 | 242 | li.__BASECLASS__-additions-menu-item-li.__BASECLASS__-additions-active a { 243 | background: #fdb201; 244 | } 245 | 246 | a.__BASECLASS__-additions-menu-item { 247 | background: #ffefc9; 248 | } 249 | 250 | .__BASECLASS__-active.__BASECLASS__-additions-page-item { 251 | min-height: 250px; 252 | padding: 0; 253 | background: transparent; 254 | } 255 | 256 | .profile-selector-selected { 257 | display: none; 258 | color: #ff5600; 259 | font-weight: bold; 260 | font-size: 14px; 261 | } 262 | 263 | @media (max-width: 1240px) { 264 | 265 | .content { 266 | width: 65%; 267 | min-height: 100px; 268 | } 269 | 270 | #sidebar { 271 | width: 35%; 272 | min-height: 100px; 273 | } 274 | } -------------------------------------------------------------------------------- /extension/env/css/singleMobile.css: -------------------------------------------------------------------------------- 1 | /* css for display extension as separate page */ 2 | /* mobile version */ 3 | 4 | 5 | .__BASECLASS__-mobile #sidebar { 6 | display : none; 7 | } 8 | 9 | 10 | @media (max-width: 1040px) { 11 | .__BASECLASS__-mobile .page-content { 12 | padding: 4px; 13 | } 14 | 15 | .__BASECLASS__-mobile #submenu { 16 | padding: 30px; 17 | padding-bottom: 20px; 18 | padding-top: 20px; 19 | } 20 | } 21 | 22 | @media (max-width: 1000px) { 23 | 24 | .__BASECLASS__-mobile .__BASECLASS__-additions-additions_menu { 25 | float : none; 26 | width : 100%; 27 | padding-top: 6px; 28 | } 29 | 30 | .__BASECLASS__-mobile .__BASECLASS__-additions-pages{ 31 | float : none; 32 | width : 100%; 33 | } 34 | 35 | .__BASECLASS__-mobile .__BASECLASS__-active.__BASECLASS__-additions-page-item ul { 36 | margin: 0px; 37 | padding: 0px; 38 | margin-left: 0; 39 | margin-top : 12px; 40 | } 41 | 42 | .__BASECLASS__-mobile .__BASECLASS__-additions-additions_menu ul li.__BASECLASS__-additions-menu-item-li { 43 | margin-right: 0; 44 | } 45 | } 46 | 47 | @media (max-width: 590px) { 48 | 49 | .__BASECLASS__-mobile #submenu { 50 | padding-left: 0; 51 | padding-right: 0; 52 | } 53 | } -------------------------------------------------------------------------------- /extension/env/css/white.css: -------------------------------------------------------------------------------- 1 | .__BASECLASS__-white button { 2 | color: #262626; 3 | } 4 | 5 | .__BASECLASS__-white .__BASECLASS__-FavItem-small .__BASECLASS__-preview-wrap { 6 | border: 3px solid rgb(102 102 102); 7 | } 8 | 9 | .__BASECLASS__-white .__BASECLASS__-FavItem-tmp-bounds .__BASECLASS__-preview-wrap, 10 | .__BASECLASS__-white .__BASECLASS__-FavItem-tmp-bounds .__BASECLASS__-preview { 11 | background: #2f2f2f; 12 | } 13 | 14 | .__BASECLASS__-white .__BASECLASS__-displayed-info { 15 | background: #c2c2c2ab; 16 | color: #575757; 17 | font-weight: bold; 18 | } 19 | 20 | .__BASECLASS__-white #submenu { 21 | color: #2c1e52; 22 | background-color: #dedede; 23 | border-color: #9f9f9f; 24 | } 25 | 26 | .__BASECLASS__-white .__BASECLASS__-MainMenuItem.__BASECLASS__-MainMenuItem-fav a, 27 | .__BASECLASS__-white .__BASECLASS__-MainMenuItem.__BASECLASS__-MainMenuItem-fav.active a { 28 | background : transparent; 29 | } 30 | 31 | .__BASECLASS__-white input[type=submit], 32 | .__BASECLASS__-white .__BASECLASS__-MainMenuItem a { 33 | background: #80838d; 34 | font-size: 16px; 35 | color: #fff; 36 | } 37 | 38 | .__BASECLASS__-white .__BASECLASS__-MainMenuItem-fav.active .__BASECLASS__-icon { 39 | background-color: #80838d; 40 | } 41 | 42 | .__BASECLASS__-white .__BASECLASS__-MainMenuItem.active a { 43 | background: #b4b7c2; 44 | color : #fff; 45 | } 46 | 47 | .__BASECLASS__-white .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a { 48 | color : #000; 49 | } 50 | 51 | .__BASECLASS__-white #submenu a, 52 | .__BASECLASS__-white a.__BASECLASS__-help { 53 | color: #ffffff; 54 | } 55 | 56 | .__BASECLASS__-white a, .__BASECLASS__-white a:hover { 57 | color: #fff; 58 | } 59 | 60 | .__BASECLASS__-white .__BASECLASS__-tab-list ul li:hover { 61 | background : #c0c4d1; 62 | 63 | } 64 | 65 | .__BASECLASS__-white .__BASECLASS__-tab-list ul li { 66 | background: #b4b7c2; 67 | } 68 | 69 | .__BASECLASS__-white .__BASECLASS__-tab-list ul li.active { 70 | background: #80838d; 71 | } 72 | 73 | .__BASECLASS__-white .__BASECLASS__-DBItem { 74 | background-color: #4e4e4e; 75 | } 76 | 77 | .__BASECLASS__-white .__BASECLASS__-DBItem, .__BASECLASS__-white .__BASECLASS__-DBItem a { 78 | color: #fff; 79 | } 80 | 81 | .__BASECLASS__-white .__BASECLASS__-downloader-controll-rangeSwitch.active, 82 | .__BASECLASS__-white .__BASECLASS__-DBItem.active { 83 | background-color: #979aa7; 84 | color: #fff; 85 | } 86 | 87 | .__BASECLASS__-white .__BASECLASS__-ModalBox-controll-buttons a { 88 | padding-left: 2px; 89 | padding-right: 2px; 90 | } 91 | 92 | .__BASECLASS__-white a.__BASECLASS__-FavItem-collection, 93 | .__BASECLASS__-white a.__BASECLASS__-FavItem-overlay-button, 94 | .__BASECLASS__-white .__BASECLASS__-preview-text { 95 | background: #5d5d5d; 96 | } 97 | 98 | .__BASECLASS__-white .__BASECLASS__-FavItem-grid-empty, 99 | .__BASECLASS__-white .__BASECLASS__-FavItem-download-number, 100 | .__BASECLASS__-white .__BASECLASS__-preview-dimensions, 101 | .__BASECLASS__-white .__BASECLASS__-message.__BASECLASS__-success, 102 | .__BASECLASS__-white .__BASECLASS__-OptionsMessage, 103 | .__BASECLASS__-white .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons p, 104 | .__BASECLASS__-white .__BASECLASS__-section-header-inline, 105 | .__BASECLASS__-white a.__BASECLASS__-pagination-item, 106 | .__BASECLASS__-white .__BASECLASS__-FavFilter.active, 107 | .__BASECLASS__-white .__BASECLASS__-FavEditButton.active { 108 | background: #80838d; 109 | color: #fffbfb; 110 | } 111 | 112 | .__BASECLASS__-white .__BASECLASS__-section-header-inline a, .__BASECLASS__-white .__BASECLASS__-section-header-inline a:hover { 113 | 114 | color : #cdcdcd; 115 | } 116 | 117 | .__BASECLASS__-white .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button.selected, 118 | .__BASECLASS__-white .__BASECLASS__-ModalBox-addition .__BASECLASS__-buttons button:hover { 119 | 120 | background-color: #989ca9; 121 | } 122 | 123 | .__BASECLASS__-white #submenu .__BASECLASS__-copyright-info a { 124 | color: #121212; 125 | } 126 | 127 | .__BASECLASS__-white .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-save_as_json, 128 | .__BASECLASS__-white .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-save_as_txt, 129 | .__BASECLASS__-white .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-save_log, 130 | .__BASECLASS__-white .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a { 131 | background: rgb(183 185 192); 132 | color : #fff; 133 | } 134 | 135 | .__BASECLASS__-white .__BASECLASS__-Main-download-btn, 136 | .__BASECLASS__-white .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a.__BASECLASS__-downloader-button-init { 137 | background-color: #80838d; 138 | } 139 | 140 | .__BASECLASS__-white .__BASECLASS__-Main-download-btn:hover, 141 | .__BASECLASS__-white .__BASECLASS__-ModalBox-downloader .__BASECLASS__-downloader-controll-buttons a:hover { 142 | background-color : #989ca9; 143 | } 144 | 145 | .__BASECLASS__-white .__BASECLASS__-buttons button { 146 | background-color: #80838d; 147 | color : #fff; 148 | } 149 | 150 | .__BASECLASS__-white .__BASECLASS__-buttons button.__BASECLASS__-related-links-options { 151 | background-color: #6a6a6a; 152 | } 153 | 154 | .__BASECLASS__-white .__BASECLASS__-message.__BASECLASS__-error { 155 | background: #ff7878; 156 | color : #fff; 157 | } 158 | 159 | .__BASECLASS__-white button.__BASECLASS__-Main-download-btn { 160 | color : #fff; 161 | } 162 | 163 | .__BASECLASS__-white .profile-selector { 164 | background: #b4b7c2; 165 | } 166 | 167 | .__BASECLASS__-white .profile-selector-selected { 168 | color: #ffffff; 169 | } 170 | 171 | .__BASECLASS__-white li.__BASECLASS__-additions-menu-item-li.kelly-jr-ui-additions-active a { 172 | background: #b4b7c2; 173 | } 174 | 175 | .__BASECLASS__-white .__BASECLASS__-active.__BASECLASS__-additions-page-item ul li a { 176 | 177 | } 178 | 179 | .__BASECLASS__-white .__BASECLASS__-additions-page-item a { 180 | color: #000; 181 | } 182 | 183 | .__BASECLASS__-white a.__BASECLASS__-additions-menu-item { 184 | background: #80838d; 185 | } 186 | -------------------------------------------------------------------------------- /extension/env/dynamic/dispetcher.js: -------------------------------------------------------------------------------- 1 | // part of KellyFavItems extension 2 | // требуется для обработки событий в "небезопасном" окне вне расширения. 3 | // используемые функции 4 | // - активирует подтверждение на закрытие окна если выполняется процесс загрузки профилей \ тегов или скачивание картинок 5 | 6 | (function() { 7 | 8 | var KellyDynamicDispetcher = new Object; 9 | KellyDynamicDispetcher.enabledEvents = {}; 10 | KellyDynamicDispetcher.messageNameBase = 'kelly_dynaminc'; 11 | KellyDynamicDispetcher.getMessage = function(e) { 12 | 13 | var handler = KellyDynamicDispetcher; 14 | 15 | if (!e.data || !e.data.method || !e.data[handler.messageNameBase]) return false; 16 | 17 | var response = { 18 | 19 | senderId : 'dynamic_dispetcher', 20 | error : '', 21 | method : e.data.method, 22 | location : window.location.href, 23 | }; 24 | 25 | 26 | if (e.data.method == handler.messageNameBase + '.unbind.beforeunload') { 27 | 28 | handler.removeBeforeUnload(); 29 | 30 | } else if (e.data.method == handler.messageNameBase + '.bind.beforeunload') { 31 | 32 | if (!handler.enabledEvents['before_unload']) { 33 | 34 | handler.enabledEvents['before_unload'] = function(e) { 35 | e.preventDefault(); 36 | e.returnValue = ''; 37 | 38 | return ""; 39 | }; 40 | 41 | window.addEventListener('beforeunload', handler.enabledEvents['before_unload']); 42 | } 43 | 44 | } else if (e.data.method == handler.messageNameBase + '.getvar') { 45 | 46 | 47 | if (e.data.varList) { 48 | 49 | var getVarAsString = function(value) { 50 | if (typeof value != 'string' && typeof String != 'undefined') value = String(value); 51 | return value; 52 | } 53 | 54 | response.varList = {}; 55 | for (var i = 0; i < e.data.varList.length; i++) response.varList[e.data.varList[i]] = typeof window[e.data.varList[i]] != 'undefined' ? getVarAsString(window[e.data.varList[i]]) : false; 56 | 57 | } else { 58 | 59 | response.error = 'var_name is undefined'; 60 | } 61 | 62 | } else if (e.data.method == handler.messageNameBase + '.self.remove') { 63 | 64 | handler.remove(); 65 | } 66 | 67 | e.source.postMessage(response, window.location.origin); 68 | }; 69 | 70 | KellyDynamicDispetcher.removeBeforeUnload = function() { 71 | 72 | if (this.enabledEvents['before_unload']) { 73 | 74 | window.removeEventListener('beforeunload', this.enabledEvents['before_unload']); 75 | this.enabledEvents['before_unload'] = false; 76 | } 77 | } 78 | 79 | KellyDynamicDispetcher.init = function() { 80 | window.addEventListener('message', KellyDynamicDispetcher.getMessage); 81 | } 82 | 83 | KellyDynamicDispetcher.remove = function() { 84 | 85 | this.removeBeforeUnload(); 86 | window.removeEventListener('message', KellyDynamicDispetcher.getMessage); 87 | } 88 | 89 | KellyDynamicDispetcher.init(); 90 | }()); -------------------------------------------------------------------------------- /extension/env/html/additions/menu-item.html: -------------------------------------------------------------------------------- 1 |
  • 2 | __NAME__ 3 |
  • -------------------------------------------------------------------------------- /extension/env/html/additions/page.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 | 5 |
    6 | 7 |
    8 | 9 |
    10 | __MODULES__ 11 |
    12 | 13 |
    14 | 19 |
    20 | 21 | 29 | 30 |
    31 | 32 |
    33 |

    Thank you for helping to improve this project!

    34 |

    Please consider donating if you like this project. I have put a lot of efforts into this project and donations help me justify it.

    35 |

    You can find actual donate links here

    36 |

     

    37 |

    Or you can add review on:

    38 |

     

    39 |

    Add review on Chrome Web Store

    40 |

    Add review on FireFox Web Store

    41 |

     

    42 |

    My e-mail contact : radiokellyc@gmail.com

    43 |

     

    44 | 45 |

    It's also important and would be great help!

    46 |

    If you want to report issue, or may be report about some translation mistake, send some suggestions \ request, you can send feedback on github ("issues" section), mail

    47 |

     

    48 |

    49 |

    Thank You for Your support!

    50 |

    You can hide additional extension annotations, support button from popup if you want hidebring back

    51 |
    52 | 53 |
    54 |

    Спасибо, что помогаете улучшить проект!

    55 |

    Если у вас есть желание поддержать развитие разработки расширения, 56 | вы можете перевести любую сумму одним из возможных способов приведенных здесь

    57 |

     

    58 |

    Так же вы можете оставить отзыв :

    59 |

     

    60 |

    Добавить отзыв в Chrome Web Store

    61 |

    Добавить отзыв в FireFox Web Store

    62 |

     

    63 |

    64 |

    Оставить обратную связь так же можно на e-mail : radiokellyc@gmail.com или на GitHub

    65 |

    Если хотите, можете скрыть любые кнопки поддержки расширения ссылающиеся на эту страницу - скрытьвернуть

    66 |
    67 | 68 |
    69 | 70 |
    71 |
    -------------------------------------------------------------------------------- /extension/env/html/additions/profile-selector-recorder.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 | 5 | @options_page_recorder_cfg@ 6 | @options_page_selected@ 7 |
    8 | 9 |
    @options_page_recorder_desc@
    10 | 11 |
    -------------------------------------------------------------------------------- /extension/env/html/additions/profile-selector.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 6 | 7 |
    8 | __PROFILENAME__ 9 | @options_page_selected@ 10 |
    11 | 12 |
    @options_page_custom_sites@ __HOSTLIST__
    13 | 14 |
    -------------------------------------------------------------------------------- /extension/env/html/bookmarksParser/parser-form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 | 5 | Страницы : (если нужно скачать все страницы, оставьте не заполненным) 6 | 7 | 8 | [__RATING_FILTER__Мин. рейтинг постов : 9 | ] 10 | 11 |
    12 |
    13 | 14 | 19 | 20 | 21 | 22 |
    23 | @download_start@ 24 | 25 | Доп. настройки 26 | [__EXTRA__Скрытые настройки] 27 | Как пользоватся? 28 |
    29 |
    30 | 31 | 32 | 35 | 36 |
    37 | 38 | @pages_n@ 39 | 40 | 43 | 44 |
    45 | 46 |
    47 | 48 | 51 | 52 | 55 | 56 |
    57 | @download_tag_filter_1@
    58 | @download_tag_filter_empty@
    59 | 60 |
    61 | 62 | 65 | 66 |
    67 | @download_createc_1@
    68 | 69 |
    70 | 71 |
    72 | 73 |
    __EXTRA_OPTIONS__
    74 | 75 |
    -------------------------------------------------------------------------------- /extension/env/html/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

    13 |
    14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /extension/env/html/recorderDownloader.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 |
    15 | 16 | 17 | 18 |
    19 |
    20 | 21 |
    22 |
    23 | 24 |
    25 |
    26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /extension/env/html/recorderPopup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /extension/env/html/update.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | KellyC Image Downloader 11 | 12 | 13 | 276 | 277 | 278 | 279 | 280 | 281 | 282 |
    283 |
    284 | 285 | 286 | 311 | 312 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | -------------------------------------------------------------------------------- /extension/env/img/arial-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/extension/env/img/arial-yellow.png -------------------------------------------------------------------------------- /extension/env/img/cup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/extension/env/img/cup.png -------------------------------------------------------------------------------- /extension/env/img/heartr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/extension/env/img/heartr.png -------------------------------------------------------------------------------- /extension/env/img/icon128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/extension/env/img/icon128x128.png -------------------------------------------------------------------------------- /extension/env/img/icon32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/extension/env/img/icon32x32.png -------------------------------------------------------------------------------- /extension/env/img/icon44x44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/extension/env/img/icon44x44.png -------------------------------------------------------------------------------- /extension/env/img/icon50x50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NC22/KellyC-Image-Downloader/58b8909659277f953f3a560f90286cf7116af2e7/extension/env/img/icon50x50.png -------------------------------------------------------------------------------- /extension/env/init/background.js: -------------------------------------------------------------------------------- 1 | KellyEDispetcher.init(); 2 | 3 | // keep empty space to prevent syntax errors if some symbols will added at end -------------------------------------------------------------------------------- /extension/env/init/commonAbout.js: -------------------------------------------------------------------------------- 1 | KellyCAbout.init(); -------------------------------------------------------------------------------- /extension/env/init/recorderDownloader.js: -------------------------------------------------------------------------------- 1 | KellyTools.loadFrontJs(function() { 2 | KellyDPage.init(); 3 | }); -------------------------------------------------------------------------------- /extension/env/init/recorderFront.js: -------------------------------------------------------------------------------- 1 | 2 | if (typeof K_WATCHDOG == 'undefined') { 3 | 4 | var onDOMRendered = function() { 5 | 6 | K_WATCHDOG = new KellyPageWatchdog(); 7 | K_WATCHDOG.exec(); 8 | } 9 | 10 | if (document.body) { // "run_at": "document_idle" 11 | 12 | onDOMRendered(); 13 | 14 | } else { // "run_at": "document_start" 15 | 16 | document.addEventListener("DOMContentLoaded", onDOMRendered); 17 | } 18 | 19 | } 20 | 21 | // keep empty space to prevent syntax errors if some symbols will added at end -------------------------------------------------------------------------------- /extension/lib/kellyAdditionsForm.js: -------------------------------------------------------------------------------- 1 | KellyAdditionsForm = { 2 | tpl : ['menu-item', 'page', 'profile-selector', 'profile-selector-recorder'], 3 | menu : ['additions_about', 'additions_help', 'additions_modules', 'additions_donate'], 4 | 5 | initToggleProfile : function(p, favEnv) { 6 | 7 | var settings = false, el = document.querySelector('[data-profile=' + p.profile + ']'); 8 | if (!el) return; 9 | 10 | if (!favEnv) { 11 | favEnv = new KellyFavItems({env : p}) 12 | favEnv.load('cfg', function(fav) { settings = fav; el.checked = !settings.coptions.disabled;}); 13 | } else { 14 | settings = favEnv.getGlobal('fav'); 15 | el.checked = !settings.coptions.disabled; 16 | } 17 | 18 | el.onchange = function() { 19 | settings.coptions.disabled = settings.coptions.disabled ? false : true; 20 | favEnv.save('cfg'); 21 | } 22 | }, 23 | show : function(container, favEnv, pageId) { 24 | 25 | if (typeof KellyProfileRecorder == 'undefined') { 26 | KellyAdditionsForm.menu = ['additions_about', 'additions_donate']; 27 | } 28 | 29 | pageId = pageId ? pageId : KellyAdditionsForm.menu[0]; 30 | 31 | favEnv.closeSidebar(); 32 | 33 | KellyTools.getBrowser().runtime.sendMessage({method: "getResources", asObject : true, items : KellyAdditionsForm.tpl, itemsRoute : {module : 'additions', type : 'html'}}, function(request) { 34 | 35 | var pModulesHtml = '', menuHtml = '', css = ''; 36 | var pModules = []; 37 | var defaultLinks = false; 38 | 39 | if (typeof KellyProfileRecorder != 'undefined') pModules.push(KellyProfileRecorder); 40 | if (typeof KellyProfileJoyreactor != 'undefined') pModules.push(KellyProfileJoyreactor); 41 | 42 | var curP = favEnv.getGlobal('env'), options = favEnv.getGlobal('options'), bcEnv = curP.className, bc = bcEnv + '-additions'; 43 | 44 | var languages = ['en', 'ru'], defaultLangugage = KellyLoc.detectLanguage(); 45 | 46 | for (var i = 0; i < languages.length; i++) { 47 | if (defaultLangugage.indexOf(languages[i]) == -1) { 48 | css += '.' + bc + '-language-' + languages[i] + ' { display : none; }'; 49 | } 50 | } 51 | 52 | KellyTools.addCss(bc + '-language', css); 53 | 54 | KellyTools.tplClass = bc; 55 | 56 | for (var i = 0; i < pModules.length; i++) { 57 | 58 | var p = pModules[i].getInstance(), tplName = '', tplData = ''; 59 | 60 | if (!defaultLinks && p.extLinks) defaultLinks = p.extLinks; 61 | 62 | if (p.profile == 'recorder') { 63 | 64 | tplName = 'profile-selector-recorder'; 65 | tplData = { 66 | CURRENT : curP.profile == p.profile, 67 | }; 68 | 69 | } else { 70 | 71 | tplName = 'profile-selector'; 72 | tplData = { 73 | HOSTLIST : p.hostList.join(', '), 74 | PROFILEID : p.profile, 75 | CURRENT : curP.profile == p.profile, 76 | PROFILENAME : KellyLoc.s('', 'options_page_custom_cfg', {PROFILENAME : KellyTools.getCamelWord(p.profile)}), 77 | }; 78 | } 79 | 80 | pModulesHtml += KellyTools.getTpl(request.data.loadedData, tplName, tplData); 81 | } 82 | 83 | for (var i = 0; i < KellyAdditionsForm.menu.length; i++) { 84 | menuHtml += KellyTools.getTpl(request.data.loadedData, 'menu-item', {NAME : KellyLoc.s(KellyAdditionsForm.menu[i], KellyAdditionsForm.menu[i]), TARGET : KellyAdditionsForm.menu[i]}); 85 | } 86 | 87 | KellyTools.setHTMLData(container, KellyTools.getTpl(request.data.loadedData, 'page', { 88 | MODULES : pModulesHtml, 89 | MENU : menuHtml, 90 | HIDDENCLASS : bcEnv + '-hidden', 91 | INSTALL_FF : defaultLinks['install_ff'], 92 | INSTALL_CHROME : defaultLinks['install_chrome'], 93 | AUTHOR : defaultLinks['author'], 94 | GITHUB : defaultLinks['github'], 95 | PP : defaultLinks['pp'], 96 | })); 97 | 98 | for (var i = 0; i < pModules.length; i++) { 99 | KellyAdditionsForm.initToggleProfile(pModules[i].getInstance(), curP == pModules[i].getInstance() ? favEnv : false); 100 | } 101 | 102 | var selectMenu = function(el) { 103 | 104 | var opened = el.parentElement.classList.contains(bc + '-active'); 105 | 106 | for (var i = 0; i < KellyAdditionsForm.menu.length; i++) { 107 | 108 | container.getElementsByClassName(bc + '-' + KellyAdditionsForm.menu[i] + '-menu-item')[0].parentElement.classList.remove(bc + '-active'); 109 | container.getElementsByClassName(bc + '-' + KellyAdditionsForm.menu[i])[0].classList.add(bcEnv + '-hidden'); 110 | container.getElementsByClassName(bc + '-' + KellyAdditionsForm.menu[i])[0].classList.remove(bcEnv + '-active'); 111 | } 112 | 113 | container.getElementsByClassName(el.getAttribute('data-target') + '-menu-item')[0].parentElement.classList.add(bc + '-active'); 114 | container.getElementsByClassName(el.getAttribute('data-target'))[0].classList.remove(bcEnv + '-hidden'); 115 | container.getElementsByClassName(el.getAttribute('data-target'))[0].classList.add(bcEnv + '-active'); 116 | } 117 | 118 | var menu = container.getElementsByClassName(bc + '-menu-item'); 119 | for (var i = 0; i < menu.length; i++) { 120 | 121 | menu[i].onclick = function() { 122 | selectMenu(this); return false; 123 | }; 124 | 125 | if (menu[i].getAttribute('data-target').indexOf(pageId) != -1) { 126 | selectMenu(menu[i]); 127 | } 128 | } 129 | 130 | var heart = container.getElementsByClassName(bc + '-heart'); 131 | var updateHeartsDisplay = function() { 132 | for (var i = 0; i < heart.length; i++) { 133 | heart[i].style.display = ''; 134 | if (options.toolbar.heartHidden && heart[i].classList.contains(bc + '-heart-hide')) { 135 | heart[i].style.display = 'none'; 136 | } else if (!options.toolbar.heartHidden && heart[i].classList.contains(bc + '-heart-show')) { 137 | heart[i].style.display = 'none'; 138 | } 139 | } 140 | } 141 | for (var i = 0; i < heart.length; i++) { 142 | heart[i].onclick = function() { 143 | 144 | if (this.classList.contains(bc + '-heart-hide')) { 145 | options.toolbar.heartHidden = true; 146 | } else { 147 | options.toolbar.heartHidden = false; 148 | } 149 | 150 | favEnv.getToolbar().init(); 151 | favEnv.save('cfg'); 152 | updateHeartsDisplay(); 153 | return false; 154 | }; 155 | } 156 | 157 | updateHeartsDisplay(); 158 | }); 159 | }, 160 | } -------------------------------------------------------------------------------- /extension/lib/kellyCAbout.js: -------------------------------------------------------------------------------- 1 | // related to About page 2 | 3 | var KellyCAbout = new Object(); 4 | KellyCAbout.language = 'en'; 5 | 6 | KellyCAbout.showPage = function(cfg) { 7 | 8 | var handler = KellyCAbout; 9 | handler.cfg = cfg; 10 | 11 | var url = new URL(window.location.href), mode = url.searchParams.get('mode'); 12 | if (!mode || ['update', 'about'].indexOf(mode) == -1) mode = 'about'; 13 | 14 | 15 | document.body.classList.add('mode-' + mode); 16 | 17 | if (KellyCAbout.jr) document.body.classList.add('env-jr'); 18 | 19 | handler.container = document.getElementsByClassName('notice-' + handler.language)[0]; 20 | handler.container.style.display = ''; 21 | 22 | if (mode == 'update') {} 23 | 24 | handler.container.getElementsByClassName('version')[0].innerText = KellyTools.getBrowser().runtime.getManifest().version; 25 | handler.bgManager = new KellyNradiowaveBg(); 26 | handler.bgManager.init(); 27 | } 28 | 29 | KellyCAbout.initVideos = function() { 30 | 31 | var video = document.querySelectorAll('video'); 32 | for (var i = 0; i < video.length; i++) { 33 | video[i].onclick = function() { 34 | 35 | if (this.paused) { 36 | this.play(); 37 | } else { 38 | this.pause(); 39 | } 40 | 41 | return false; 42 | } 43 | } 44 | } 45 | 46 | KellyCAbout.initSpoilers = function() { 47 | 48 | var spoiler = document.querySelectorAll('.spoiler-show'); 49 | for (var i = 0; i < spoiler.length; i++) { 50 | spoiler[i].onclick = function() { 51 | var target = this.parentElement.querySelector('.spoiler'); 52 | if (!target) return true; 53 | 54 | if (target.classList.contains('spoiler-hidden')) target.classList.remove('spoiler-hidden'); 55 | else target.classList.add('spoiler-hidden'); 56 | return false; 57 | } 58 | } 59 | } 60 | 61 | KellyCAbout.init = function() 62 | { 63 | var jrDb = 'kelly_cfg_joyreactor_config'; 64 | 65 | KellyTools.getBrowser().runtime.sendMessage({ 66 | method: "getApiStorageItem", 67 | dbName : jrDb, 68 | }, function(response) { 69 | 70 | if (!response.item || response.item === null || !response.item[jrDb]) { 71 | KellyCAbout.jr = false; 72 | } else { 73 | KellyCAbout.jr = true; 74 | } 75 | 76 | var lang = KellyTools.getBrowser().i18n.getUILanguage(); 77 | if (lang.indexOf('ru') != -1) KellyCAbout.language = 'ru'; 78 | 79 | KellyCAbout.initSpoilers(); 80 | KellyCAbout.initVideos(); 81 | KellyCAbout.showPage(); 82 | 83 | }); 84 | } -------------------------------------------------------------------------------- /extension/lib/kellyDispetcherNetRequest.js: -------------------------------------------------------------------------------- 1 | // compatibility \ testing for manifest v3 2 | 3 | // createObjectURL - dosnt work from background (window - is undefined), so base64 transport method is impossible to implement 4 | // downloader.download - api - crashs browser if you try to download blob - tested on 88.0.4324.96 chrome 5 | 6 | // check rules acception - some passed only after page reload. check difference between dinamic rules and session rules 7 | // CONNECTED Error already connected - check tab disconnect 8 | // todo - keep alive 9 | 10 | // todo - предусмотреть инструменты для фикса 301 редиректов которые легко учесть в webRequestах но нельзя в declarativeNetRequest - возможно подстановка referer так же сработает 11 | // например добавлять в редирект контрольный get параметр или cookie и по ней отсеивать для простановки referer (+2 правила на каждую картинку) 12 | 13 | var KellyEDispetcherDR = { 14 | declaredRulesId : 1000, 15 | }; 16 | 17 | KellyEDispetcherDR.init = function() { 18 | 19 | if (KellyTools.getManifestVersion() > 2) { 20 | 21 | KellyEDispetcher.api.declarativeNetRequest.getSessionRules(function(rules) { 22 | 23 | if (KellyTools.getBrowser().runtime.lastError) { 24 | KellyTools.log('Error : ' + KellyTools.getBrowser().runtime.lastError.message, 'KellyEDispetcher | declarativeNetRequest'); 25 | return; 26 | } 27 | 28 | if (rules.length > 0) { 29 | for (var i = 0; i < rules.length; i++) { 30 | if (KellyEDispetcherDR.declaredRulesId < rules[i].id) { 31 | KellyEDispetcherDR.declaredRulesId = rules[i].id + 1; 32 | } 33 | } 34 | } 35 | }); 36 | 37 | KellyEDispetcher.events.push({onTabConnect : KellyEDispetcherDR.onTabConnect}); 38 | } 39 | } 40 | 41 | KellyEDispetcherDR.cleanupSessionRulesForTab = function(tabId, onReady) { 42 | 43 | KellyEDispetcher.api.declarativeNetRequest.getSessionRules(function(rules) { 44 | 45 | if (KellyTools.getBrowser().runtime.lastError) { 46 | KellyTools.log('Error : ' + KellyTools.getBrowser().runtime.lastError.message, 'KellyEDispetcher | declarativeNetRequest'); 47 | if (onReady) onReady(false); 48 | return; 49 | } 50 | 51 | if (rules.length > 0) { 52 | var ids = []; 53 | for (var i = 0; i < rules.length; i++) { 54 | if (rules[i].condition.tabIds && rules[i].condition.tabIds.indexOf(tabId) !== -1) { 55 | ids.push(rules[i].id); 56 | } 57 | } 58 | 59 | KellyEDispetcher.api.declarativeNetRequest.updateSessionRules({addRules : [], removeRuleIds : ids}, function() { 60 | 61 | KellyTools.log('[Cleanup] session Rules | Num : ' + ids.length + ' [TabId : ' + tabId + '] ', 'KellyEDispetcher | declarativeNetRequest'); 62 | 63 | if (KellyTools.getBrowser().runtime.lastError) { 64 | KellyTools.log('Error : ' + KellyTools.getBrowser().runtime.lastError.message, 'KellyEDispetcher | declarativeNetRequest'); 65 | if (onReady) onReady(false); 66 | return; 67 | } else { 68 | if (onReady) onReady(true); 69 | } 70 | 71 | }); 72 | } else { 73 | 74 | KellyTools.log('[Cleanup] session Rules | Num : [nothing to cleanup] [TabId : ' + tabId + '] ', 'KellyEDispetcher | declarativeNetRequest'); 75 | 76 | if (onReady) onReady(true); 77 | } 78 | }); 79 | } 80 | 81 | /* 82 | tabData request rules described in kellyDispetcher 83 | */ 84 | 85 | KellyEDispetcherDR.addRequestListeners = function(tabData, onRegistered) { 86 | 87 | tabData.declaredRules = []; 88 | 89 | var addRule = function(params) { 90 | 91 | var priority = 2; 92 | 93 | // match any url - used in downloader to prevent 301 redirects go without referer from extension page 94 | 95 | if (typeof params.matches == "string" && params.matches == 'ANY') { 96 | priority = 1; 97 | params.matches = '|http*'; 98 | } 99 | 100 | var responseHeaders, requestHeaders; 101 | 102 | if (typeof params.additionResponseHeaders != 'undefined') { 103 | 104 | responseHeaders = []; 105 | 106 | for (var key in params.additionResponseHeaders) { 107 | 108 | responseHeaders.push({"header" : key, "operation" : "set", "value" : params.additionResponseHeaders[key]}); 109 | } 110 | 111 | } else { 112 | 113 | responseHeaders = [ 114 | { "header" : "Access-Control-Allow-Origin", "operation" : "set", "value": "*" }, 115 | 116 | { "header" : "Pragma-directive", "operation" : "set", "value": "no-cache" }, 117 | { "header" : "Cache-directive", "operation" : "set", "value": "no-cache" }, 118 | { "header" : "Cache-control", "operation" : "set", "value": "no-cache" }, 119 | { "header" : "Pragma", "operation" : "set", "value": "no-cache" }, 120 | { "header" : "Expires", "operation" : "set", "value": "0" }, 121 | ] 122 | } 123 | 124 | if (typeof params.additionRequestHeaders != 'undefined') { 125 | 126 | requestHeaders = []; 127 | 128 | for (var key in params.additionRequestHeaders) { 129 | 130 | requestHeaders.push({"header" : key, "operation" : "set", "value" : params.additionRequestHeaders[key]}); 131 | } 132 | 133 | } else { 134 | 135 | requestHeaders = [ 136 | { "header" : "cache-control", "operation" : "set", "value" : "no-cache, must-revalidate, post-check=0, pre-check=0" }, 137 | { "header" : "pragma", "operation" : "set", "value" : 'no-cache' }, 138 | { "header" : "Referer", "operation" : "set", "value" : params.referrer }, 139 | // { "header" : "Origin", "operation" : "set", "value" : params.referrer }, 140 | ]; 141 | 142 | } 143 | 144 | if (typeof params.matches == "string") { 145 | params.matches = [params.matches]; 146 | } 147 | 148 | for (var i = 0; i < params.matches.length; i++) { 149 | 150 | KellyEDispetcherDR.declaredRulesId++; 151 | 152 | tabData.declaredRules.push({ 153 | "id" : KellyEDispetcherDR.declaredRulesId, // tabData.declaredRules.length + 1, 154 | "action": { 155 | "type" : "modifyHeaders", 156 | "requestHeaders" : requestHeaders, 157 | "responseHeaders" : responseHeaders, 158 | }, 159 | "condition": { 160 | "urlFilter" : params.matches[i], 161 | "resourceTypes" : tabData.types ? tabData.types : ['main_frame', 'image', 'xmlhttprequest', 'media'], 162 | "tabIds" : [tabData.id], 163 | }, 164 | "priority" : priority, 165 | }); 166 | } 167 | } 168 | 169 | if (tabData.referrer) { 170 | addRule({matches : KellyTools.getHostlistMatches(tabData.hostList, true), referrer : tabData.referrer}); 171 | } 172 | 173 | if (tabData.urlMap) { 174 | for (var i = 0; i < tabData.urlMap.length; i++) { 175 | 176 | addRule({ 177 | matches : tabData.urlMap[i][0], 178 | referrer : tabData.urlMap[i][1], 179 | additionRequestHeaders : tabData.urlMap[i][2], 180 | additionResponseHeaders : tabData.urlMap[i][3], 181 | }); 182 | } 183 | } 184 | 185 | KellyEDispetcher.callEvent('onBeforeUpdateNetRequestRules', tabData); 186 | 187 | KellyTools.getBrowser().declarativeNetRequest.updateSessionRules({addRules : tabData.declaredRules, removeRuleIds : []}, function() { 188 | 189 | if (KellyTools.getBrowser().runtime.lastError) { 190 | KellyTools.log('Error : ' + KellyTools.getBrowser().runtime.lastError.message, 'KellyEDispetcher | declarativeNetRequest'); 191 | } else { 192 | 193 | KellyTools.log('[ADD] session Rules | Num : ' + tabData.declaredRules.length + ' [TabId : ' + tabData.id + ']', 'KellyEDispetcher | declarativeNetRequest'); 194 | } 195 | 196 | onRegistered(); 197 | }); 198 | 199 | } 200 | 201 | KellyEDispetcherDR.onTabConnect = function(self, data) { 202 | 203 | var port = data.port, reconect = false; 204 | var tabData = false; 205 | 206 | KellyTools.log('[Downloader] CONNECTED Tab ' + port.sender.tab.id, 'KellyEDispetcher | declarativeNetRequest'); 207 | 208 | // Check is extension from front was already connected before 209 | // This can happen IN case worker was killed, or some other unpredicted event happend (port closed, page reloaded without callback, etc.) 210 | 211 | for (var i = 0; i < KellyEDispetcher.downloaderTabs.length; i++) { 212 | if (KellyEDispetcher.downloaderTabs[i].id == port.sender.tab.id) { 213 | KellyTools.log('[Downloader] Tab was already connected : reset connection, change port', 'KellyEDispetcher | declarativeNetRequest'); 214 | reconect = true; 215 | 216 | tabData = KellyEDispetcher.downloaderTabs[i]; 217 | tabData.closePort(); 218 | 219 | tabData.port = port; 220 | break; 221 | } 222 | } 223 | 224 | var onDownloaderMessage = function(request) { 225 | 226 | var response = { 227 | notice : false, 228 | message : 'ok', 229 | method : request.method, 230 | ready : function() { 231 | 232 | if (response.notice) KellyTools.log(response.notice, 'KellyEDispetcher [PORT]'); 233 | 234 | tabData.port.postMessage({method : response.method, message : response.message}); 235 | } 236 | } 237 | 238 | if (request.method == 'registerDownloader') { 239 | 240 | tabData.resetEvents('Init new tab', function() { 241 | 242 | response.notice = 'Update registerDownloader request modifiers'; 243 | response.message = request.disable ? "disabled" : "registered"; 244 | 245 | if (!request.disable) { 246 | 247 | tabData.referrer = request.referrer; 248 | tabData.types = request.types; 249 | tabData.hostList = request.hostList; 250 | 251 | if (request.urlMap) tabData.urlMap = request.urlMap; 252 | if (tabData.urlMap && request.unlistedReferer) tabData.urlMap.push(['ANY', request.unlistedReferer]); 253 | 254 | KellyEDispetcherDR.addRequestListeners(tabData, response.ready); 255 | 256 | tabData.eventsEnabled = true; 257 | 258 | } else { 259 | 260 | tabData.eventsEnabled = false; 261 | response.ready(); 262 | 263 | } 264 | 265 | }); 266 | 267 | } else if (request.method == 'setDebugMode') { 268 | 269 | KellyTools.DEBUG = request.state; 270 | response.notice = 'Tab loaded in debug mode, switch background process debug mode to ' + (KellyTools.DEBUG ? 'TRUE' : 'FALSE'); 271 | response.ready(); 272 | 273 | } else if (request.method == 'updateUrlMap') { 274 | 275 | tabData.resetEvents('Update url map', function() { 276 | 277 | if (!tabData.eventsEnabled) { 278 | 279 | response.message = "tab not initialized. use registerDownloader method first"; 280 | KellyTools.log('[updateUrlMap] ' + response.message); 281 | 282 | response.ready(); 283 | return; 284 | } 285 | 286 | if (request.urlMap) { 287 | tabData.urlMap = request.urlMap; 288 | } 289 | 290 | if (tabData.urlMap && request.unlistedReferer) tabData.urlMap.push(['ANY', request.unlistedReferer]); 291 | 292 | KellyEDispetcherDR.addRequestListeners(tabData, response.ready); 293 | 294 | }); 295 | 296 | } else { 297 | 298 | KellyTools.log('Unknown request', 'KellyEDispetcher [PORT]'); 299 | KellyTools.log(request); 300 | } 301 | } 302 | 303 | if (tabData === false) { 304 | 305 | tabData = {port : port, tab : port.sender.tab, id : port.sender.tab.id, declaredRules : [], eventsEnabled : false}; 306 | tabData.resetEvents = function(reason, onReady) { 307 | 308 | KellyEDispetcherDR.cleanupSessionRulesForTab(tabData.id, function() { 309 | 310 | KellyTools.log('resetEvents [READY] | Reason : ' + (reason ? reason : 'no reason'), 'KellyEDispetcher [PORT]'); 311 | tabData.declaredRules = []; 312 | if (onReady) onReady(); 313 | }); 314 | } 315 | 316 | tabData.closePort = function() { 317 | tabData.port.onMessage.removeListener(onDownloaderMessage); 318 | tabData.port.disconnect(); 319 | } 320 | 321 | KellyEDispetcher.downloaderTabs.push(tabData); 322 | } 323 | 324 | tabData.port.postMessage({method : 'onPortCreate', message : 'connected', isDownloadSupported : KellyEDispetcher.isDownloadSupported()}); 325 | tabData.port.onMessage.addListener(onDownloaderMessage); 326 | 327 | tabData.port.onDisconnect.addListener(function(p){ 328 | 329 | KellyTools.log('[DISCONECTED] | Reason : ' + (p.error ? p.error : 'Close tab'), 'KellyEDispetcher [PORT]'); 330 | tabData.resetEvents('Disconect tab'); 331 | tabData.closePort(); 332 | 333 | if (KellyEDispetcher.downloaderTabs.indexOf(tabData) != -1) KellyEDispetcher.downloaderTabs.splice(KellyEDispetcher.downloaderTabs.indexOf(tabData), 1); 334 | }); 335 | 336 | return true; 337 | } 338 | 339 | KellyEDispetcherDR.init(); -------------------------------------------------------------------------------- /extension/lib/kellyLoc.js: -------------------------------------------------------------------------------- 1 | var KellyLoc = new Object(); 2 | 3 | // buffered i18n.getMessage data 4 | KellyLoc.locs = {}; 5 | KellyLoc.browser = -1; 6 | 7 | // deprecated, detectLanguage not required for i18n mode, - better replace to chrome.i18n.getAcceptLanguages(callback?: function,) if needed 8 | KellyLoc.detectLanguage = function() { 9 | 10 | var language = window.navigator.userLanguage || window.navigator.language; 11 | if (language) { 12 | if (language.indexOf('-') != -1) language = language.split('-')[0]; 13 | 14 | language = language.trim(); 15 | 16 | return language; 17 | } else return this.defaultLanguage; 18 | 19 | } 20 | 21 | KellyLoc.parseText = function(text, vars) { 22 | 23 | if (!text) return ''; 24 | 25 | if (vars) { 26 | for (var key in vars){ 27 | if (typeof vars[key] != 'function') { 28 | text = text.replace('__' + key + '__', vars[key]); 29 | } 30 | } 31 | } 32 | 33 | return text; 34 | } 35 | 36 | KellyLoc.s = function(defaultLoc, key, vars) { 37 | 38 | if (this.locs[key]) return this.parseText(this.locs[key], vars); 39 | 40 | if (this.browser == -1) this.browser = KellyTools.getBrowser(); 41 | 42 | if (!this.browser || !this.browser.i18n || !this.browser.i18n.getMessage) return this.parseText(defaultLoc, vars); 43 | 44 | this.locs[key] = this.browser.i18n.getMessage(key); 45 | if (!this.locs[key]) this.locs[key] = defaultLoc; 46 | 47 | return this.parseText(this.locs[key], vars); 48 | } -------------------------------------------------------------------------------- /extension/lib/kellyNradiowaveBg.js: -------------------------------------------------------------------------------- 1 | function KellyNradiowaveBg(cfg) { 2 | 3 | var bgItem = false; // current loaded item 4 | var character = false; 5 | var characterGhost = false; 6 | var order = false; 7 | 8 | var updateTimer = false; 9 | 10 | var loader = {image : document.createElement('IMG'), imageGhost : document.createElement('IMG')}; 11 | 12 | var bgItems = [ 13 | {src : 'light1.png', xAnim : {offset : 0, time : 40}, yAnim : {offsetX : 'center', time : 40}}, 14 | {src : 'light2.png', xAnim : {offset : 0, time : 40}, yAnim : {offsetX : 'center', time : 40}}, 15 | 16 | {src : 'bg5.png', xAnim : {offset : 0, time : 40}, yAnim : {offsetX : 'center', time : 40}}, 17 | {src : 'bg2.png', xAnim : {offset : 0, time : 40}, yAnim : {offsetX : 'center', time : 40}}, 18 | ]; 19 | 20 | function shuffleArray(array) { 21 | var currentIndex = array.length, randomIndex; 22 | 23 | while (currentIndex != 0) { 24 | 25 | randomIndex = Math.floor(Math.random() * currentIndex); 26 | currentIndex--; 27 | 28 | [array[currentIndex], array[randomIndex]] = [ 29 | array[randomIndex], array[currentIndex]]; 30 | } 31 | 32 | return array; 33 | } 34 | 35 | function getNextRandItem() { 36 | 37 | if (!order) { 38 | 39 | order = localStorage.getItem('kelly-bg-order'); 40 | cursor = parseInt(localStorage.getItem('kelly-bg-cursor')); 41 | 42 | if (isNaN(cursor)) cursor = 0; 43 | 44 | if (order) order = order.split(','); 45 | if (!order || order.length != bgItems.length) order = false; 46 | 47 | if (!order) { 48 | 49 | order = []; 50 | for (var i = 0; i < bgItems.length; i++) order.push(i); 51 | 52 | // order = shuffleArray(order); 53 | 54 | } 55 | 56 | } else { 57 | 58 | cursor++; 59 | if (cursor > order.length-1) cursor = 0; 60 | } 61 | 62 | if (typeof bgItems[cursor] == 'undefined') { 63 | console.log('item ' + cursor + ' was removed - reset order'); 64 | cursor = 0; 65 | } 66 | 67 | localStorage.setItem('kelly-bg-order', order.join(',')); 68 | localStorage.setItem('kelly-bg-cursor', cursor); 69 | 70 | 71 | return order[cursor]; 72 | } 73 | 74 | var handler = this; 75 | 76 | var addCss = function(id, css, clean) { 77 | 78 | var style = document.getElementById(id), head = document.head || document.getElementsByTagName('head')[0]; 79 | if (!style) { 80 | 81 | style = document.createElement('style'); 82 | style.type = 'text/css'; 83 | style.id = id; 84 | head.appendChild(style); 85 | 86 | } 87 | 88 | if (style.styleSheet){ 89 | 90 | if (clean) style.styleSheet.cssText = ''; 91 | style.styleSheet.cssText += css; 92 | 93 | } else { 94 | 95 | if (clean) style.innerHTML = ''; 96 | style.appendChild(document.createTextNode(css)); 97 | } 98 | 99 | } 100 | 101 | var getRand = function(min, max) { 102 | 103 | min = Math.ceil(min); 104 | max = Math.floor(max); 105 | 106 | return Math.floor(Math.random() * (max - min + 1)) + min; 107 | } 108 | 109 | var getViewport = function() { 110 | 111 | var elem = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; 112 | return { screenHeight: elem.clientHeight, screenWidth: elem.clientWidth}; 113 | } 114 | 115 | var getBgImageSize = function(imageBounds) { 116 | 117 | var bgSize = {}, viewport = getViewport(); 118 | if (viewport.screenHeight > viewport.screenWidth) { // resize image by screen height 119 | 120 | // viewport.screenWidth += bgItem.xAnim.maxMove; 121 | 122 | var imageRatio = viewport.screenHeight / imageBounds.h; 123 | 124 | bgSize.w = imageRatio * imageBounds.w; 125 | bgSize.h = viewport.screenHeight; 126 | 127 | if (bgSize.w < viewport.screenWidth) { 128 | 129 | imageRatio = viewport.screenWidth / imageBounds.w; 130 | 131 | bgSize.w = viewport.screenWidth; 132 | bgSize.h = imageRatio * imageBounds.h; 133 | 134 | } 135 | 136 | } else { // by screen width 137 | 138 | // viewport.screenHeight += bgItem.yAnim.maxMove; 139 | 140 | var imageRatio = viewport.screenWidth / imageBounds.w; 141 | 142 | bgSize.w = viewport.screenWidth; 143 | bgSize.h = imageRatio * imageBounds.h; 144 | 145 | if (bgSize.h < viewport.screenHeight) { 146 | 147 | imageRatio = viewport.screenHeight / imageBounds.h; 148 | 149 | bgSize.w = imageRatio * imageBounds.w; 150 | bgSize.h = viewport.screenHeight; 151 | } 152 | 153 | } 154 | 155 | return bgSize; 156 | } 157 | 158 | this.resetLoad = function() { 159 | 160 | loader.inUse = false; 161 | 162 | loader.imageGhost.src = ''; 163 | loader.imageGhost.onload = function() {}; 164 | loader.imageGhost.onerror = function() {}; 165 | 166 | loader.image.src = ''; 167 | loader.image.onload = function() {}; 168 | loader.image.onerror = function() {}; 169 | } 170 | 171 | this.loadBgItem = function(itemN) { 172 | 173 | if (typeof itemN == 'number') bgItem = bgItems[itemN]; 174 | 175 | if (!bgItem || itemN == 'random') { 176 | itemN = getNextRandItem(); 177 | bgItem = bgItems[itemN]; 178 | } 179 | 180 | handler.resetLoad(); 181 | loader.inUse = true; 182 | 183 | //character.classList.remove('fade-in'); 184 | 185 | loader.imageGhost.src = 'https://nradiowave.ru//style/Default/img/nradiowave/bg/o_' + bgItem.src; 186 | loader.imageGhost.onload = function() { 187 | 188 | var imageBounds = {w : this.naturalWidth, h : this.naturalHeight}; 189 | var bgSize = getBgImageSize(imageBounds); 190 | 191 | characterGhost.style.backgroundImage = 'url(' + this.src + ')'; 192 | characterGhost.style.backgroundSize = bgSize.w + 'px ' + bgSize.h + 'px'; 193 | 194 | characterGhost.classList.add('fade-in'); 195 | 196 | 197 | loader.image.src = 'https://nradiowave.ru/style/Default/img/nradiowave/bg/' + bgItem.src; 198 | loader.image.onload = function() { 199 | 200 | var imageBounds = {w : this.naturalWidth, h : this.naturalHeight}; 201 | var viewport = getViewport(), bgSize = getBgImageSize(imageBounds), workAnim = 'xAnim'; 202 | 203 | 204 | if (viewport.screenHeight > viewport.screenWidth) { // resize image by screen height 205 | 206 | if (!bgItem.xAnim.offset) bgItem.xAnim.offset = 0; 207 | if (!bgItem.xAnim.maxMove) bgItem.xAnim.maxMove = bgSize.w - viewport.screenWidth - bgItem.xAnim.offset; 208 | 209 | character.style.animation = 'bg-move-x-'+ itemN +' ' + bgItem.xAnim.time + 's ease-in-out infinite'; 210 | 211 | if (bgItem.xAnim.offsetY) { 212 | character.style.backgroundPositionY = bgItem.xAnim.offsetY == 'center' ? 'center' : '-' + bgItem.xAnim.offsetY + 'px'; 213 | } 214 | 215 | } else { // by screen width 216 | 217 | workAnim = 'yAnim'; 218 | if (!bgItem.yAnim.offset) bgItem.yAnim.offset = 0; 219 | if (!bgItem.yAnim.maxMove) bgItem.yAnim.maxMove = bgSize.h - viewport.screenHeight - bgItem.yAnim.offset; 220 | 221 | character.style.animation = 'bg-move-'+ itemN +' ' + bgItem.yAnim.time + 's ease-in-out infinite'; 222 | 223 | if (bgItem.yAnim.offsetX) { 224 | character.style.backgroundPositionX = bgItem.yAnim.offsetX == 'center' ? 'center' : '-' + bgItem.yAnim.offsetX + 'px'; 225 | } 226 | } 227 | 228 | if (!bgItem[workAnim].time) { 229 | bgItem[workAnim].time = 0.2 * bgItem[workAnim].maxMove * 0.58; // 58px per 20s 230 | } 231 | 232 | handler.loadBgItemDelay('random', bgItem[workAnim].time * 0.5 * 1000);// bgItem[workAnim].time * 1.5 * 1000); 233 | 234 | 235 | var css = '\ 236 | @keyframes bg-move-'+ itemN +' {\ 237 | 0%, 100% {\ 238 | background-position-y: -' + (bgItem.yAnim.revers ? bgItem.yAnim.maxMove : bgItem.yAnim.offset) + 'px;\ 239 | }\ 240 | 50% {\ 241 | background-position-y: -' + (bgItem.yAnim.revers ? bgItem.yAnim.offset : bgItem.yAnim.maxMove) + 'px;\ 242 | }\ 243 | }\ 244 | \ 245 | @keyframes bg-move-x-'+ itemN +' {\ 246 | 0%, 100% {\ 247 | background-position-x: -' + bgItem.xAnim.offset + ';\ 248 | }\ 249 | 50% {\ 250 | background-position-x: -' + bgItem.xAnim.maxMove + 'px;\ 251 | }\ 252 | }\ 253 | '; 254 | 255 | addCss('random-character-anims', css, true); 256 | 257 | character.style.backgroundImage = 'url(' + this.src + ')'; 258 | character.style.backgroundSize = bgSize.w + 'px ' + bgSize.h + 'px'; 259 | 260 | character.classList.add('fade-in'); 261 | setTimeout(function() {characterGhost.classList.remove('fade-in');}, 900); 262 | handler.resetLoad(); 263 | 264 | } 265 | } 266 | 267 | 268 | } 269 | 270 | this.loadBgItemDelay = function(itemN, delay) { 271 | if (updateTimer !== false) clearTimeout(updateTimer); 272 | 273 | updateTimer = setTimeout(function() { 274 | 275 | updateTimer = false; 276 | character.classList.remove('fade-in'); 277 | setTimeout(function() { handler.loadBgItem(itemN); }, 1000); 278 | 279 | }, delay); 280 | } 281 | 282 | this.init = function() { 283 | 284 | character = document.getElementsByClassName('random-character-main')[0]; 285 | characterGhost = document.getElementsByClassName('random-character-ghost')[0]; 286 | 287 | handler.loadBgItem('random'); 288 | 289 | window.addEventListener('resize', function() { 290 | character.classList.remove('fade-in'); 291 | character.style.backgroundSize = 'cover'; 292 | character.style.backgroundPositionX = '0px'; 293 | character.style.backgroundPositionY = '0px'; 294 | character.style.animation = ''; 295 | 296 | handler.loadBgItemDelay('current', 100); 297 | }); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /extension/lib/kellyThreadWork.js: -------------------------------------------------------------------------------- 1 | function KellyThreadWork(cfg) { 2 | 3 | var jobs = []; 4 | var maxThreads = 1; // эксперименты чреваты баном за спам запросами, пробовать только за впном или если у вас динамический адрес 5 | var threads = []; 6 | 7 | var timeout = 15; // таймаут ожидания загрузки страницы в секундах, в обработчик onLoad попадет response = false 8 | var timeoutOnEnd = [2, 2.2, 1.1, 3.4, 4, 3, 3, 3, 4.6]; // таймер перехода к следующей задаче после выполнения - сек. рандомно из массива 9 | 10 | // long pause every 11 | var pauseEvery = [40,30,20]; 12 | var untilPause = getRandom(pauseEvery); 13 | var pauseTimer = [10,14,20]; 14 | 15 | var events = { onProcess : false, onEnd : false }; 16 | var handler = this; 17 | var threadId = 1; 18 | var beasy = false, pause = false; 19 | 20 | function constructor(cfg) { 21 | handler.updateCfg(cfg); 22 | } 23 | 24 | function parseTimeset(text) { 25 | 26 | if (typeof text == 'number') text = KellyTools.val(text, 'string'); 27 | if (typeof text == 'string') text = KellyTools.getVarList(text, 'float'); 28 | 29 | if (text && text.length) { 30 | 31 | var list = []; 32 | 33 | for (var i = 0; i < text.length; i++) { 34 | 35 | var tmp = text[i]; 36 | 37 | if (typeof tmp != 'number') { 38 | tmp = KellyTools.val(tmp, 'float'); 39 | } 40 | 41 | if (tmp <= 0) continue; 42 | 43 | list[list.length] = tmp; 44 | } 45 | 46 | return list; 47 | 48 | } else { 49 | return []; 50 | } 51 | 52 | } 53 | 54 | function timesetToString(timeset) { 55 | var string = ''; 56 | 57 | for (var i = 0; i < timeset.length; i++) { 58 | string += string ? ',' + timeset[i] : timeset[i]; 59 | } 60 | 61 | return string; 62 | } 63 | 64 | this.getCfg = function() { 65 | return { 66 | pauseEvery : timesetToString(pauseEvery), 67 | pauseTimer : timesetToString(pauseTimer), 68 | timeoutOnEnd : timesetToString(timeoutOnEnd), 69 | timeout : timeout, 70 | maxThreads : maxThreads, 71 | } 72 | } 73 | 74 | this.pause = function(state) { 75 | 76 | var oldPause = pause; 77 | pause = state ? true : false; 78 | 79 | if (pause == false) { 80 | 81 | if (oldPause) jobContinue(); 82 | KellyTools.log('work continue', 'KellyThreadWork'); 83 | 84 | } else { 85 | 86 | 87 | for (var i = 0; i < threads.length; i++) { 88 | 89 | if (threads[i].job) { 90 | jobs.push(threads[i].job); 91 | KellyTools.log('work paused - return task back to jobs pool', 'KellyThreadWork'); 92 | } 93 | 94 | handler.clearThreadTimers(threads[i]); 95 | } 96 | 97 | threads = []; 98 | beasy = false; 99 | } 100 | } 101 | 102 | this.updateCfg = function(cfg) { 103 | 104 | if (!cfg) return; 105 | 106 | var tmp = parseTimeset(cfg.pauseEvery); 107 | if (tmp.length) { 108 | pauseEvery = tmp; 109 | untilPause = getRandom(pauseEvery); 110 | } 111 | 112 | tmp = parseTimeset(cfg.pauseTimer); 113 | if (tmp.length) pauseTimer = tmp; 114 | 115 | tmp = parseTimeset(cfg.timeoutOnEnd); 116 | if (tmp.length) timeoutOnEnd = tmp; 117 | 118 | tmp = KellyTools.val(cfg.timeout, 'float'); 119 | if (tmp > 2) timeout = tmp; 120 | 121 | tmp = KellyTools.val(cfg.maxThreads, 'int'); 122 | if (tmp >= 1 && tmp <= 15) maxThreads = tmp; 123 | } 124 | 125 | function getRandom(input) { 126 | return input[Math.floor(Math.random() * ((input.length - 1) + 1))]; 127 | } 128 | 129 | this.getJobs = function() { 130 | return jobs; 131 | } 132 | 133 | this.getThreads = function() { 134 | return threads; 135 | } 136 | 137 | this.setEvent = function(name, f) { 138 | 139 | events[name] = f; 140 | } 141 | 142 | this.stop = function(noCleanJobs) { 143 | 144 | KellyTools.log('clean job', 'KellyThreadWork'); 145 | onEnd('stop', true); 146 | } 147 | 148 | function removeThreadItem(thread) { 149 | 150 | var threadIndex = threads.indexOf(thread); 151 | if (threadIndex == -1) return false; 152 | 153 | threads.splice(threadIndex, 1); 154 | return true; 155 | } 156 | 157 | function jobContinue() { 158 | 159 | if (!jobs.length && !threads.length) { 160 | 161 | onEnd('onJobEnd'); 162 | 163 | } else if (jobs.length > 0) { 164 | 165 | var timeout = getRandom(timeoutOnEnd); 166 | 167 | if (pauseEvery && pauseEvery.length) { 168 | 169 | if (untilPause > 0) { 170 | untilPause--; 171 | KellyTools.log('before pause ' + untilPause, 'KellyThreadWork'); 172 | } else { 173 | untilPause = getRandom(pauseEvery); 174 | timeout = getRandom(pauseTimer); 175 | 176 | KellyTools.log('timeout ' + timeout + ' | new pause ' + untilPause, 'KellyThreadWork'); 177 | 178 | } 179 | } 180 | 181 | var threadPlaceholder = {}; 182 | threadPlaceholder.applayTimer = setTimeout(function() { 183 | applayJob(threadPlaceholder); 184 | }, timeout * 1000); 185 | 186 | threads.push(threadPlaceholder); 187 | } 188 | } 189 | 190 | this.onJobEnd = function(thread) { 191 | 192 | removeThreadItem(thread); 193 | 194 | if (pause) { 195 | return; 196 | } 197 | 198 | if (!thread.response) { 199 | // error 200 | KellyTools.log('job end without load document', 'KellyThreadWork'); 201 | KellyTools.log(thread, 'KellyThreadWork'); 202 | } 203 | 204 | var isUnfinished = thread.job.onLoad(handler, thread, jobs.length); // for addition sub requests, dont use unfinished state if you dont know how threads class works 205 | if (isUnfinished === true) return; 206 | 207 | if (events.onProcess) events.onProcess(jobs.length, thread); 208 | 209 | handler.clearThreadTimers(thread); 210 | 211 | if (pause) { 212 | 213 | KellyTools.log('Global pause, dont take actions', 'KellyThreadWork'); 214 | 215 | } else jobContinue(); 216 | } 217 | 218 | function applayJob(threadPlaceholder) { 219 | 220 | if (threadPlaceholder) removeThreadItem(threadPlaceholder); 221 | 222 | if (threads.length >= maxThreads || pause) { 223 | return false; 224 | } 225 | 226 | if (!jobs.length && !threads.length) { 227 | onEnd('applayJob'); 228 | return false; 229 | } 230 | 231 | if (!jobs.length) { 232 | return false; 233 | } 234 | 235 | threadId++; 236 | var thread = { 237 | job : jobs.pop(), 238 | response : false, 239 | request : false, 240 | id : threadId, 241 | error : '', 242 | } 243 | 244 | var config = thread.job.config ? thread.job.config : {method : 'GET', responseType : 'text'}; 245 | 246 | // method | responseType 247 | 248 | thread.rules = []; 249 | 250 | if (thread.job.url.indexOf('##FETCH_RULES##') != -1) { 251 | thread.job.url = thread.job.url.split('##FETCH_RULES##'); 252 | thread.rules = thread.job.url.length == 2 ? thread.job.url[1] : false; 253 | thread.job.url = thread.job.url[0]; 254 | 255 | thread.rules = thread.rules.split('&'); 256 | thread.rules.forEach(function(rule) { 257 | rule = rule.split('='); 258 | if (rule.length == 2 && rule.indexOf('mark_') == -1) config[rule[0]] = rule[1]; 259 | }); 260 | } 261 | 262 | thread.request = KellyTools.xmlRequest(thread.job.url, config, handler.createDefaultHttpRequestCallback(thread)); 263 | thread.timeoutTimer = setTimeout(function() { handler.onJobEnd(thread); }, timeout * 1000); 264 | threads.push(thread); 265 | 266 | return true; 267 | } 268 | 269 | this.createDefaultHttpRequestCallback = function(thread) { 270 | 271 | var defaultCallback = function(urlOrig, data, errorCode, errorText, controller) { 272 | 273 | if (pause) return; 274 | 275 | if (data !== false) { 276 | thread.response = data; 277 | } else { 278 | thread.response = false; 279 | thread.error = '[HTTP Request ERROR] Error code : ' + errorCode + ' | error message : ' + errorText; 280 | } 281 | 282 | handler.onJobEnd(thread); 283 | } 284 | 285 | return defaultCallback; 286 | } 287 | 288 | this.isBeasy = function() { 289 | return beasy; 290 | } 291 | 292 | this.clearThreadTimers = function(thread) { 293 | 294 | if (thread.applayTimer) { 295 | clearTimeout(thread.applayTimer); 296 | thread.applayTimer = false; 297 | } 298 | 299 | if (thread.request) { 300 | thread.request.abort(); 301 | threadrequest = false; 302 | } 303 | 304 | if (thread.timeoutTimer) { 305 | clearTimeout(thread.timeoutTimer); 306 | thread.timeoutTimer = false; 307 | } 308 | } 309 | 310 | function onEnd(cname, forced) { 311 | 312 | for (var i = 0; i < threads.length; i++) handler.clearThreadTimers(threads[i]); // stops any requests and addition timers 313 | 314 | threads = []; jobs = []; 315 | 316 | forced = forced ? true : false; 317 | beasy = false; 318 | pause = false; 319 | 320 | if (events.onEnd) events.onEnd(cname, forced); 321 | } 322 | 323 | this.exec = function() { 324 | 325 | if (this.isBeasy()) return false; 326 | 327 | if (!jobs.length) { 328 | onEnd('exec'); 329 | return false; 330 | } 331 | 332 | for (var i = 1; i <= maxThreads; i++) { 333 | if (!applayJob()) break; 334 | else beasy = true; 335 | } 336 | 337 | // job exist, but not applayed (bad maxThreads \ applayJob) 338 | 339 | if (!this.isBeasy()) { 340 | onEnd('exec_fail'); 341 | return false; 342 | } 343 | 344 | return true; 345 | } 346 | 347 | // data - page \ nik \ etc 348 | 349 | this.addJob = function(url, onLoad, data, requestCfg) { 350 | 351 | if (typeof onLoad !== 'function') { 352 | onLoad = false; 353 | } 354 | 355 | var job = { 356 | url : url, 357 | onLoad : onLoad, 358 | data : data, 359 | config : requestCfg ? requestCfg : false, 360 | }; 361 | 362 | jobs[jobs.length] = job; 363 | return job; 364 | } 365 | 366 | constructor(cfg); 367 | } -------------------------------------------------------------------------------- /extension/lib/kellyToolbar.js: -------------------------------------------------------------------------------- 1 | // todo readonly check on after delete 2 | 3 | function KellyToolbar(cfg) { 4 | 5 | var handler = this; 6 | handler.container = false; 7 | handler.cfg = {show : false, heartNewWindow : false, themeHidden : false, deselectBtn : false}; 8 | handler.className = 'toolbar'; 9 | handler.dom = {}; 10 | handler.events = { 11 | onDisplayBlock : function(mode, action, oldMode) { 12 | handler.show(handler.cfg.userCfg.enabled && mode == 'fav' && action == 'show'); 13 | }, 14 | onUpdateFilteredData : function(displayedItems) { 15 | handler.show(handler.cfg.show); 16 | }, 17 | } 18 | 19 | function constructor(cfg) { 20 | 21 | handler.container = cfg.container; 22 | handler.className = cfg.className; 23 | handler.cfg = cfg; 24 | handler.env = cfg.favController.getGlobal('env'); 25 | handler.favController = cfg.favController; 26 | } 27 | 28 | function getCatListHtml(title, filter, logic) { 29 | 30 | var html = ''; 31 | var db = handler.favController.getGlobal('fav'); 32 | 33 | if (filter.length > 0) { 34 | 35 | html = '[' + title + ''; 36 | 37 | if (filter.length == 1) { 38 | html += ' ' + KellyLoc.s('', 'toolbar_from_single_cat'); 39 | } else { 40 | if (logic == 'and') html += ' ' + KellyLoc.s('', 'toolbar_from_and_cats'); 41 | else html += ' ' + KellyLoc.s('', 'toolbar_from_or_cats'); 42 | } 43 | 44 | html += ': '; 45 | 46 | for (var i = 0; i < filter.length; i++) { 47 | if (i >= 3) { 48 | html += '...'; 49 | break; 50 | } 51 | 52 | var group = handler.favController.getStorageManager().getCategoryById(db, filter[i]); 53 | var name = group.name; 54 | if (name.length > 20) { 55 | name = name.substring(0, 20) + '...'; 56 | } 57 | 58 | if (group.id > 0) html += (i > 0 ? ', ' : '') + '' + name + ''; // todo - check - after sort - some unexist items adds to exclude filter 59 | } 60 | 61 | html += ']'; 62 | } 63 | 64 | return html; 65 | } 66 | 67 | function updateStatesInfo() { 68 | 69 | var filters = handler.favController.getFilters(); 70 | var html = ''; 71 | 72 | if (!filters.readOnly) { 73 | html += '[' + KellyLoc.s('', 'toolbar_edit_mode') + ']'; 74 | } 75 | 76 | if (filters.catFilters.length <= 0 && filters.catIgnoreFilters.length <= 0) { 77 | html += '[' + KellyLoc.s('', 'toolbar_cat_not_selected') + ']'; 78 | } else { 79 | html += getCatListHtml(KellyLoc.s('', 'toolbar_selected_images'), filters.catFilters, filters.logic); 80 | if (filters.catFilters.length > 0) html += ' '; 81 | html += getCatListHtml(KellyLoc.s('', 'toolbar_deselected_images'), filters.catIgnoreFilters, filters.logic); 82 | } 83 | 84 | KellyTools.setHTMLData(handler.dom['catlist'], '' + html + ''); 85 | } 86 | 87 | function initBlock(html, name) { 88 | 89 | handler.dom[name] = KellyTools.setHTMLData(handler.dom[name] ? handler.dom[name] : document.createElement('DIV'), html); 90 | handler.container.appendChild(handler.dom[name]); 91 | } 92 | 93 | function updateBlocksClass() { 94 | 95 | var className = handler.env.hostClass + ' ' + handler.className + ' ' + handler.className + '-' + (handler.cfg.userCfg.tiny ? 'tiny' : 'full'); 96 | if (handler.cfg.userCfg.collapsed) className += ' ' + handler.className + '-collapsed'; 97 | if (handler.cfg.show) className += ' ' + handler.className + '-shown'; 98 | 99 | handler.dom['main'].className = className + ' ' + handler.className + '-main'; 100 | handler.dom['helper-container'].className = className + ' ' + handler.className + '-helper-container'; 101 | if (handler.cfg.deselectBtn || !handler.cfg.userCfg.tiny) handler.dom['helper-container'].className += ' ' + handler.className + '-helper-enabled'; 102 | } 103 | 104 | this.init = function() { 105 | 106 | initBlock('\ 107 |
    \ 108 | \
    \ 109 | ', 'helper-container'); 110 | 111 | initBlock('\ 112 |
    \ 113 |
    \ 114 |
    \ 115 |
    \ 116 |
    \ 117 | ', 'main'); 118 | 119 | var dom = ['deselect-wrap', 'catlist', 'theme', 'collapse', 'help']; 120 | for (var i = 0; i < dom.length; i++) { 121 | handler.dom[dom[i]] = handler.container.getElementsByClassName(handler.className + '-' + dom[i])[0]; 122 | } 123 | 124 | handler.dom['help'].onclick = function() { 125 | 126 | if (handler.cfg.heartNewWindow) { 127 | 128 | KellyTools.getBrowser().runtime.sendMessage({method: "openTab", url : '/env/html/' + handler.env.profile + 'Downloader.html?tab=donate'}, function(request) {}); 129 | 130 | } else { 131 | handler.setDeselectBtn(handler.cfg.deselectBtn); // reset state 132 | handler.favController.showAdditionsDialog('additions_donate'); 133 | } 134 | 135 | return false; 136 | } 137 | 138 | handler.dom['collapse'].onclick = function() { 139 | 140 | handler.cfg.userCfg.collapsed = !handler.cfg.userCfg.collapsed; 141 | updateBlocksClass(); 142 | handler.favController.save('cfg'); 143 | } 144 | 145 | handler.dom['theme'].onclick = function() { 146 | 147 | var options = handler.favController.getGlobal('options'); 148 | var delayUpdate = false; 149 | 150 | if (document.body.classList.contains(handler.env.className + '-dark')) { 151 | 152 | document.body.classList.remove(handler.env.className + '-dark'); 153 | document.body.classList.add(handler.env.className + '-white'); 154 | options.darkTheme = false; 155 | 156 | } else { 157 | 158 | document.body.classList.add(handler.env.className + '-dark'); 159 | document.body.classList.remove(handler.env.className + '-white'); 160 | options.darkTheme = true; 161 | } 162 | 163 | if (!document.getElementById(handler.className + '-dyn-css')) { 164 | KellyTools.getBrowser().runtime.sendMessage({method: "getResources", items : [options.darkTheme ? 'dark' : 'white']}, function(request) { 165 | if (!request || !request.data.loadedData) return false; 166 | 167 | KellyTools.addCss(handler.className + '-dyn-css', KellyTools.replaceAll(request.data.loadedData, '__BASECLASS__', handler.env.className), true); 168 | handler.favController.updateImageGrid(); 169 | }); 170 | 171 | delayUpdate = true; 172 | } 173 | 174 | if (!delayUpdate) handler.favController.updateImageGrid(); 175 | handler.favController.save('cfg'); 176 | } 177 | 178 | handler.setDeselectBtn(handler.cfg.deselectBtn); 179 | } 180 | 181 | this.setDeselectBtn = function(btn) { 182 | 183 | if (!handler.dom['main']) handler.init(); 184 | 185 | handler.dom['deselect-wrap'].innerHTML = ''; 186 | handler.cfg.deselectBtn = btn; 187 | 188 | if (!btn) return; 189 | 190 | KellyTools.setHTMLData(handler.dom['deselect-wrap'], '\ 191 | \ 192 | \ 193 | '); 194 | 195 | handler.dom['deselect-all'] = document.getElementById(handler.className + '-deselect-all'); 196 | handler.dom['deselect-all'].onclick = function(e) { 197 | return btn.callback(handler, this, e); 198 | } 199 | } 200 | 201 | this.show = function(visible) { 202 | 203 | if (!handler.cfg.userCfg.enabled) return false; 204 | if (!handler.dom['main']) handler.init(); 205 | 206 | handler.cfg.show = visible; 207 | updateBlocksClass(); 208 | 209 | if (handler.cfg.show) { 210 | 211 | if (handler.cfg.themeHidden) handler.dom['theme'].style.display = 'none'; 212 | if (handler.cfg.userCfg.heartHidden) handler.dom['help'].style.display = 'none'; 213 | if (handler.cfg.userCfg.tiny) handler.dom['catlist'].innerHTML = ''; 214 | else updateStatesInfo(); 215 | } 216 | } 217 | 218 | constructor(cfg); 219 | } -------------------------------------------------------------------------------- /extension/lib/profiles/default.js: -------------------------------------------------------------------------------- 1 | // part of KellyFavItems extension 2 | // todo addEventListener 3 | 4 | function KellyProfileDefault() { 5 | 6 | var handler = this; 7 | 8 | this.hostList = []; 9 | this.webRequestsRules = false; 10 | 11 | this.profile = 'default'; 12 | this.className = 'kelly-jr-ui'; // base class for every extension container \ element 13 | 14 | this.sidebarConfig = { 15 | topMax : 0, 16 | paddingTop : 0, 17 | nDisabled : -1, // 1 - sidebar not found or hidden (jras - sidebar can be hidden) 18 | }; 19 | 20 | this.fav = false; 21 | 22 | /* imp, could be helpfull for set webreuqest rules, addition variables, depends on page environment. Unused in universal recorder */ 23 | 24 | this.setLocation = function(location) { 25 | 26 | handler.location = { 27 | protocol : location.protocol, 28 | host : location.host, 29 | href : location.href, 30 | }; 31 | 32 | handler.hostClass = handler.className + '-' + location.host.split('.').join("-"); 33 | } 34 | 35 | // calls every time o request image link for download request or for show on page 36 | 37 | this.getImageDownloadLink = function(url, full, format) { return url; } 38 | this.getStaticImage = function(url) { return url; } 39 | 40 | this.getMainContainers = function() { 41 | 42 | // todo move create buttons methods from faitems core 43 | 44 | if (!handler.mContainers) { 45 | handler.mContainers = { 46 | 47 | // public 48 | 49 | body : document.getElementById('container'), // place where to put all dynamic absolute position elements 50 | siteContent : document.getElementById('contentinner'), // site main container 51 | favContent : false, // main extension container - image grid \ options block 52 | sideBar : false, // place where to put extension sidebar block (add post \ filters menu) 53 | menu : document.getElementById('submenu'), // currently used in kellyFavItems to create menu buttons 54 | 55 | // private 56 | 57 | sideBlock : document.getElementById('sidebar'), // helps to detect width of sideBar, only for updateSidebarPosition() 58 | }; 59 | 60 | handler.mContainers.sideBar = handler.mContainers.body; 61 | 62 | if (handler.mContainers.siteContent) { 63 | handler.mContainers.favContent = document.createElement('div'); 64 | handler.mContainers.favContent.className = handler.className + '-FavContainer ' + handler.hostClass; 65 | handler.mContainers.siteContent.parentNode.insertBefore(handler.mContainers.favContent, handler.mContainers.siteContent); 66 | } 67 | 68 | if (!handler.mContainers.favContent || !handler.mContainers.body) { 69 | KellyTools.log('getMainContainers : cant create containers, check selectors', KellyTools.E_ERROR); 70 | KellyTools.log(handler.mContainers, KellyTools.E_ERROR); 71 | return false; 72 | } 73 | } 74 | 75 | return handler.mContainers; 76 | } 77 | 78 | this.events = { 79 | 80 | /* 81 | calls when new cfg | items list loaded 82 | 83 | storage - current actual storage | loadType - cfg | items | false (both) | context - undefined (from any other method) | selectDB (from options page) 84 | if return true - prevent default callback 85 | */ 86 | 87 | onStorageAfterload : function(storage, loadType, context) { return false; }, 88 | 89 | /* 90 | calls on document.ready, or if getPosts find some data 91 | if return true prevent native init environment logic (initFormatPage -> InitWorktop) 92 | */ 93 | 94 | onPageReady : function() { return false; }, 95 | 96 | /* 97 | calls after extension resources is loaded 98 | if return true prevent native init worktop logic (image viewer initialization) 99 | */ 100 | 101 | onInitWorktop : function() {return false; }, 102 | 103 | onExtensionReady : function() { 104 | handler.sidebarConfig.topMax = handler.getMainContainers().siteContent.getBoundingClientRect().top; 105 | 106 | KellyTools.addEventPListener(window, "resize", updateSidebarPosition, '_fav_dialog'); 107 | KellyTools.addEventPListener(window, "scroll", updateSidebarPosition, '_fav_dialog'); 108 | }, 109 | 110 | onSideBarShow : function(sideBarWrap, close) { 111 | 112 | if (!sideBarWrap) return; 113 | 114 | var siteSideBlock = handler.getMainContainers().sideBlock; 115 | if (siteSideBlock) { 116 | siteSideBlock.style.visibility = close ? 'visible' : 'hidden'; 117 | siteSideBlock.style.opacity = close ? '1' : '0'; 118 | } 119 | 120 | if (close) { 121 | sideBarWrap.style.top = '50px'; 122 | return; 123 | } 124 | 125 | updateSidebarProportions(sideBarWrap); 126 | updateSidebarPosition(); 127 | }, 128 | 129 | onSideBarUpdate : updateSidebarPosition, 130 | onOptionsUpdate : function(refreshPosts) {}, 131 | onDisplayBlock : function(mode, action, oldMode) {}, 132 | onBeforeGoToFavPage : function(newPage) {}, 133 | } 134 | 135 | function updateSidebarProportions(sideBarWrap) { 136 | 137 | if (sideBarWrap.className.indexOf('inline') !== -1) return; 138 | var filters = KellyTools.getElementByClass(sideBarWrap, handler.className + '-FiltersMenu'); 139 | if (filters && filters.offsetHeight > 440 && filters.className.indexOf('calculated') == -1) { 140 | 141 | var filtersBlock = KellyTools.getElementByClass(sideBarWrap, handler.className + '-FiltersMenu-container'); 142 | 143 | filtersBlock.style.maxHeight = '0'; 144 | filtersBlock.style.overflow = 'hidden'; 145 | 146 | var modalBox = KellyTools.getElementByClass(document, handler.className + '-ModalBox-main'); 147 | modalBox.style.minHeight = '0'; 148 | 149 | var modalBoxHeight = modalBox.getBoundingClientRect().height; 150 | 151 | var viewport = KellyTools.getViewport(); 152 | if (viewport.screenHeight < modalBoxHeight + filters.offsetHeight + handler.sidebarConfig.paddingTop) { 153 | filtersBlock.style.maxHeight = (viewport.screenHeight - modalBoxHeight - handler.sidebarConfig.paddingTop - 44 - handler.sidebarConfig.paddingTop) + 'px'; 154 | filtersBlock.style.overflowY = 'scroll'; 155 | 156 | } else { 157 | 158 | filtersBlock.style.maxHeight = 'none'; 159 | filtersBlock.style.overflow = 'auto'; 160 | } 161 | 162 | filters.className += ' calculated'; 163 | } 164 | } 165 | 166 | function updateSidebarPosition() { 167 | 168 | if (!handler.fav) return false; 169 | 170 | var sideBarWrap = handler.fav.getView('sidebar'), sideBlock = handler.getMainContainers().sideBlock; 171 | if (!sideBarWrap || sideBarWrap.className.indexOf('hidden') !== -1 || sideBarWrap.className.indexOf('inline') !== -1) return false; 172 | 173 | if (handler.sidebarConfig.nDisabled == -1) { // first time update position, validate sidebar block 174 | 175 | if (sideBlock && window.getComputedStyle(sideBlock).position == 'absolute') { 176 | 177 | KellyTools.log('Bad sidebar position', 'updateSidebarPosition'); 178 | handler.sidebarConfig.nDisabled = 1; 179 | } 180 | 181 | if (!sideBlock) { 182 | KellyTools.log('Sidebar not found', 'updateSidebarPosition'); 183 | handler.sidebarConfig.nDisabled = 1; 184 | } 185 | 186 | if (handler.sidebarConfig.nDisabled == 1) { 187 | 188 | var collapseButton = KellyTools.getElementByClass(sideBarWrap, handler.className + '-sidebar-collapse'); 189 | if (collapseButton) { 190 | KellyTools.classList('add', collapseButton, handler.className + '-active'); 191 | } 192 | } 193 | } 194 | 195 | if (handler.sidebarConfig.nDisabled == 1) sideBlock = false; 196 | 197 | var scrollTop = KellyTools.getScrollTop(), scrollLeft = KellyTools.getScrollLeft(); 198 | var topMax = handler.sidebarConfig.topMax, top = topMax; 199 | 200 | if (!handler.fav.sideBarLock && handler.sidebarConfig.paddingTop + scrollTop > top) top = handler.sidebarConfig.paddingTop + scrollTop; 201 | 202 | sideBarWrap.style.top = top + 'px'; 203 | 204 | if (sideBlock) { 205 | 206 | var widthBase = 0, sideBlockBounds = sideBlock.getBoundingClientRect(); 207 | sideBarWrap.style.right = 'auto'; 208 | sideBarWrap.style.left = Math.round(sideBlockBounds.left + scrollLeft) + 'px'; 209 | sideBarWrap.style.width = Math.round(sideBlockBounds.width + widthBase) + 'px'; 210 | 211 | } else { 212 | sideBarWrap.style.right = '20px'; 213 | sideBarWrap.style.left = 'auto'; 214 | } 215 | } 216 | 217 | /* imp */ 218 | 219 | this.setFav = function(fav) { 220 | handler.fav = fav; 221 | } 222 | 223 | /* not imp */ 224 | 225 | this.getRecomendedDownloadSettings = function() { 226 | 227 | var browser = KellyTools.getBrowserName(); 228 | return { 229 | transportMethod : browser == 'opera' || browser == 'chrome' ? KellyGrabber.TRANSPORT_BLOB : KellyGrabber.TRANSPORT_BLOBBASE64, 230 | requestMethod : KellyGrabber.REQUEST_FETCH 231 | } 232 | } 233 | } 234 | 235 | KellyProfileDefault.getInstance = function() { 236 | if (typeof KellyProfileDefault.self == 'undefined') { 237 | KellyProfileDefault.self = new KellyProfileDefault(); 238 | } 239 | 240 | return KellyProfileDefault.self; 241 | } -------------------------------------------------------------------------------- /extension/lib/profiles/recorder.js: -------------------------------------------------------------------------------- 1 | // part of KellyFavItems extension 2 | 3 | var KellyProfileRecorder = new Object(); 4 | KellyProfileRecorder.create = function() { 5 | 6 | KellyProfileRecorder.self = new KellyProfileDefault(); 7 | var handler = KellyProfileRecorder.self; 8 | 9 | handler.profile = 'recorder'; 10 | handler.extLinks = { 11 | 12 | pp : 'https://github.com/NC22/KellyC-Image-Downloader/wiki/%5BPP%5D-Privacy-Policy', 13 | github : 'https://github.com/NC22/KellyC-Image-Downloader', 14 | 15 | install_ff : 'https://addons.mozilla.org/ru/firefox/addon/kellyc-favorites/', 16 | install_chrome : 'https://chrome.google.com/webstore/detail/kellyc-image-downloader/mbhkdmjolnhcppnkldbdfaomeabjiofm?hl=en', 17 | install_edge : 'https://microsoftedge.microsoft.com/addons/detail/kellyc-image-downloader/dgjfegjceojpbngijkaekoihllhhdocn', 18 | install_opera : 'https://kdl.catface.ru/ru/install-opera/', 19 | 20 | author : 'https://nradiowave.catface.ru/', 21 | }; 22 | } 23 | 24 | KellyProfileRecorder.getInstance = function() { 25 | if (typeof KellyProfileRecorder.self == 'undefined') KellyProfileRecorder.create(); 26 | return KellyProfileRecorder.self; 27 | } -------------------------------------------------------------------------------- /extension/lib/recorder/filters/9gag.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilter9Gag = new Object(); 2 | KellyRecorderFilter9Gag.manifest = {host : '9gag.com', detectionLvl : ['imagePreview', 'imageOriginal', 'imageByDocument']}; 3 | 4 | KellyRecorderFilter9Gag.parseImagesDocByDriver = function(handler, data) { 5 | 6 | if (handler.url.indexOf('9gag.com') == -1) return; 7 | 8 | var pageDataRegExp = /window\._config[\s]*=[\s]*JSON\.parse\(\"\{([\s\S]*)\}\}\"\)\;[\s]*\<\/script/g 9 | var pageData = pageDataRegExp.exec(data.thread.response); 10 | 11 | if (pageData) { 12 | 13 | try { 14 | 15 | var gagData = KellyTools.parseJSON('{' + pageData[1] + '}}', true, true); // parse escaped json 16 | var bigImage = false; 17 | console.log(gagData); 18 | 19 | for (var imageId in gagData.data.post.images) { 20 | if (!bigImage || bigImage.width < gagData.data.post.images[imageId].width) { 21 | bigImage = gagData.data.post.images[imageId]; 22 | } 23 | } 24 | 25 | if (bigImage) { 26 | handler.imagesPool.push({ 27 | relatedSrc : [ bigImage.url ], 28 | relatedGroups : [['imageOriginal']] 29 | }); 30 | } 31 | 32 | } catch (e) { 33 | console.log(e); 34 | } 35 | } 36 | 37 | return true; 38 | } 39 | 40 | KellyPageWatchdog.validators.push({ 41 | url : '9gag.com', 42 | host : '9gag.com', 43 | patterns : [ 44 | ['img-9gag-fun', 'imagePreview'], 45 | ] 46 | }); 47 | 48 | KellyPageWatchdog.filters.push(KellyRecorderFilter9Gag); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/README.md: -------------------------------------------------------------------------------- 1 | Documentation available on official extension page -------------------------------------------------------------------------------- /extension/lib/recorder/filters/_validators.js: -------------------------------------------------------------------------------- 1 | KellyPageWatchdog.validators.push({url : 'catface.ru', host : 'catface.ru', patterns : [['catface.ru/get', 'imageOriginal'], ['/userfiles/media/previews', 'imagePreview'] ]}); 2 | KellyPageWatchdog.validators.push({url : 'patreon.com', host : 'patreon.com', patterns : [['patreon-media/p/post/', 'imageAny']]}); 3 | KellyPageWatchdog.validators.push({url : 'pinterest.', host : ['pinterest.com', 'pinterest.ru'], patterns : [['/originals/', 'imageOriginal'], ['i.pinimg.com/474x', 'imagePreview'], ['i.pinimg.com/736x', 'imagePreview'], ['i.pinimg.com/236x', 'imagePreview']]}); 4 | 5 | KellyPageWatchdog.validators.push({url : 'fanbox.cc', host : 'fanbox.cc', patterns : [['fanbox/public/images', 'imagePreview']]});https://thumbs.hentai-foundry.com/thumb.php?pid=768245&size=1550 6 | KellyPageWatchdog.validators.push({url : '2ch.hk', host : '2ch.hk', patterns : [[new RegExp('//2ch.hk/[a-zA-Z0-9]+/src/'), 'imageOriginal'], [new RegExp('//2ch.hk/[a-zA-Z0-9]+/thumb/'), 'imagePreview']]}); 7 | KellyPageWatchdog.validators.push({url : 'furaffinity.net', host : 'furaffinity.net', patterns : [[new RegExp('//t.[a-zA-Z0-9]+.net/[0-9]+@[0-9]+\-[0-9]+.[a-zA-Z]+'), 'imagePreview'], ['/profile_banner', 'imageTrash'], [new RegExp('//d.[a-zA-Z0-9]+.net/art'), 'imageByDocument']]}); 8 | // KellyPageWatchdog.validators.push({url : 'exhentai.org', host : 'exhentai.org', patterns : [[new RegExp('//exhentai.org/t/[0-9]+@[0-9]+\-[0-9]+.[a-zA-Z]+'), 'imagePreview'], ['/h/', 'imageByDocument']]}); 9 | KellyPageWatchdog.validators.push({url : 'sankakucomplex.com', host : ['sankakucomplex.com', 'chan.sankakucomplex.com'], patterns : [['data/preview/', 'imagePreview'], ['data/sample/', 'imageByDocument']]}); 10 | 11 | KellyPageWatchdog.validators.push({url : 'boards.4channel.org', host : ['4chan.org', '4channel.org'], patterns : [[new RegExp('//i.4cdn.org/[a-zA-Z0-9]+/[0-9]+s\\.[a-zA-Z]+'), 'imagePreview'], [new RegExp('//i.4cdn.org/[a-zA-Z0-9]+/[0-9]+\\.[a-zA-Z]+'), 'imageOriginal']]}); 12 | 13 | KellyPageWatchdog.filtersHelp.push({host : 'vk.com', link : 'https://www.youtube.com/watch?v=XpXhwndWYyg', loc : 'help_vk'}); 14 | KellyPageWatchdog.filtersHelp.push({host : 'twitter.com', link : 'https://www.youtube.com/watch?v=x1-kqKMnMmA', loc : 'help_twitter'}); 15 | KellyPageWatchdog.filtersHelp.push({host : 'pixiv.net', link : 'https://www.youtube.com/watch?v=1Nivs34BDbI', loc : 'help_pixiv'}); 16 | KellyPageWatchdog.filtersHelp.push({host : 'pinterest.com', link : 'https://www.youtube.com/watch?v=ImKbC_1Oz8c', loc : 'help_pinterest'}); 17 | KellyPageWatchdog.filtersHelp.push({host : 'deviantart.com', link : 'https://www.youtube.com/watch?v=O3JDEhig0P8', loc : 'help_deviantart'}); 18 | KellyPageWatchdog.filtersHelp.push({host : 'instagram.com', link : 'https://www.youtube.com/watch?v=RenHptOCnh8', loc : 'help_instagram'}); 19 | KellyPageWatchdog.filtersHelp.push({host : 'pikabu.ru', link : 'https://www.youtube.com/watch?v=QNm4L8hmoDs', loc : 'help_pikabu'}); 20 | KellyPageWatchdog.filtersHelp.push({host : '9gag.com', link : 'https://youtu.be/bTU7FpUzeao', loc : 'help_9gag'}); 21 | KellyPageWatchdog.filtersHelp.push({host : 'reddit.com', link : 'https://youtu.be/PLkNKM7Trlw', loc : 'help_reddit'}); 22 | 23 | KellyPageWatchdog.bannedUrls.push('counter.yadro.ru', 'bat.bing.com', 'ads.adfox'); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/artstation.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterArtstation = new Object(); 2 | KellyRecorderFilterArtstation.manifest = {host : 'artstation.com', detectionLvl : ['imageAny', 'imageByDocument']}; 3 | KellyRecorderFilterArtstation.parseImagesDocByDriver = function(handler, data) { 4 | 5 | if (handler.url.indexOf('artstation') != -1) { 6 | 7 | var meta = data.thread.response.match(/<\meta name="twitter:image"([\s\S]*?)>/g); // todo - parse graphql data to detect multiple images for one publication 8 | if (meta) { 9 | var image = KellyTools.val('
    ' + meta[0] + '
    ', 'html').querySelector('meta[name="twitter:image"]'); 10 | if (image) image = image.getAttribute('content'); 11 | if (image) handler.imagesPool.push({relatedSrc : [image]}); 12 | } 13 | 14 | data.thread.response = ''; 15 | } 16 | } 17 | 18 | KellyPageWatchdog.validators.push({url : 'artstation', patterns : [['assets/images', 'imageAny']]}); 19 | KellyPageWatchdog.filters.push(KellyRecorderFilterArtstation); 20 | -------------------------------------------------------------------------------- /extension/lib/recorder/filters/bsky.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterBsky = new Object(); 2 | KellyRecorderFilterBsky.manifest = {host : 'bsky.app', detectionLvl : ['imageAny', 'imagePreview', 'imageOriginal']}; 3 | 4 | KellyRecorderFilterBsky.addItemByDriver = function(handler, data) { 5 | if (handler.url.indexOf('bsky.app') != -1 && data.el.tagName == 'IMG' && data.el.src.indexOf('img/feed_') != -1) { 6 | console.log(' KellyRecorderFilterBsky tst'); 7 | handler.addSingleSrc(data.item, data.el.src, 'addSrcFromAttributes-src', data.el, 'imagePreview'); 8 | handler.addSingleSrc(data.item, data.el.src.replace('feed_thumbnail', 'feed_fullsize'), 'addSrcFromAttributes-src', data.el, 'imageOriginal'); 9 | 10 | return data.item.relatedSrc.length > 0 ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 11 | } 12 | } 13 | 14 | KellyPageWatchdog.validators.push({url : 'bsky.app', patterns : [['cdn.bsky.app', 'imageAny']]}); 15 | KellyPageWatchdog.filters.push(KellyRecorderFilterBsky); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/deviantart.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterDA = new Object(); 2 | KellyRecorderFilterDA.manifest = {host : 'deviantart.com', detectionLvl : ['imagePreview', 'imageAny', 'imageByDocument']}; 3 | 4 | KellyRecorderFilterDA.addItemByDriver = function(handler, data) { 5 | 6 | if (handler.url.indexOf('deviantart') == -1) return; 7 | 8 | if (data.el.tagName == 'IMG' && data.el.src.indexOf('images-wixmp') != -1) { 9 | 10 | 11 | handler.addSrcFromAttributes(data.el, data.item); 12 | if (data.item.relatedSrc.length <= 0) return handler.addDriverAction.SKIP; 13 | 14 | var relatedDoc = KellyTools.getParentByTag(data.el, 'A'); 15 | if (!relatedDoc) return handler.addDriverAction.SKIP; 16 | 17 | data.item.relatedDoc = relatedDoc.href; 18 | 19 | data.item.relatedGroups = []; 20 | data.item.relatedGroups[data.item.relatedSrc.length-1] = ['imagePreview']; 21 | 22 | return (data.item.relatedSrc.length > 0 && data.item.relatedDoc) ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 23 | } 24 | } 25 | 26 | KellyRecorderFilterDA.parseImagesDocByDriver = function(handler, data) { 27 | 28 | if (handler.url.indexOf('deviantart') != -1) { 29 | 30 | try { 31 | 32 | var begin = 'window.__INITIAL_STATE__ = JSON.parse("', end = '")'; 33 | 34 | KellyRecorderFilterDA.strDa = data.thread.response.substring(data.thread.response.lastIndexOf(begin) + begin.length); 35 | KellyRecorderFilterDA.strDa = KellyRecorderFilterDA.strDa.substring(0, KellyRecorderFilterDA.strDa.indexOf(end)); 36 | 37 | // todo - DA currently have some JSON stringified blocks inside comments sections and this brock parse sintax 38 | 39 | var da = JSON.parse(JSON.parse('"' + KellyRecorderFilterDA.strDa.replace(/\\'/g, '') + '"')); 40 | 41 | KellyRecorderFilterDA.lastDa = da; 42 | 43 | for (var daName in da['@@entities']['deviation']) { 44 | var deviation = da['@@entities']['deviation'][daName], mediaQuality = false; 45 | 46 | if (data.thread.job.url.indexOf(deviation.url) == -1) continue; // check is data image related to preview image by relatedDoc url 47 | 48 | deviation['media']['types'].forEach(function(type) { // select biggest resolution 49 | if ((!mediaQuality || type.h > mediaQuality.h) && type.h) mediaQuality = type; 50 | }); 51 | 52 | var url = '', baseUrl = deviation['media']['baseUri']; 53 | if (baseUrl[baseUrl.length-1] == '/') baseUrl = baseUrl.substr(0, baseUrl.length-1); // double delimiter simbol / is not allowed 54 | 55 | if (mediaQuality && mediaQuality.c) { // formating url depending on media type 56 | 57 | url = mediaQuality.c.replace('', deviation['media']['prettyName'] ? deviation['media']['prettyName'] : ''); 58 | 59 | if (url[0] != '/') url = '/' + url; 60 | url = baseUrl + url; 61 | 62 | } else if (mediaQuality && mediaQuality.t == 'gif') { 63 | 64 | url = mediaQuality.b; 65 | 66 | } else if (mediaQuality && mediaQuality.t == 'fullview') { 67 | 68 | url = baseUrl; 69 | } 70 | 71 | if (url) handler.imagesPool.push({relatedSrc : [url + '?token=' + deviation['media']['token'][0]]}); 72 | } 73 | 74 | } catch (e) { 75 | handler.docLoader.lastError = 'DeviantArt page JSON parse (KellyRecorderFilterDA.strDa) error in url :
    ' + handler.url + ''; 76 | console.log(e); 77 | } 78 | 79 | data.thread.response = ''; 80 | return true; 81 | } 82 | } 83 | 84 | KellyPageWatchdog.validators.push({url : 'deviantart', patterns : [['images-wixmp', 'imageAny']]}); 85 | KellyPageWatchdog.filters.push(KellyRecorderFilterDA); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/discord.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterDiscord = new Object(); 2 | KellyRecorderFilterDiscord.manifest = {host : 'discord.com', detectionLvl : ['imageOriginal', 'imagePreview']}; 3 | 4 | KellyRecorderFilterDiscord.addItemByDriver = function(handler, data) { 5 | 6 | if (handler.url.indexOf('discord.com') == -1) return; 7 | 8 | if (data.el.tagName == 'DIV' && data.el.className.indexOf('imageWrapper') != -1) { 9 | 10 | var video = data.el.getElementsByTagName('VIDEO'); 11 | if (video.length > 0) { 12 | 13 | handler.addSingleSrc(data.item, video[0].src, 'addSrcFromAttributes-src', video[0], ['imageOriginal']); 14 | handler.addSingleSrc(data.item, video[0].getAttribute('poster'), 'addSrcFromAttributes-src', data.el, ['imagePreview']); 15 | 16 | } else { 17 | 18 | var full = data.el.getElementsByTagName('A'); 19 | var preview = data.el.getElementsByTagName('IMG'); 20 | 21 | if (preview.length > 0) { 22 | handler.addSingleSrc(data.item, preview[0].src, 'addSrcFromAttributes-src', preview[0], ['imagePreview']); 23 | } else return handler.addDriverAction.SKIP; 24 | 25 | if (full.length > 0 && full[0].getAttribute('data-safe-src')) { 26 | 27 | var url = new URL(full[0].getAttribute('data-safe-src')); 28 | url.searchParams.delete('height'); 29 | url.searchParams.delete('width'); 30 | 31 | handler.addSingleSrc(data.item, url.href, 'addSrcFromAttributes-src', full[0], ['imageOriginal']); 32 | 33 | } 34 | 35 | } 36 | 37 | if (data.item.relatedSrc.length <= 0) return handler.addDriverAction.SKIP; 38 | return handler.addDriverAction.ADD; 39 | } 40 | } 41 | 42 | // KellyPageWatchdog.validators.push({url : 'discord.', host : 'discord.com', patterns : [['cdn.discordapp.com/attachments', 'imageOriginal'], ['&height=', 'imagePreview']]}); 43 | 44 | KellyPageWatchdog.filters.push(KellyRecorderFilterDiscord); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/ehentai.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterEHentai = new Object(); 2 | KellyRecorderFilterEHentai.manifest = {host : ['e-hentai.org', 'exhentai.org'], detectionLvl : ['imagePreview', 'imageByDocument']}; 3 | KellyRecorderFilterEHentai.previewTileMap = false; 4 | KellyRecorderFilterEHentai.canvas = document.createElement('canvas'); 5 | KellyRecorderFilterEHentai.previewImagesPool = []; 6 | 7 | KellyRecorderFilterEHentai.getPreviewTileBounds = function(el) { 8 | return { x : Math.abs(parseInt(el.style.backgroundPositionX)), y : parseInt(el.style.backgroundPositionY), width : parseInt(el.style.width), height : parseInt(el.style.height) } 9 | } 10 | 11 | KellyRecorderFilterEHentai.addItemByDriver = function(handler, data) { 12 | 13 | if (handler.url.indexOf('e-hentai.org') == -1 && handler.url.indexOf('exhentai.org') == -1) return; 14 | 15 | if (data.el.tagName != 'DIV') return; 16 | 17 | var previewUrl = handler.getSrcFromStyle(data.el); 18 | if (previewUrl && data.el.style.backgroundPosition) { 19 | 20 | var relatedDoc = KellyTools.getElementByTag(data.el, 'A'), bounds = KellyRecorderFilterEHentai.getPreviewTileBounds(data.el); 21 | if (!relatedDoc) return handler.addDriverAction.SKIP; 22 | 23 | handler.addSingleSrc(data.item, 'data:image-tilemap;' + previewUrl + ',' + bounds.x + ',' + bounds.y + ',' + bounds.width + ',' + bounds.height, 'addSrcFromStyle', data.el, 'imagePreview'); 24 | data.item.relatedDoc = relatedDoc.href; 25 | 26 | console.log(data.item); 27 | 28 | return handler.addDriverAction.ADD; 29 | } 30 | 31 | } 32 | 33 | KellyRecorderFilterEHentai.parseImagesDocByDriver = function(handler, data) { 34 | 35 | if (handler.url.indexOf('e-hentai.org') != -1 && typeof data.thread.response == 'string') { 36 | 37 | var parser = new DOMParser(); 38 | data.thread.loadDoc = parser.parseFromString(data.thread.response, 'text/html'); 39 | 40 | var image = data.thread.loadDoc.querySelector('#i3 img'); 41 | if (image){ 42 | handler.imagesPool.push({relatedSrc : [image.getAttribute('src')], relatedGroups : []}); 43 | return true; 44 | } 45 | 46 | data.thread.response = ''; 47 | return; 48 | } 49 | 50 | } 51 | 52 | KellyPageWatchdog.filters.push(KellyRecorderFilterEHentai); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/flickr.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterFlickr = new Object(); 2 | KellyRecorderFilterFlickr.manifest = {host : 'flickr.com', detectionLvl : ['imagePreview', 'imageOriginal', 'imageByDocument']}; 3 | KellyRecorderFilterFlickr.addItemByDriver = function(handler, data) { 4 | 5 | if (handler.url.indexOf('flickr.com') == -1) return; 6 | 7 | if (data.el.className && data.el.classList.contains('photo-list-photo-view')) { 8 | 9 | handler.addSrcFromStyle(data.el, data.item, 'imagePreview'); 10 | var relatedDoc = data.el.getElementsByTagName('A'); 11 | if (relatedDoc.length > 0) data.item.relatedDoc = relatedDoc[0].href; 12 | 13 | return (data.item.relatedSrc.length > 0 && data.item.relatedDoc) ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 14 | } 15 | } 16 | 17 | KellyRecorderFilterFlickr.parseImagesDocByDriver = function(handler, data) { 18 | 19 | if (handler.url.indexOf('flickr.com') == -1 && data.thread.response) return; 20 | 21 | var parser = new DOMParser(); 22 | var doc = parser.parseFromString(data.thread.response, 'text/html'); 23 | var src = doc.querySelector('[property="og:image"]').getAttribute('content'); 24 | if (src) handler.imagesPool.push({relatedSrc : [src]}); 25 | 26 | return true; 27 | } 28 | 29 | KellyPageWatchdog.filters.push(KellyRecorderFilterFlickr); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/hfoundry.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterHFoundry = new Object(); 2 | KellyRecorderFilterHFoundry.manifest = {host : 'hentai-foundry.com', detectionLvl : ['imagePreview', 'imageByDocument']}; 3 | KellyRecorderFilterHFoundry.parseImagesDocByDriver = function(handler, data) { 4 | 5 | if (handler.url.indexOf('hentai-foundry.com') == -1) return; 6 | if (!data.thread.response) return; 7 | 8 | var parser = new DOMParser(); 9 | var doc = parser.parseFromString(data.thread.response, 'text/html'); 10 | var image = doc.querySelector('#picBox img'); 11 | 12 | if (image) { 13 | 14 | var imageSrc = false; 15 | 16 | if (image.getAttribute('onclick') && image.getAttribute('onclick').indexOf('resize_message') != -1) { 17 | 18 | imageSrc = image.getAttribute('onclick').split("'")[1]; 19 | 20 | if (!imageSrc) imageSrc = image.getAttribute('src'); 21 | else imageSrc = 'https:' + imageSrc; 22 | 23 | } else { 24 | 25 | imageSrc = image.getAttribute('src'); 26 | } 27 | 28 | var item = {relatedSrc : []}; 29 | handler.addSingleSrc(item, imageSrc, 'addSrcFromAttributes-src', image, []); 30 | 31 | console.log(item); 32 | if (item.relatedSrc.length > 0) handler.imagesPool.push(item); 33 | 34 | } else { 35 | handler.docLoader.lastError = 'Original image not found in document :
    ' + handler.url + ''; 36 | } 37 | 38 | data.thread.response = ''; 39 | return true; 40 | } 41 | 42 | KellyPageWatchdog.validators.push({url : 'hentai-foundry.', host : 'hentai-foundry.com', patterns : [['thumb.php', 'imagePreview'], [new RegExp('pictures.hentai-foundry.com/[0-9a-zA-Z]+\/'), 'imageByDocument']]}); 43 | KellyPageWatchdog.filters.push(KellyRecorderFilterHFoundry); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/instagram.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterInstagram = new Object(); 2 | KellyRecorderFilterInstagram.manifest = {host : 'instagram.com', detectionLvl : ['imageAny', 'imageByDocument']}; 3 | KellyRecorderFilterInstagram.addItemByDriver = function(handler, data) { 4 | 5 | if (handler.url.indexOf('instagram') != -1 && data.el.tagName == 'IMG' && data.el.src.indexOf('instagram.com') != -1) { 6 | 7 | var link = KellyTools.getParentByTag(data.el, 'A'); // match pattern https://www.instagram.com/p/CJdsKX4DEDK/ 8 | if (link && link.getAttribute('href').length > 4) data.item.relatedDoc = link.href; 9 | 10 | if (data.item.relatedDoc && KellyTools.getParentByTag(data.el, 'ARTICLE')) { 11 | var cat = 'inst_post'; 12 | } else { 13 | var cat = 'inst_story'; 14 | if (link || !KellyTools.getParentByTag(data.el, 'SECTION')) return handler.addDriverAction.SKIP; 15 | } 16 | 17 | handler.addSingleSrc(data.item, data.el.getAttribute('src'), 'addSrcFromAttributes-src', data.el, cat); 18 | 19 | if ( data.item.relatedSrc.length > 0 ) { 20 | return handler.addDriverAction.ADD; 21 | } else { 22 | return handler.addDriverAction.SKIP; 23 | } 24 | } 25 | } 26 | 27 | KellyRecorderFilterInstagram.getBestQuality = function(instImageItem, imagesPool) { 28 | 29 | var mediaQuality = false; 30 | instImageItem.image_versions2.candidates.forEach(function(srcData) { 31 | if (!mediaQuality || srcData.width > mediaQuality.width) mediaQuality = srcData; 32 | }); 33 | 34 | if (mediaQuality) imagesPool.push({relatedSrc : [mediaQuality.url]}); 35 | } 36 | 37 | KellyRecorderFilterInstagram.onBeforeParseImagesDocByDriver = function(handler, data) { 38 | 39 | if (data.thread.instagramRequest) return; 40 | 41 | if (handler.url.indexOf('instagram') != -1){ 42 | 43 | var appIdRegExp = /\"appId\"\:\"([0-9]+)?\"/; 44 | var mediaIdRegExp = /\"media_id\"\:\"([0-9]+)?\"/; 45 | 46 | var pageData = { 47 | appId : appIdRegExp.exec(data.thread.response), 48 | mediaId : mediaIdRegExp.exec(data.thread.response), 49 | }; 50 | 51 | if (pageData.appId) pageData.appId = pageData.appId[1].trim(); 52 | if (pageData.mediaId) pageData.mediaId = pageData.mediaId[1].trim(); 53 | 54 | if (!pageData.appId || !pageData.mediaId) { 55 | 56 | console.log('[Instagram] : bad media info'); 57 | console.log(pageData); 58 | return; 59 | } else { 60 | 61 | console.log(pageData); 62 | } 63 | 64 | var requestUrl = 'https://i.instagram.com/api/v1/media/' + pageData.mediaId + '/info/'; 65 | 66 | data.thread.instagramRequest = 'mediaRequest'; 67 | 68 | return {requestUrl : requestUrl, cfg : {method : 'GET', xHeaders : {'x-ig-app-id' : pageData.appId}, responseType : 'json'}}; 69 | } 70 | } 71 | 72 | // debug throw KellyDPage.aDProgress.docLoader.parser 73 | 74 | KellyRecorderFilterInstagram.parseImagesDocByDriver = function(handler, data) { 75 | 76 | if (data.thread.instagramRequest == 'mediaRequest' && handler.url.indexOf('instagram') != -1){ 77 | 78 | try { 79 | handler.lastThreadJson = data.thread.response; 80 | if (!handler.lastThreadJson) { 81 | 82 | handler.lastThreadJson = {error : 'empty page data. check regexp'}; 83 | return; 84 | } 85 | 86 | var item = handler.lastThreadJson.items[0]; 87 | if (item.carousel_media) { 88 | item.carousel_media.forEach(function(instImageItem) { 89 | KellyRecorderFilterInstagram.getBestQuality(instImageItem, handler.imagesPool); 90 | }); 91 | } else { 92 | KellyRecorderFilterInstagram.getBestQuality(handler.lastThreadJson.items[0], handler.imagesPool); 93 | } 94 | 95 | } catch (e) { 96 | 97 | handler.lastThreadJson = {error : 'parse fail'}; 98 | console.log(e); 99 | } 100 | 101 | return true; 102 | } 103 | } 104 | 105 | KellyRecorderFilterInstagram.onStartRecord = function(handler, data) { 106 | if (handler.url.indexOf('instagram') == -1) return; 107 | 108 | handler.additionCats = { 109 | inst_story : {name : 'Stories & Misc', selected : 80, color : '#b7dd99'}, 110 | inst_post : {name : 'Publication Preview', selected : 90, color : '#b7dd99'}, 111 | }; 112 | } 113 | 114 | KellyPageWatchdog.validators.push({url : 'instagram', patterns : [['cdninstagram', 'imageAny']]}); // may be use regexp pattern for previews - [any symbol]instagram[any symbol]640x640[any] 115 | KellyPageWatchdog.filters.push(KellyRecorderFilterInstagram); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/joyreactor.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterJoyreactor = new Object(); 2 | KellyRecorderFilterJoyreactor.manifest = {host : ['joyreactor.cc', 'reactor.cc'], detectionLvl : ['imagePreview', 'imageOriginal']}; 3 | KellyRecorderFilterJoyreactor.addItemByDriver = function(handler, data) { 4 | 5 | if (handler.url.indexOf('reactor.cc') != -1) { 6 | 7 | if (data.el.tagName == 'IMG' && data.el.src && (data.el.src.indexOf('pics/post') != -1 || data.el.src.indexOf('pics/comment') != -1)) { 8 | var src = data.el.src.indexOf('full') == -1 ? data.el.src : data.el.src.replace('full/', ''); 9 | if (src.indexOf('static') != -1) { 10 | src = src.replace('static/', ''); 11 | src = src.substr(0, src.lastIndexOf('.') + 1) + 'gif'; 12 | } 13 | 14 | var groups = ['imageOriginal']; 15 | if (src.indexOf('comment/') != -1) groups.push('Comment'); 16 | else if (src.indexOf('post/') != -1) groups.push('Post'); 17 | 18 | handler.addSingleSrc(data.item, src, 'addSrcFromAttributes-src', data.el, 'imagePreview'); 19 | handler.addSingleSrc(data.item, src.replace('comment/', 'comment/full/').replace('post/', 'post/full/'), 'addSrcFromAttributes-src', data.el, groups); 20 | 21 | return data.item.relatedSrc.length > 0 ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 22 | } else if (data.el.tagName == 'A' && data.el.href && data.el.href.indexOf('/full/') != -1) { 23 | 24 | return handler.addDriverAction.SKIP; 25 | } 26 | 27 | } 28 | } 29 | 30 | KellyPageWatchdog.filters.push(KellyRecorderFilterJoyreactor); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/kemonoparty.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterKemono = new Object(); 2 | KellyRecorderFilterKemono.manifest = {host : ['kemono.su', 'kemono.party', 'coomer.party'], detectionLvl : ['imagePreview', 'imageOriginal', 'imageByDocument']}; 3 | 4 | KellyRecorderFilterKemono.parseImagesDocByDriver = function(handler, data) { 5 | 6 | if (handler.url.indexOf('kemono.su') == -1 && handler.url.indexOf('kemono.party') == -1 && handler.url.indexOf('coomer.party') == -1 && data.thread.response) return; 7 | 8 | var parser = new DOMParser(); 9 | var doc = parser.parseFromString(data.thread.response, 'text/html'); 10 | 11 | var images = doc.querySelectorAll('.fileThumb'); 12 | for (var i = 0; i < images.length; i++) { 13 | var href = KellyTools.getLocationFromUrl(images[i].href).pathname; 14 | if (href && href.indexOf('/data/') === 0) handler.imagesPool.push({relatedSrc : ['https://' + handler.hostname + href]}); 15 | } 16 | 17 | data.thread.response = ''; 18 | return true; 19 | } 20 | 21 | KellyRecorderFilterKemono.onStartRecord = function(handler, data) { 22 | if (handler.url.indexOf('kemono.su') == -1 && handler.url.indexOf('kemono.party') == -1) return; 23 | handler.allowDuplicates = true; 24 | } 25 | 26 | KellyPageWatchdog.validators.push({ 27 | url : 'kemono.party', 28 | host : 'kemono.party', 29 | patterns : [['/thumbnail/', 'imagePreview']] 30 | }); 31 | 32 | KellyPageWatchdog.validators.push({ 33 | url : 'kemono.su', 34 | host : 'kemono.su', 35 | patterns : [['/thumbnail/', 'imagePreview']] 36 | }); 37 | 38 | KellyPageWatchdog.validators.push({ 39 | url : 'coomer.party', 40 | host : 'coomer.party', 41 | patterns : [['/thumbnail/', 'imagePreview']] 42 | }); 43 | 44 | KellyPageWatchdog.filters.push(KellyRecorderFilterKemono); 45 | -------------------------------------------------------------------------------- /extension/lib/recorder/filters/pixiv.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterPixiv = new Object(); 2 | KellyRecorderFilterPixiv.manifest = {host : 'pixiv.net', detectionLvl : ['imagePreview', 'imageByDocument']}; 3 | KellyRecorderFilterPixiv.artworkReg = {id : new RegExp('[0-9]+'), url : new RegExp('/artworks/[0-9]+')}; 4 | 5 | KellyRecorderFilterPixiv.validateByDriver = function(handler, data) { 6 | 7 | if (handler.url.indexOf('pixiv.net') == -1 || data.item.relatedSrc.length != 1 || !data.item.relatedDoc) return; 8 | if (data.item.relatedDoc.match(KellyRecorderFilterPixiv.artworkReg.url) === null) return; 9 | 10 | var artworkId = KellyRecorderFilterPixiv.artworkReg.id.exec(data.item.relatedDoc)[0]; 11 | data.item.relatedDoc = 'https://www.pixiv.net/ajax/illust/' + artworkId + '/pages' + '##FETCH_RULES##method=GET&responseType=json'; 12 | data.item.relatedGroups = [['imagePreview']]; 13 | 14 | // https://www.pixiv.net/ajax/illust/112344306/ugoira_meta 15 | // https://github.com/Stuk/jszip/tree/main 16 | // https://github.com/thenickdude/webm-writer-js 17 | 18 | // animations on pixiv has different design and require zip archivator libraries to work with 19 | } 20 | 21 | KellyRecorderFilterPixiv.parseImagesDocByDriver = function(handler, data) { 22 | 23 | if (handler.url.indexOf('pixiv.net') != -1) { 24 | if (typeof data.thread.response == 'object' && !data.thread.response.error && typeof data.thread.response.body == 'object') { 25 | 26 | for (var i = 0; i < data.thread.response.body.length; i++) { 27 | // todo - take width \ height and put this data to relatedBounds pool - implement kellyLoadDocControll to accept this data 28 | var urls = data.thread.response.body[i].urls; 29 | if (urls && (urls.regular || urls.original)) handler.imagesPool.push({relatedSrc : [urls.original ? urls.original : urls.regular]}); 30 | } 31 | 32 | } else console.log('bad response - cant recognize object - check KellyDPage.aDProgress.docLoader.parser.lastThreadReport'); 33 | 34 | data.thread.response = ''; 35 | return true; 36 | 37 | } 38 | } 39 | 40 | KellyPageWatchdog.filters.push(KellyRecorderFilterPixiv); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/reddit.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterReddit = new Object(); 2 | KellyRecorderFilterReddit.manifest = {host : 'reddit.com', detectionLvl : ['imageOriginal', 'imagePreview', 'imageByDocument']}; 3 | 4 | KellyRecorderFilterReddit.addItemByDriver = function(handler, data) { 5 | 6 | if (handler.url.indexOf('reddit.com') == -1) return; 7 | 8 | if (data.el.getAttribute('data-click-id') == 'image') return handler.addDriverAction.SKIP; 9 | 10 | if (data.el.getAttribute('data-testid') && data.el.getAttribute('data-testid') == 'post-container') { 11 | 12 | // bookmarks, upvoted, downvoted etc. 13 | 14 | var preview = false; 15 | if (handler.url.indexOf('/user/') != -1 || handler.url.indexOf('/search/?q=') != -1) { 16 | 17 | preview = data.el.querySelector('[data-click-id="image"]'); 18 | if (preview) { 19 | handler.addSrcFromStyle(preview, data.item, 'reddit_post'); 20 | } 21 | } 22 | 23 | if (!preview) { 24 | preview = data.el.querySelector('[data-click-id="media"] img'); 25 | if (preview) { 26 | handler.addSingleSrc(data.item, preview.src, 'addSrcFromAttributes-src', preview, 'reddit_post'); 27 | } 28 | } 29 | 30 | // console.log(data.item); 31 | // console.log(handler.lastError); 32 | // console.log(preview); 33 | 34 | var link = data.el.querySelector('a[data-click-id="body"]'); 35 | if (link) { 36 | data.item.relatedDoc = link.href; 37 | } else { 38 | data.item.relatedSrc = []; 39 | } 40 | 41 | return data.item.relatedSrc.length > 0 ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 42 | } 43 | } 44 | 45 | KellyRecorderFilterReddit.parseImagesDocByDriver = function(handler, data) { 46 | 47 | if (handler.url.indexOf('reddit.com') == -1) return; 48 | 49 | var pageDataRegExp = /window\.___r[\s]*=[\s]*\{([\s\S]*)\}\}\;\<\/script/g 50 | var pageData = pageDataRegExp.exec(data.thread.response); 51 | 52 | if (pageData) { 53 | 54 | try { 55 | var redditData = JSON.parse('{' + pageData[1] + '}}'); 56 | 57 | for (var postId in redditData.posts.models) { 58 | 59 | var model = redditData.posts.models[postId]; 60 | 61 | if (model.media.mediaMetadata ) { 62 | for (var mediaId in model.media.mediaMetadata) { 63 | 64 | if (model.media.mediaMetadata[mediaId].s) { 65 | 66 | handler.imagesPool.push({ 67 | relatedSrc : [ model.media.mediaMetadata[mediaId].s.u ], 68 | relatedGroups : [['reddit_orig']] 69 | }); 70 | } 71 | 72 | } 73 | 74 | } else if (redditData.posts.models[postId].media.content) { 75 | handler.imagesPool.push({ 76 | relatedSrc : [ redditData.posts.models[postId].media.content ], 77 | relatedGroups : [['reddit_orig']] 78 | }); 79 | } 80 | 81 | break; 82 | } 83 | 84 | } catch (e) { 85 | console.log(e); 86 | } 87 | } 88 | 89 | return true; 90 | } 91 | 92 | KellyRecorderFilterReddit.onStartRecord = function(handler, data) { 93 | if (handler.url.indexOf('reddit.com') == -1) return; 94 | 95 | handler.additionCats = { 96 | reddit_post : {name : 'Post (Preview)', color : '#b7dd99', selected : 90}, 97 | reddit_orig : {name : 'Post media', color : '#b7dd99', selected : 91}, 98 | }; 99 | } 100 | 101 | KellyPageWatchdog.validators.push({ 102 | url : 'reddit.com', 103 | host : 'reddit.com', 104 | patterns : [ 105 | ['preview.redd.it/award_images', 'imageTrash'], 106 | ['preview.redd.it', 'imagePreview'], 107 | ['i.imgur.com', 'imagePreview'], 108 | ['i.redd.it', 'imagePreview'], 109 | ] 110 | }); 111 | 112 | 113 | KellyPageWatchdog.filters.push(KellyRecorderFilterReddit); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/twitter.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterTwitter = new Object(); 2 | KellyRecorderFilterTwitter.manifest = {host : 'twitter.com', detectionLvl : ['imageAny', 'imagePreview', 'imageOriginal']}; 3 | 4 | KellyRecorderFilterTwitter.onInitLocation = function(handler, data) { 5 | if (handler.url.indexOf('twitter') != -1) { 6 | 7 | handler.getConfig(function(options, source) { 8 | 9 | 10 | if (options.tweekTwitter18W) { 11 | 12 | KellyRecorderFilterTwitter.observer = new MutationObserver(function(mutations) { 13 | 14 | for (var i = 0; i < mutations.length; i++) { 15 | 16 | if (mutations[i].addedNodes.length > 0) { 17 | 18 | for (var b = 0; b < mutations[i].addedNodes.length; b++) { 19 | 20 | if (mutations[i].addedNodes[b] != handler.recorder && 21 | mutations[i].addedNodes[b].nodeType == Node.ELEMENT_NODE) { 22 | 23 | KellyRecorderFilterTwitter.delayUpdate18wTweek(); 24 | } 25 | } 26 | 27 | } 28 | } 29 | }); 30 | 31 | KellyRecorderFilterTwitter.observer.observe(document.body, {childList: true, subtree: true}); 32 | KellyRecorderFilterTwitter.delayUpdate18wTweek(); 33 | } 34 | }); 35 | } 36 | } 37 | 38 | KellyRecorderFilterTwitter.update18wTweek = function() { 39 | 40 | var getParentByTag = function(el, tagName) { 41 | var parent = el; 42 | if (!tagName) return false; 43 | 44 | tagName = tagName.toLowerCase(); 45 | 46 | while (parent && parent.tagName.toLowerCase() != tagName) { 47 | parent = parent.parentElement; 48 | } 49 | 50 | return parent; 51 | } 52 | 53 | var findSignMainBlock = function(el) { 54 | var parent = el; 55 | 56 | while (parent) { 57 | parent = parent.parentElement; 58 | if (parent.children.length == 1) { 59 | var testBlock = parent.children[0].querySelector('div[role="button"]') ; 60 | if (testBlock) return parent; 61 | } 62 | } 63 | 64 | return false; 65 | } 66 | 67 | 68 | if (window.location.href.indexOf('/media') != -1) { 69 | 70 | var posts = document.querySelectorAll('li[role="listitem"]'); 71 | 72 | for(var i=0; i < posts.length; i++) { 73 | 74 | var post = posts[i]; 75 | var containers = post.getElementsByTagName('DIV'); 76 | 77 | for (var b = 0; b < containers.length; b++) { 78 | 79 | if (containers[b].children.length == 2 && containers[b].children[0].tagName == 'DIV' && containers[b].children[1].tagName == 'DIV') { 80 | var style = window.getComputedStyle(containers[b].children[0]); 81 | if (style.filter && style.filter.indexOf('blur') != -1 && style.filter.indexOf('blur(0px)') == -1) { 82 | containers[b].children[0].style.filter = 'blur(0px)'; 83 | containers[b].children[1].style.display = 'none'; 84 | break; 85 | } 86 | } 87 | } 88 | } 89 | 90 | } else { 91 | 92 | var posts = document.getElementsByTagName('ARTICLE'); 93 | for(var i=0; i < posts.length; i++) { 94 | 95 | var post = posts[i]; 96 | var containers = post.getElementsByTagName('DIV'); 97 | var warningEl = false; 98 | 99 | for (var b = 0; b < containers.length; b++) { 100 | 101 | if (!warningEl) { 102 | var sign = containers[b].getElementsByTagName('SPAN'); 103 | 104 | if (sign.length > 0 && sign[0].innerHTML.indexOf('sensitive') != -1) { 105 | 106 | warningEl = findSignMainBlock(sign[0]); 107 | warningEl.style.display = 'none'; 108 | } 109 | } 110 | 111 | var style = window.getComputedStyle(containers[b]); 112 | if (style.filter && style.filter.indexOf('blur') != -1) { 113 | containers[b].style.filter = 'blur(0px)'; 114 | //containers[b].style.webkitFilter = ''; 115 | //console.log(containers[b]); 116 | } 117 | } 118 | } 119 | 120 | } 121 | } 122 | 123 | KellyRecorderFilterTwitter.delayUpdate18wTweek = function() { 124 | 125 | if (KellyRecorderFilterTwitter.timer18wTweek) { 126 | clearTimeout(KellyRecorderFilterTwitter.timer18wTweek); 127 | } 128 | 129 | 130 | KellyRecorderFilterTwitter.timer18wTweek = setTimeout(KellyRecorderFilterTwitter.update18wTweek, 300); 131 | } 132 | 133 | KellyRecorderFilterTwitter.addItemByDriver = function(handler, data) { 134 | if (handler.url.indexOf('twitter') != -1 && data.el.tagName == 'IMG' && data.el.src.indexOf('name=') != -1 && data.el.src.indexOf('pbs.twimg.com/media') != -1) { 135 | 136 | handler.addSingleSrc(data.item, data.el.src, 'addSrcFromAttributes-src', data.el, 'imagePreview'); 137 | handler.addSingleSrc(data.item, data.el.src.split('&name=')[0] + '&name=orig', 'addSrcFromAttributes-src', data.el, 'imageOriginal'); 138 | 139 | return data.item.relatedSrc.length > 0 ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 140 | } 141 | } 142 | 143 | KellyPageWatchdog.validators.push({url : 'twitter', patterns : [['twimg.com/media', 'imageAny']]}); 144 | KellyPageWatchdog.filters.push(KellyRecorderFilterTwitter); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/vk.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterVK = new Object(); 2 | KellyRecorderFilterVK.manifest = {host : 'vk.com', detectionLvl : ['imagePreview', 'imageByDocument']}; 3 | 4 | KellyRecorderFilterVK.addHelperGroups = function(data) { 5 | if (data.item.relatedSrc.length <= 0) return; 6 | if (KellyTools.getParentByClass(data.el, 'reply_content')) { 7 | data.item.relatedGroups[0].push('vk_comment'); 8 | } else { 9 | data.item.relatedGroups[0].push('vk_post'); 10 | } 11 | } 12 | 13 | KellyRecorderFilterVK.addItemByDriver = function(handler, data) { 14 | 15 | // Document file separate page \ GIFS on page 16 | 17 | if (handler.url.indexOf('vk.com') != -1 && (data.el.classList.contains('page_doc_photo_href') || data.el.classList.contains('page_post_thumb_unsized'))) { 18 | 19 | data.item.relatedDoc = data.el.href; 20 | if (KellyTools.getParentByClass(data.el, 'reply_content')) { 21 | data.item.relatedDoc += '##FETCH_RULES##mark_comment=1'; 22 | } 23 | 24 | if (data.el.getAttribute('data-thumb')) handler.addSingleSrc(data.item, data.el.getAttribute('data-thumb'), 'addSrcFromAttributes-src', data.el, 'imagePreview'); 25 | else handler.addSrcFromStyle(data.el, data.item, 'imagePreview'); 26 | 27 | KellyRecorderFilterVK.addHelperGroups(data); 28 | 29 | return data.item.relatedSrc.length > 0 ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 30 | 31 | // Album items \ feed 32 | 33 | } else if (handler.url.indexOf('vk.com') != -1 && (data.el.getAttribute('data-id') || data.el.getAttribute('data-photo-id'))) { 34 | 35 | if (data.el.children[0] && data.el.children[0].tagName == 'IMG') { 36 | handler.addSingleSrc(data.item, data.el.children[0].src, 'addSrcFromAttributes-src', data.el.children[0], 'imagePreview'); 37 | } else { 38 | handler.addSrcFromStyle(data.el, data.item, 'imagePreview'); 39 | } 40 | 41 | if (data.item.relatedSrc.length <= 0) return handler.addDriverAction.SKIP; 42 | 43 | // Mobile 44 | 45 | if (data.el.getAttribute('data-src_big')) { 46 | handler.addSingleSrc(data.item, data.el.getAttribute('data-src_big'), 'addSrcFromAttributes-src', data.el, 'imageOriginal'); 47 | return handler.addDriverAction.ADD; 48 | } 49 | 50 | // Desktop - OLD \ NEW design 51 | 52 | var query = '&module=feed', params = [], marks = '', relatedDoc = data.el.tagName == 'A' ? data.el : KellyTools.getElementByTag(data.el, 'A'); 53 | 54 | if (data.el.getAttribute('data-list-id')) { 55 | 56 | params = [data.el.getAttribute('data-photo-id'), data.el.getAttribute('data-list-id')]; 57 | 58 | } else { 59 | 60 | if (!relatedDoc || !relatedDoc.getAttribute('onclick') || relatedDoc.getAttribute('onclick').indexOf('showPhoto') == -1) return handler.addDriverAction.SKIP; 61 | 62 | var paramList = relatedDoc.getAttribute('onclick').split('{')[0], regexpParam = /[\'\"]([-A-Za-z0-9_]+)[\'\"]/g, match = null; 63 | 64 | while((match = regexpParam.exec(paramList)) !== null) { params.push(match[1]); } 65 | 66 | } 67 | 68 | if (params.length > 1) { 69 | if (params[1].indexOf('wall-') != -1) query = '&module=public&list=' + params[1]; 70 | else if (params[1].indexOf('mail') != -1) query = '&module=im&list=' + params[1]; 71 | else if (params[1]) query = '&module=feed&list=' + params[1]; 72 | } else if (params.length <= 0) return handler.addDriverAction.SKIP; 73 | 74 | if (KellyTools.getParentByClass(data.el, 'reply_content')) { 75 | marks += '&mark_comment=1'; 76 | } 77 | 78 | KellyRecorderFilterVK.addHelperGroups(data); 79 | 80 | data.item.relatedDoc = 'https://vk.com/al_photos.php?act=show&al=1&al_ad=0&dmcah=' + query + '&photo=' + params[0]; 81 | data.item.relatedDoc += '##FETCH_RULES##method=POST&responseType=json&contentType=application/x-www-form-urlencoded&xRequestedWith=XMLHttpRequest' + marks; 82 | 83 | return handler.addDriverAction.ADD; 84 | } 85 | } 86 | 87 | KellyRecorderFilterVK.onStartRecord = function(handler, data) { 88 | if (handler.url.indexOf('vk.com') == -1) return; 89 | 90 | handler.additionCats = { 91 | vk_comment : {name : 'Comment', color : '#b7dd99'}, 92 | vk_post : {name : 'Post', color : '#b7dd99'}, 93 | }; 94 | } 95 | 96 | KellyRecorderFilterVK.parseImagesDocByDriver = function(handler, data) { 97 | if (handler.url.indexOf('vk.com') != -1 && typeof data.thread.response == 'object' && handler.url.indexOf('photo=') != -1) { 98 | 99 | var photoId = handler.url.split('photo='); 100 | photoId = photoId.length == 2 ? photoId[1] : false; 101 | 102 | var findSrc = function(response) { 103 | if (!response) return; 104 | 105 | if (typeof response['id'] != 'undefined' && response['id'] == photoId) { 106 | if (typeof response['w_src'] != 'undefined') return response['w_src']; 107 | else if (typeof response['y_src'] != 'undefined') return response['y_src']; 108 | else if (typeof response['z_src'] != 'undefined') return response['z_src']; 109 | else if (typeof response['x_src'] != 'undefined') return response['x_src']; 110 | else return; 111 | } 112 | 113 | for (var name in response) { 114 | if (typeof response[name] == 'object') { 115 | var result = findSrc(response[name]); 116 | if (result) return result; 117 | } 118 | } 119 | } 120 | 121 | var image = findSrc(data.thread.response); 122 | if (image) handler.imagesPool.push({relatedSrc : [image], relatedGroups : data.thread.rules.indexOf('mark_comment=1') != -1 ? [['vk_comment']] : []}); 123 | 124 | data.thread.response = ''; 125 | return true; 126 | 127 | } else if (handler.url.indexOf('vk.com/doc') != -1 && typeof data.thread.response == 'string' && handler.url.indexOf('hash=') != -1) { 128 | 129 | data.thread.loadDoc = KellyTools.val(KellyTools.validateHtmlDoc(data.thread.response), 'html'); 130 | var image = data.thread.loadDoc.querySelector('center img'); 131 | if (image) handler.imagesPool.push({relatedSrc : [image.getAttribute('src')], relatedGroups : data.thread.rules.indexOf('mark_comment=1') != -1 ? [['vk_comment']] : []}); 132 | 133 | data.thread.response = ''; 134 | return true; 135 | } 136 | } 137 | 138 | KellyPageWatchdog.filters.push(KellyRecorderFilterVK); -------------------------------------------------------------------------------- /extension/lib/recorder/filters/yandex.js: -------------------------------------------------------------------------------- 1 | KellyRecorderFilterYandex = new Object(); 2 | KellyRecorderFilterYandex.manifest = {host : 'yandex.ru', detectionLvl : ['imageOriginal', 'imagePreview']}; 3 | KellyRecorderFilterYandex.addItemByDriver = function(handler, data) { 4 | 5 | if (handler.url.indexOf('yandex.ru') != -1) { 6 | 7 | if (data.el.classList.contains('serp-item__thumb') && data.el.tagName == 'IMG' && data.el.src) { 8 | 9 | var orig = KellyTools.getParentByClass(data.el, 'serp-item'); 10 | if (!orig || !orig.getAttribute('data-bem')) return; 11 | 12 | var origImage = false; 13 | try { 14 | var origData = JSON.parse(orig.getAttribute('data-bem')); 15 | origImage = origData['serp-item']['img_href']; 16 | } catch(e) { 17 | return; 18 | } 19 | 20 | if (!origImage) return; 21 | 22 | data.item.referrer = KellyTools.getLocationFromUrl(origImage).host; 23 | 24 | handler.addSingleSrc(data.item, data.el.src, 'addSrcFromAttributes-src', data.el, 'imagePreview'); 25 | handler.addSingleSrc(data.item, origImage, 'addSrcFromAttributes-src', data.el, 'imageOriginal'); 26 | 27 | return data.item.relatedSrc.length > 0 ? handler.addDriverAction.ADD : handler.addDriverAction.SKIP; 28 | } 29 | } 30 | } 31 | 32 | KellyPageWatchdog.filters.push(KellyRecorderFilterYandex); -------------------------------------------------------------------------------- /extension/lib/recorder/kellyEDRecorder.js: -------------------------------------------------------------------------------- 1 | var KellyEDRecorder = new Object; 2 | KellyEDRecorder.cacheEnabled = false; 3 | KellyEDRecorder.recorder = {}; 4 | 5 | // todo - save recorder state cache for manifest v3 6 | 7 | KellyEDRecorder.getDefaultRecorder = function() { 8 | return { 9 | images : [], // array of founded images, could be collected from various tabs - see kellyPageWatchdog (imagesPool = []) for object description 10 | record : false, // is recording enabled 11 | cats : {}, // array of category objects that extends KellyDPage.cats 12 | host : false, // used as profile name only, images[] could be taken from various hosts and contain different [referrer] 13 | url : false, // used as profile name only 14 | srcs : [], // list of all added relatedSrc strings during record process, to prevent dublicates 15 | }; 16 | } 17 | 18 | KellyEDRecorder.loadState = function() { 19 | KellyEDispetcher.api.storage.local.get('kelly_cache_recorder', function(response) { 20 | 21 | var result = 'OK'; 22 | if (KellyEDispetcher.api.runtime.lastError) { 23 | result = KellyEDispetcher.api.runtime.lastError.message; 24 | } 25 | 26 | if (!response || response === null || !response['kelly_cache_recorder'] || typeof response['kelly_cache_recorder'].srcs == 'undefined') { 27 | result = 'Bad storage item - Reset'; 28 | response['kelly_cache_recorder'] = KellyEDRecorder.getDefaultRecorder(); 29 | } 30 | 31 | KellyEDRecorder.recorder = response['kelly_cache_recorder']; 32 | KellyTools.log('[loadState] [' + result + ']', 'KellyEDRecorder'); 33 | }); 34 | } 35 | 36 | // base64 items can be very heavy, calc max size ? 37 | 38 | KellyEDRecorder.saveState = function() { 39 | 40 | KellyEDispetcher.api.storage.local.set({'kelly_cache_recorder' : KellyEDRecorder.recorder}, function() { 41 | 42 | var result = 'OK'; 43 | if (KellyEDispetcher.api.runtime.lastError) { 44 | result = KellyEDispetcher.api.runtime.lastError.message; 45 | } 46 | 47 | KellyTools.log('[saveState] [' + result + ']', 'KellyEDRecorder'); 48 | }); 49 | } 50 | 51 | KellyEDRecorder.init = function() { 52 | 53 | KellyEDRecorder.cacheEnabled = KellyTools.getManifestVersion() > 2 ? true : false; 54 | if (KellyEDRecorder.cacheEnabled) KellyEDRecorder.loadState(); 55 | 56 | KellyEDispetcher.events.push({onMessage : KellyEDRecorder.onMessage}); 57 | } 58 | 59 | KellyEDRecorder.onMessage = function(dispetcher, data) { 60 | 61 | var response = data.response; // default response array {senderId : 'dispetcher', error : '', method : request.method,} 62 | var request = data.request; 63 | var callback = function (cacheUpdate) { 64 | 65 | if (cacheUpdate && KellyEDRecorder.cacheEnabled) KellyEDRecorder.saveState(); 66 | if (data.callback) data.callback(response); 67 | 68 | return true; 69 | } 70 | 71 | if (request.method == 'addRecord') { 72 | 73 | response.imagesNum = 0; 74 | 75 | if (request.clean) { // start record and skip check recording state 76 | 77 | KellyEDRecorder.recorder = KellyEDRecorder.getDefaultRecorder(); 78 | 79 | } else if (!KellyEDRecorder.recorder.record) { // add if recording 80 | 81 | response.error = 'Record is not enabled'; 82 | return callback(); 83 | } 84 | 85 | if (request.host && !KellyEDRecorder.recorder.host) { 86 | 87 | KellyEDRecorder.recorder.host = request.host; 88 | KellyEDRecorder.recorder.url = request.url; 89 | } 90 | 91 | KellyTools.log('[addRecord] : images : ' + request.images.length + ' | cats : ' + (request.cats ? Object.keys(request.cats).length : 'not setted')); 92 | 93 | // addition categories information (color \ name etc.) 94 | 95 | if (request.cats) { 96 | 97 | for (var k in request.cats) { 98 | 99 | if (typeof KellyEDRecorder.recorder.cats[k] == 'undefined') { 100 | 101 | KellyEDRecorder.recorder.cats[k] = request.cats[k]; 102 | } 103 | } 104 | } 105 | 106 | if (request.images) { 107 | 108 | for (var i = 0; i < request.images.length; i++) { 109 | 110 | var validatedSrc = [], validatedGroups = []; 111 | 112 | for (var srcIndex = 0; srcIndex < request.images[i].relatedSrc.length; srcIndex++) { 113 | 114 | if (KellyEDRecorder.recorder.srcs.indexOf(request.images[i].relatedSrc[srcIndex]) != -1) { 115 | 116 | if (!request.allowDuplicates) continue; 117 | // console.log('skip ' + request.images[i].relatedSrc[srcIndex]); 118 | 119 | } else { 120 | 121 | KellyEDRecorder.recorder.srcs.push(request.images[i].relatedSrc[srcIndex]); 122 | } 123 | 124 | validatedSrc.push(request.images[i].relatedSrc[srcIndex]); 125 | if (request.images[i].relatedGroups && request.images[i].relatedSrc[srcIndex]) validatedGroups[srcIndex] = request.images[i].relatedGroups[srcIndex]; 126 | } 127 | 128 | if (validatedSrc.length > 0) { 129 | 130 | request.images[i].relatedSrc = validatedSrc; 131 | 132 | if (validatedGroups.length > 0) request.images[i].relatedGroups = validatedGroups; 133 | else delete request.images[i].relatedGroups; 134 | 135 | KellyEDRecorder.recorder.images.push(request.images[i]); 136 | } else { 137 | // console.log('skip, no new images'); 138 | // console.log(KellyEDRecorder.recorder.images[i]); 139 | } 140 | } 141 | } 142 | 143 | // todo - skip save state for zero 144 | response.imagesNum = KellyEDRecorder.recorder.images.length; 145 | return callback(true); 146 | 147 | } else if (request.method == 'getRecord') { 148 | 149 | if (!KellyEDRecorder.recorder || !KellyEDRecorder.recorder.images) { 150 | KellyEDRecorder.recorder = KellyEDRecorder.getDefaultRecorder(); 151 | } 152 | 153 | response.images = KellyEDRecorder.recorder.images; 154 | response.cats = KellyEDRecorder.recorder.cats; 155 | response.url = KellyEDRecorder.recorder.url; 156 | response.host = KellyEDRecorder.recorder.host; 157 | 158 | return callback(); 159 | 160 | } else if (request.method == 'startRecord') { 161 | 162 | response.isRecorded = true; 163 | 164 | KellyEDRecorder.recorder = KellyEDRecorder.getDefaultRecorder(); 165 | KellyEDRecorder.recorder.record = true; 166 | 167 | return callback(true); 168 | 169 | } else if (request.method == 'stopRecord') { 170 | 171 | if (request.clean) KellyEDRecorder.recorder = KellyEDRecorder.getDefaultRecorder(); 172 | 173 | response.isRecorded = false; 174 | response.imagesNum = KellyEDRecorder.recorder.images.length; 175 | KellyEDRecorder.recorder.record = false; 176 | 177 | return callback(true); 178 | 179 | } else if (request.method == 'isRecorded') { 180 | 181 | response.isRecorded = KellyEDRecorder.recorder.record ? true : false; 182 | response.imagesNum = 0; 183 | 184 | if (response.isRecorded) response.imagesNum = KellyEDRecorder.recorder.images.length; 185 | 186 | return callback(); 187 | } 188 | 189 | return false; 190 | } 191 | 192 | KellyEDRecorder.init(); -------------------------------------------------------------------------------- /extension/manifest.v2.all.json: -------------------------------------------------------------------------------- 1 | { 2 | "content_scripts": [ { 3 | "run_at": "document_start", 4 | "js": [ 5 | "widget/kellyTooltip.js", 6 | "widget/kellyTileGrid.js", 7 | "widget/kellyImageView.js", 8 | "lib/kellyLoc.js", 9 | "lib/kellyStorageManager.js", 10 | "lib/kellyThreadWork.js", 11 | "lib/kellyGrabber.js", 12 | "lib/kellyFastSave.js", 13 | "lib/kellyToolbar.js", 14 | "lib/kellyTools.js", 15 | "lib/kellyOptions.js", 16 | "lib/kellyAdditionsForm.js", 17 | "lib/kellyFavItems.js", 18 | "lib/profiles/joyreactor.js", 19 | "lib/profiles/joyreactor.unlock.js", 20 | "lib/profiles/topjoyreactor.js", 21 | "lib/recorder/kellyPageWatchdog.js", 22 | "lib/recorder/kellyLoadDocControll.js", 23 | "lib/recorder/kellyDPage.js", 24 | "lib/recorder/filters/artstation.js", 25 | "lib/recorder/filters/deviantart.js", 26 | "lib/recorder/filters/ehentai.js", 27 | "lib/recorder/filters/instagram.js", 28 | "lib/recorder/filters/joyreactor.js", 29 | "lib/recorder/filters/kemonoparty.js", 30 | "lib/recorder/filters/pixiv.js", 31 | "lib/recorder/filters/twitter.js", 32 | "lib/recorder/filters/vk.js", 33 | "lib/recorder/filters/flickr.js", 34 | "lib/recorder/filters/pikabu.js", 35 | "lib/recorder/filters/reddit.js", 36 | "lib/recorder/filters/bsky.js", 37 | "lib/recorder/filters/9gag.js", 38 | "lib/recorder/filters/hfoundry.js", 39 | "lib/recorder/filters/yandex.js", 40 | "lib/recorder/filters/discord.js", 41 | "lib/recorder/filters/_validators.js", 42 | "lib/profiles/default.js", 43 | "lib/profiles/recorder.js", 44 | "env/init/recorderFront.js", 45 | "env/init/joyreactorFront.js" 46 | ], 47 | "all_frames": false, 48 | "matches": [ 49 | "http://*/*", 50 | "https://*/*" 51 | ] 52 | } ], 53 | "manifest_version" : 2, 54 | "version": "1.2.9.5", 55 | "name": "KellyC Image Downloader", 56 | "description": "__MSG_ext_description_recorder__", 57 | "icons": { 58 | "32": "env/img/icon32x32.png", 59 | "44": "env/img/icon44x44.png", 60 | "128": "env/img/icon128x128.png" }, 61 | "author" : "nradiowave", 62 | "default_locale" : "en", 63 | "permissions": [ 64 | "downloads", 65 | "storage", 66 | "tabs", 67 | "unlimitedStorage", 68 | "webRequest", 69 | "webRequestBlocking", 70 | "" 71 | ], 72 | "background": { 73 | "scripts": [ 74 | "lib/kellyTools.js", 75 | "lib/kellyDispetcher.js", 76 | "env/init/background.js", 77 | "lib/profiles/joyreactor.unlock.d.js", 78 | "lib/recorder/kellyEDRecorder.js" 79 | ], 80 | "persistent": true 81 | }, 82 | "options_ui": { 83 | "page": "env/html/recorderDownloader.html?tab=modules", 84 | "open_in_tab": true 85 | }, 86 | "browser_action": { 87 | "default_popup": "env/html/recorderPopup.html", 88 | "default_icon": { 89 | "32": "env/img/icon32x32.png" 90 | } 91 | }, 92 | "web_accessible_resources": [ 93 | "env/css/*.css", 94 | "env/html/*.html", 95 | "env/dynamic/*.js" 96 | ] 97 | } 98 | -------------------------------------------------------------------------------- /extension/manifest.v3.all.json: -------------------------------------------------------------------------------- 1 | { 2 | "content_scripts": [ { 3 | "run_at": "document_start", 4 | "js": [ 5 | "widget/kellyTooltip.js", 6 | "widget/kellyTileGrid.js", 7 | "widget/kellyImageView.js", 8 | "lib/kellyLoc.js", 9 | "lib/kellyStorageManager.js", 10 | "lib/kellyThreadWork.js", 11 | "lib/kellyGrabber.js", 12 | "lib/kellyFastSave.js", 13 | "lib/kellyToolbar.js", 14 | "lib/kellyTools.js", 15 | "lib/kellyOptions.js", 16 | "lib/kellyAdditionsForm.js", 17 | "lib/kellyFavItems.js", 18 | "lib/profiles/joyreactor.js", 19 | "lib/profiles/joyreactor.unlock.js", 20 | "lib/profiles/topjoyreactor.js", 21 | "lib/recorder/kellyPageWatchdog.js", 22 | "lib/recorder/kellyLoadDocControll.js", 23 | "lib/recorder/kellyDPage.js", 24 | "lib/recorder/filters/artstation.js", 25 | "lib/recorder/filters/deviantart.js", 26 | "lib/recorder/filters/ehentai.js", 27 | "lib/recorder/filters/instagram.js", 28 | "lib/recorder/filters/joyreactor.js", 29 | "lib/recorder/filters/kemonoparty.js", 30 | "lib/recorder/filters/pixiv.js", 31 | "lib/recorder/filters/twitter.js", 32 | "lib/recorder/filters/vk.js", 33 | "lib/recorder/filters/flickr.js", 34 | "lib/recorder/filters/pikabu.js", 35 | "lib/recorder/filters/reddit.js", 36 | "lib/recorder/filters/bsky.js", 37 | "lib/recorder/filters/9gag.js", 38 | "lib/recorder/filters/hfoundry.js", 39 | "lib/recorder/filters/yandex.js", 40 | "lib/recorder/filters/discord.js", 41 | "lib/recorder/filters/_validators.js", 42 | "lib/profiles/default.js", 43 | "lib/profiles/recorder.js", 44 | "env/init/recorderFront.js", 45 | "env/init/joyreactorFront.js" 46 | ], 47 | "matches": ["*://*/*"] 48 | } ], 49 | "host_permissions": ["*://*/*"], 50 | "manifest_version" : 3, 51 | "version": "1.2.9.5", 52 | "name": "KellyC Image Downloader", 53 | "description": "__MSG_ext_description_recorder__", 54 | "icons": { 55 | "32": "env/img/icon32x32.png", 56 | "44": "env/img/icon44x44.png", 57 | "128": "env/img/icon128x128.png" }, 58 | "author" : "nradiowave", 59 | "default_locale" : "en", 60 | "permissions": [ 61 | "downloads", 62 | "storage", 63 | "webRequest", 64 | "declarativeNetRequest", 65 | "tabs", 66 | "unlimitedStorage" 67 | ], 68 | "background": { 69 | "service_worker": "background.js" 70 | }, 71 | "options_ui": { 72 | "page": "env/html/recorderDownloader.html?tab=modules", 73 | "open_in_tab": true 74 | }, 75 | "action": { 76 | "default_popup": "env/html/recorderPopup.html", 77 | "default_icon": { 78 | "32": "env/img/icon32x32.png" 79 | } 80 | }, 81 | "web_accessible_resources": [{ 82 | "resources": ["env/css/*.css", "env/html/*.html", "env/dynamic/*.js"], 83 | "matches": ["*://*/*"], 84 | "extension_ids": [] 85 | }] 86 | } --------------------------------------------------------------------------------