├── assets ├── fonts │ └── Roboto-Regular.ttf └── satus │ ├── satus.css │ └── satus.js ├── _locales ├── am │ └── messages.json ├── ar │ └── messages.json ├── bg │ └── messages.json ├── bn │ └── messages.json ├── ca │ └── messages.json ├── cs │ └── messages.json ├── da │ └── messages.json ├── de │ └── messages.json ├── el │ └── messages.json ├── en │ └── messages.json ├── es │ └── messages.json ├── et │ └── messages.json ├── fa │ └── messages.json ├── fi │ └── messages.json ├── fil │ └── messages.json ├── fr │ └── messages.json ├── gu │ └── messages.json ├── he │ └── messages.json ├── hi │ └── messages.json ├── hin │ └── messages.json ├── hr │ └── messages.json ├── hu │ └── messages.json ├── id │ └── messages.json ├── it │ └── messages.json ├── ja │ └── messages.json ├── kn │ └── messages.json ├── ko │ └── messages.json ├── lt │ └── messages.json ├── lv │ └── messages.json ├── ml │ └── messages.json ├── mr │ └── messages.json ├── ms │ └── messages.json ├── nb_NO │ └── messages.json ├── nl │ └── messages.json ├── no │ └── messages.json ├── pl │ └── messages.json ├── pt_BR │ └── messages.json ├── pt_PT │ └── messages.json ├── ro │ └── messages.json ├── sk │ └── messages.json ├── sl │ └── messages.json ├── sr │ └── messages.json ├── sv │ └── messages.json ├── sw │ └── messages.json ├── ta │ └── messages.json ├── te │ └── messages.json ├── th │ └── messages.json ├── tr │ └── messages.json ├── uk │ └── messages.json ├── vi │ └── messages.json ├── zh_CN │ └── messages.json ├── zh_TW │ └── messages.json └── ru │ └── messages.json ├── background.js ├── content-script.js ├── newtab ├── index.html ├── styles.css └── script.js ├── manifest.json ├── readme.md └── locale.py /assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-charity/History-Manager/HEAD/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /_locales/am/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ar/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/bg/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/bn/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ca/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/cs/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/da/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/el/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/es/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/et/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/fa/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/fi/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/fil/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/gu/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/he/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/hi/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/hin/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/hr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/hu/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/id/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/it/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/kn/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ko/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/lt/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/lv/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ml/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/mr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ms/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/nb_NO/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/nl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/no/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/pl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/pt_BR/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/pt_PT/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ro/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/sk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/sl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/sr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/sv/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/sw/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ta/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/te/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/th/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/tr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/uk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/vi/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/zh_CN/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/zh_TW/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Search {ENGINE} or type a URL" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "This time, search with:" 7 | } 8 | } -------------------------------------------------------------------------------- /_locales/ru/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "searchEngineOrTypeAUrl": { 3 | "message": "Найдите в {ENGINE} или введите адрес" 4 | }, 5 | "thisTimeSearchWith": { 6 | "message": "В этот раз искать в:" 7 | } 8 | } -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | >>> BACKGROUND 3 | ---------------------------------------------------------------- 4 | # 5 | --------------------------------------------------------------*/ -------------------------------------------------------------------------------- /content-script.js: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | >>> CONTENT SCRIPT 3 | ---------------------------------------------------------------- 4 | # 5 | --------------------------------------------------------------*/ -------------------------------------------------------------------------------- /newtab/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | New Tab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "History Manager", 4 | "version": "1.0", 5 | "version_name": "May 2020", 6 | "default_locale": "en", 7 | "offline_enabled": true, 8 | "background": { 9 | "service_worker": "background.js" 10 | }, 11 | "content_scripts": [ 12 | { 13 | "all_frames": true, 14 | "js": [ 15 | "content-script.js" 16 | ], 17 | "matches": [ 18 | "" 19 | ], 20 | "run_at": "document_start" 21 | } 22 | ], 23 | "chrome_url_overrides": { 24 | "newtab": "newtab/index.html" 25 | }, 26 | "permissions": [ 27 | "bookmarks", 28 | "clipboardRead", 29 | "history", 30 | "storage", 31 | "unlimitedStorage", 32 | "tabs" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Superb history manager to treasure (archive & apply) your whole history whenever you need it!
Smart summary and ranking (faster than searching things again). To let your computer storage work hand in hand with your vital, progessive brain memory, instead of starting from zero over and over again. Chromium deletes history after 90 days, but life is longer than that. ( Senile storage' is a paradox. Storage is to reduce forgetting, not promote it. Just like we also avoid Alzheimers & Snapchat ) 5 |
6 | b

7 | 8 | ### [**chrome webstore PREVIEW version!**](https://chrome.google.com/webstore/detail/history-manager/odognhgojidbcgconbcipmgffjcmfaoj) (commit from [july26](https://github.com/code-charity/History-Manager-with-indexedDB/tree/453f6696892e1182c9667467e5a50927a72d71ba) ) 9 | 10 | #### Please select* "SEARCH IN TABLES", to see the feature to filter & make use of your history in multiple ways at once: 11 | 12 | a" 13 | 14 | *(should soon be a multi-search, requiring no selection) 15 | 16 | 17 | --- 18 | 19 | Please consider joining us. We fulfill wishes since 10+ years & unfortunately lost our main developer in 2022 :( 20 | 21 | 1.) Should we use the most powerful JS lib for search and various smart, predictive queries in future? (but not reinvent the wheel) 22 | - Compare History Manager with this older version from feburary using neither DB (just JS) https://github.com/code-charity/History-Manager-with-no-DB/releases/tag/v1.0-alpha.5 (it looks polished & has search in tables enabled by default. Yet missing some features) 23 | - (BTW SqlLite is also back for chrome by now https://www.sqlite.org/2024/sqlite-wasm-3450200.zip, unlike when we started) 24 | 25 | 2.) While july26 (above) is the most functional we got, the current master.zip already has GUI updates/enhancements. 26 | Please help us pick up/carry on our GUI lib too: https://github.com/code-for-charity/SATUS I dont know why the "latest commits in this repo are the older ones" 27 | 28 | 29 | 30 | Wiki » 31 | -------------------------------------------------------------------------------- /newtab/styles.css: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | >>> STYLES 3 | ---------------------------------------------------------------- 4 | # Fonts 5 | # Body 6 | # Themes 7 | # Skeleton 8 | # Header 9 | # Main 10 | # Sidebar 11 | # Layers 12 | --------------------------------------------------------------*/ 13 | 14 | /*-------------------------------------------------------------- 15 | # FONTS 16 | --------------------------------------------------------------*/ 17 | 18 | @font-face { 19 | font-family: Roboto; 20 | 21 | src: url(../assets/fonts/Roboto-Regular.ttf); 22 | } 23 | 24 | 25 | /*-------------------------------------------------------------- 26 | # BODY 27 | --------------------------------------------------------------*/ 28 | 29 | body { 30 | font-family: Roboto, sans-serif; 31 | font-size: 16px; 32 | 33 | overflow: hidden; 34 | 35 | height: 100vh; 36 | margin: 0; 37 | } 38 | 39 | 40 | /*-------------------------------------------------------------- 41 | # THEMES 42 | --------------------------------------------------------------*/ 43 | 44 | .satus-base { 45 | --satus-base-background: #fff; 46 | --satus-main-background: #fff; 47 | } 48 | 49 | 50 | /*-------------------------------------------------------------- 51 | # SKELETON 52 | --------------------------------------------------------------*/ 53 | 54 | /*-------------------------------------------------------------- 55 | # HEADER 56 | --------------------------------------------------------------*/ 57 | 58 | .satus-header { 59 | box-shadow: none; 60 | } 61 | 62 | .satus-header>* { 63 | flex: 1; 64 | } 65 | 66 | .satus-header .satus-text-field { 67 | position: relative; 68 | } 69 | 70 | .satus-header .satus-text-field[focus][results] { 71 | border-bottom-color: transparent; 72 | border-bottom-right-radius: 0; 73 | border-bottom-left-radius: 0; 74 | } 75 | 76 | .satus-header .satus-text-field>svg { 77 | box-sizing: unset; 78 | max-width: 22px; 79 | max-height: 22px; 80 | margin: 0 0 0 8px; 81 | 82 | background-repeat: no-repeat; 83 | background-position: center; 84 | background-size: contain; 85 | 86 | justify-content: center; 87 | align-items: center; 88 | } 89 | 90 | .temporary-engine { 91 | height: 28px; 92 | margin: 0 0 0 6px; 93 | padding: 0 8px; 94 | 95 | border-radius: 4px; 96 | background: var(--satus-header-background); 97 | } 98 | 99 | .temporary-engine::after { 100 | line-height: 0; 101 | 102 | position: relative; 103 | top: 1px; 104 | 105 | margin-left: 8px; 106 | 107 | content: '✕'; 108 | transition: opacity .3s cubic-bezier(.25, .8, .5, 1); 109 | 110 | opacity: .4; 111 | } 112 | 113 | .temporary-engine:hover::after { 114 | opacity: .85; 115 | } 116 | 117 | .satus-search__dropdown-menu { 118 | position: absolute; 119 | top: 100%; 120 | left: -1px; 121 | 122 | display: none; 123 | flex-direction: column; 124 | 125 | width: calc(100% + 2px); 126 | padding: 1px 8px 0; 127 | 128 | border: 1px solid var(--satus-text-field-border); 129 | border-top: 0; 130 | border-bottom-right-radius: 4px; 131 | border-bottom-left-radius: 4px; 132 | background: var(--satus-text-field-background); 133 | } 134 | 135 | .satus-header .satus-text-field[focus][results] .satus-search__dropdown-menu { 136 | display: flex; 137 | } 138 | 139 | .satus-search__results-list { 140 | display: flex; 141 | 142 | padding: 8px 0; 143 | 144 | border-top: 1px solid var(--satus-text-field-border); 145 | 146 | align-items: center; 147 | } 148 | 149 | .satus-search-results__item { 150 | width: 100%; 151 | padding: 6px 4px; 152 | 153 | border-radius: 4px; 154 | } 155 | 156 | .satus-search-results__item { 157 | transition: background-color .3s cubic-bezier(.25, .8, .5, 1); 158 | } 159 | 160 | .satus-search-results__item:hover { 161 | background-color: rgba(var(--satus-light), .08); 162 | } 163 | 164 | .satus-search-results__item:focus { 165 | background-color: rgba(var(--satus-light), .24); 166 | } 167 | 168 | .satus-search-results__item-icon { 169 | width: 18px; 170 | height: 18px; 171 | margin-right: 8px; 172 | 173 | background-repeat: no-repeat; 174 | background-position: center; 175 | background-size: contain; 176 | 177 | flex: 0 0 18px; 178 | } 179 | 180 | .satus-search-results__item-query { 181 | overflow: hidden; 182 | 183 | white-space: nowrap; 184 | text-overflow: ellipsis; 185 | } 186 | 187 | .satus-search-results__item-engine { 188 | padding-left: .3em; 189 | 190 | white-space: nowrap; 191 | 192 | opacity: .64; 193 | } 194 | 195 | .satus-search__engines { 196 | display: flex; 197 | 198 | padding: 8px 0; 199 | 200 | border-top: 1px solid var(--satus-text-field-border); 201 | 202 | align-items: center; 203 | } 204 | 205 | .satus-search__engines>button { 206 | width: 28px; 207 | height: 28px; 208 | margin: 0 0 0 8px; 209 | 210 | background-repeat: no-repeat; 211 | background-position: center; 212 | background-size: 18px; 213 | } 214 | 215 | 216 | /*-------------------------------------------------------------- 217 | # MAIN 218 | --------------------------------------------------------------*/ 219 | 220 | /*-------------------------------------------------------------- 221 | # SIDEBAR 222 | --------------------------------------------------------------*/ 223 | 224 | .satus-sidebar { 225 | box-shadow: none; 226 | } 227 | 228 | 229 | /*-------------------------------------------------------------- 230 | # LAYERS 231 | --------------------------------------------------------------*/ 232 | 233 | .satus-layers { 234 | border: 1px solid var(--satus-header-shadow); 235 | border-right: none; 236 | border-bottom: none; 237 | } -------------------------------------------------------------------------------- /locale.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # ------------------------------------------------------------------------------ 5 | # >>> TABLE OF CONTENTS: 6 | # ------------------------------------------------------------------------------ 7 | # 1.0 Import modules 8 | # 2.0 Lower camel case 9 | # 3.0 Get list of files 10 | # 4.0 Add item 11 | # 5.0 Remove item 12 | # 6.0 Change key 13 | # 7.0 Decode 14 | # 8.0 Upgrade 15 | # 9.0 Initialization 16 | # ------------------------------------------------------------------------------ 17 | 18 | # ------------------------------------------------------------------------------ 19 | # 1.0 IMPORT MODULES 20 | # ------------------------------------------------------------------------------ 21 | 22 | import io 23 | import json 24 | import os 25 | import pathlib 26 | import re 27 | import sys 28 | 29 | 30 | # ------------------------------------------------------------------------------ 31 | # 2.0 LOWER CAMEL CASE 32 | # ------------------------------------------------------------------------------ 33 | 34 | def lowerCamelCase(string): 35 | string = re.sub(r"(-|_)+", ' ', string).title() 36 | string = re.sub(r"[^a-zA-Z0-9]", '', string) 37 | 38 | return string[0].lower() + string[1:] 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # 3.0 GET LIST OF FILES 43 | # ------------------------------------------------------------------------------ 44 | 45 | def getListOfFiles(path): 46 | allFiles = list() 47 | 48 | for entry in os.listdir(path): 49 | fullPath = os.path.join(path, entry) 50 | 51 | if not os.path.isdir(fullPath): 52 | allFiles.append(fullPath) 53 | 54 | for entry in os.listdir(path): 55 | fullPath = os.path.join(path, entry) 56 | 57 | if os.path.isdir(fullPath): 58 | allFiles = allFiles + getListOfFiles(fullPath) 59 | 60 | return allFiles 61 | 62 | 63 | # ------------------------------------------------------------------------------ 64 | # 4.0 ADD ITEM 65 | # ------------------------------------------------------------------------------ 66 | 67 | def addItem(allFiles): 68 | message = input('Enter your message: ') 69 | camelized_message = lowerCamelCase(message) 70 | 71 | for keyFile in allFiles: 72 | with open(keyFile, 'r+') as json_file: 73 | data = json.load(json_file) 74 | 75 | if (camelized_message in data) == False: 76 | data[camelized_message] = {'message': message} 77 | 78 | json_file.seek(0) 79 | json.dump(data, json_file, ensure_ascii=False, indent=4, sort_keys=True) 80 | json_file.truncate() 81 | 82 | 83 | # ------------------------------------------------------------------------------ 84 | # 5.0 REMOVE ITEM 85 | # ------------------------------------------------------------------------------ 86 | 87 | def removeItem(allFiles): 88 | key = input('Enter your key (lowerCamelCase): ') 89 | 90 | for keyFile in allFiles: 91 | with open(keyFile, 'r+') as json_file: 92 | data = json.load(json_file) 93 | 94 | if data[key]: 95 | del data[key] 96 | 97 | json_file.seek(0) 98 | json.dump(data, json_file, ensure_ascii=False, indent=4, 99 | sort_keys=True) 100 | json_file.truncate() 101 | 102 | 103 | # ------------------------------------------------------------------------------ 104 | # 6.0 CHANGE KEY 105 | # ------------------------------------------------------------------------------ 106 | 107 | def changeKey(allFiles): 108 | old_key = input('Enter key: ') 109 | new_key = input('Enter new key: ') 110 | 111 | for keyFile in allFiles: 112 | with open(keyFile, 'r+') as file: 113 | data = json.load(file) 114 | 115 | if old_key in data: 116 | data[new_key] = data[old_key] 117 | 118 | del data[old_key] 119 | 120 | file.seek(0) 121 | json.dump(data, file, ensure_ascii=False, indent=4, sort_keys=True) 122 | file.truncate() 123 | 124 | 125 | # ------------------------------------------------------------------------------ 126 | # 7.0 DECODE 127 | # ------------------------------------------------------------------------------ 128 | 129 | def decodeCharacters(allFiles): 130 | for keyFile in allFiles: 131 | with open(keyFile, 'r+') as json_file: 132 | data = json.load(json_file) 133 | 134 | json_file.seek(0) 135 | json.dump(data, json_file, ensure_ascii=False, indent=4, 136 | sort_keys=True) 137 | json_file.truncate() 138 | 139 | 140 | # ------------------------------------------------------------------------------ 141 | # 8.0 UPGRADE 142 | # ------------------------------------------------------------------------------ 143 | 144 | def upgrade(): 145 | locales = [ 146 | 'am', 147 | 'ar', 148 | 'bg', 149 | 'bn', 150 | 'ca', 151 | 'cs', 152 | 'da', 153 | 'de', 154 | 'el', 155 | 'en', 156 | 'es', 157 | 'et', 158 | 'fa', 159 | 'fi', 160 | 'fil', 161 | 'fr', 162 | 'gu', 163 | 'he', 164 | 'hi', 165 | 'hin', 166 | 'hr', 167 | 'hu', 168 | 'id', 169 | 'it', 170 | 'ja', 171 | 'kn', 172 | 'ko', 173 | 'lt', 174 | 'lv', 175 | 'ml', 176 | 'mr', 177 | 'ms', 178 | 'nb_NO', 179 | 'nl', 180 | 'no', 181 | 'pl', 182 | 'pt_BR', 183 | 'pt_PT', 184 | 'ro', 185 | 'ru', 186 | 'sk', 187 | 'sl', 188 | 'sr', 189 | 'sv', 190 | 'sw', 191 | 'ta', 192 | 'te', 193 | 'th', 194 | 'tr', 195 | 'uk', 196 | 'vi', 197 | 'zh_CN', 198 | 'zh_TW' 199 | ] 200 | 201 | if os.path.exists('_locales/en/messages.json'): 202 | file = open('_locales/en/messages.json', 'r+') 203 | 204 | default_locale = json.load(file) 205 | 206 | file.close() 207 | else: 208 | default_locale = {} 209 | 210 | for locale in locales: 211 | path = '_locales/' + locale 212 | 213 | if not os.path.exists(path): 214 | pathlib.Path(path).mkdir(parents=True, exist_ok=True) 215 | 216 | file = io.open(path + '/messages.json', mode='w', encoding='utf-8') 217 | 218 | json.dump(default_locale, file, ensure_ascii=False, indent=4, sort_keys=True) 219 | 220 | file.close() 221 | else: 222 | with open(path + '/messages.json', 'r+') as file: 223 | data = json.load(file) 224 | 225 | file.seek(0) 226 | 227 | for key in default_locale: 228 | if (key in data) == False: 229 | data[key] = default_locale[key] 230 | 231 | json.dump(data, file, ensure_ascii=False, indent=4, sort_keys=True) 232 | 233 | file.truncate() 234 | 235 | file.close() 236 | 237 | 238 | # ------------------------------------------------------------------------------ 239 | # 9.0 INITIALIZATION 240 | # ------------------------------------------------------------------------------ 241 | 242 | if not os.path.exists('_locales/'): 243 | pathlib.Path('_locales/').mkdir(parents=True, exist_ok=True) 244 | 245 | allFiles = getListOfFiles('_locales/') 246 | 247 | for arg in sys.argv: 248 | if arg == '-add': 249 | addItem(allFiles) 250 | elif arg == '-remove': 251 | removeItem(allFiles) 252 | elif arg == '-decode': 253 | decodeCharacters(allFiles) 254 | elif arg == '-change-key': 255 | changeKey(allFiles) 256 | elif arg == '-upgrade': 257 | upgrade() -------------------------------------------------------------------------------- /newtab/script.js: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | >>> SCRIPT 3 | ---------------------------------------------------------------- 4 | # Skeleton 5 | # Initialization 6 | --------------------------------------------------------------*/ 7 | 8 | /*-------------------------------------------------------------- 9 | # SKELETON 10 | --------------------------------------------------------------*/ 11 | 12 | var skeleton = { 13 | component: 'base', 14 | on: { 15 | click: function (event) { 16 | var founded = false; 17 | 18 | for (var i = 0, l = event.path.length; i < l; i++) { 19 | var element = event.path[i]; 20 | 21 | if (typeof element.className === 'string' && element.className.indexOf('satus-search') !== -1) { 22 | founded = true; 23 | } 24 | } 25 | 26 | if (founded === false) { 27 | skeleton.header.search.rendered.removeAttribute('focus'); 28 | } 29 | } 30 | }, 31 | 32 | header: { 33 | component: 'header', 34 | 35 | section_left: { 36 | component: 'section', 37 | variant: 'align-start' 38 | }, 39 | search: { 40 | component: 'text-field', 41 | class: 'satus-search', 42 | storage: false, 43 | placeholder: function () { 44 | var placeholder = satus.locale.get('searchEngineOrTypeAUrl'), 45 | search_engine = satus.storage.get('searchEngine'); 46 | 47 | if (this.skeleton.engines[search_engine]) { 48 | search_engine = this.skeleton.engines[search_engine].name; 49 | } else { 50 | search_engine = this.skeleton.engines['google'].name; 51 | } 52 | 53 | return placeholder.replace('{ENGINE}', search_engine); 54 | }, 55 | on: { 56 | focus: function () { 57 | this.setAttribute('focus', ''); 58 | }, 59 | input: function () { 60 | this.skeleton.dropDownMenu.results.rendered.update(); 61 | 62 | if (this.value.length > 0) { 63 | this.setAttribute('focus', ''); 64 | this.setAttribute('results', ''); 65 | } else { 66 | this.removeAttribute('results'); 67 | } 68 | }, 69 | keydown: function (event) { 70 | if (event.keyCode === 13) { 71 | var list = this.skeleton.dropDownMenu.results.rendered; 72 | 73 | if (list.firstChild) { 74 | list.firstChild.click(); 75 | } 76 | } 77 | } 78 | }, 79 | engines: { 80 | google: { 81 | name: 'Google', 82 | url: 'https://www.google.com/search?q=', 83 | favicon: 'https://www.google.com/favicon.ico' 84 | }, 85 | youtube: { 86 | name: 'YouTube', 87 | url: 'https://youtube.com/results?search_query=', 88 | favicon: 'https://youtube.com/favicon.ico' 89 | }, 90 | duckduckgo: { 91 | name: 'DuckDuckGo', 92 | url: 'https://duckduckgo.com/?q=', 93 | favicon: 'https://duckduckgo.com/favicon.ico' 94 | }, 95 | bing: { 96 | name: 'Bing', 97 | url: 'https://www.bing.com/search?q=', 98 | favicon: 'https://www.bing.com/favicon.ico' 99 | }, 100 | yahoo: { 101 | name: 'Yahoo', 102 | url: 'https://search.yahoo.com/', 103 | favicon: 'https://search.yahoo.com/favicon.ico' 104 | }, 105 | ecosia: { 106 | name: 'Ecosia', 107 | url: 'https://www.ecosia.org/search?q=', 108 | favicon: 'https://cdn-static.ecosia.org/assets/images/ico/favicon.ico' 109 | } 110 | }, 111 | before: { 112 | svg: { 113 | component: 'svg', 114 | attr: { 115 | 'viewBox': '0 0 24 24', 116 | 'fill': 'currentColor' 117 | }, 118 | 119 | path: { 120 | component: 'path', 121 | attr: { 122 | 'd': 'm20.5 19-5.7-5.7a6.5 6.5 0 1 0-1.5 1.5l5.7 5.7 1.5-1.5zM5 9.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0z' 123 | } 124 | } 125 | } 126 | }, 127 | 128 | dropDownMenu: { 129 | component: 'div', 130 | class: 'satus-search__dropdown-menu', 131 | 132 | results: { 133 | component: 'div', 134 | class: 'satus-search__results-list', 135 | properties: { 136 | update: function () { 137 | var list = this.skeleton.rendered, 138 | search_element = this.skeleton.parentSkeleton.parentSkeleton.rendered, 139 | search_engines = this.skeleton.parentSkeleton.parentSkeleton.engines, 140 | default_search_engine = satus.storage.get('defaultSearchEngine') || 'google'; 141 | 142 | if (satus.isElement(search_element.temporaryEngine)) { 143 | default_search_engine = search_element.temporaryEngine.name; 144 | } 145 | 146 | satus.empty(list); 147 | 148 | satus.render({ 149 | component: 'button', 150 | class: 'satus-search-results__item', 151 | attr: { 152 | url: search_engines[default_search_engine].url 153 | }, 154 | on: { 155 | click: function () { 156 | window.open(this.getAttribute('url') + encodeURIComponent(this.parentNode.skeleton.parentSkeleton.parentSkeleton.rendered.value), '_self'); 157 | } 158 | }, 159 | before: { 160 | icon: { 161 | component: 'span', 162 | class: 'satus-search-results__item-icon', 163 | style: { 164 | backgroundImage: 'url(' + search_engines[default_search_engine].favicon + ')' 165 | } 166 | } 167 | }, 168 | 169 | query: { 170 | component: 'span', 171 | class: 'satus-search-results__item-query', 172 | text: this.skeleton.parentSkeleton.parentSkeleton.rendered.value 173 | }, 174 | engine: { 175 | component: 'span', 176 | class: 'satus-search-results__item-engine', 177 | text: '- ' + search_engines[default_search_engine].name + ' ' + 'Search' 178 | } 179 | }, list); 180 | } 181 | } 182 | }, 183 | engines: { 184 | component: 'div', 185 | class: 'satus-search__engines', 186 | on: { 187 | render: function () { 188 | var search_engines = this.skeleton.parentSkeleton.parentSkeleton.engines; 189 | 190 | satus.render({ 191 | component: 'span', 192 | text: 'thisTimeSearchWith' 193 | }, this); 194 | 195 | for (var key in search_engines) { 196 | var search_engine = search_engines[key]; 197 | 198 | satus.render({ 199 | component: 'button', 200 | variant: 'icon', 201 | attr: { 202 | name: key 203 | }, 204 | style: { 205 | backgroundImage: 'url(' + search_engine.favicon + ')' 206 | }, 207 | on: { 208 | click: function () { 209 | var name = this.getAttribute('name'), 210 | search_element = this.parentNode.skeleton.parentSkeleton.parentSkeleton.rendered, 211 | search_engine = search_element.skeleton.engines[name]; 212 | 213 | if (search_element.temporaryEngine) { 214 | search_element.temporaryEngine.remove(); 215 | } 216 | 217 | search_element.temporaryEngine = satus.render({ 218 | component: 'button', 219 | class: 'temporary-engine', 220 | text: search_engine.name, 221 | properties: { 222 | name: name, 223 | data: search_engine 224 | }, 225 | on: { 226 | click: function () { 227 | var search_element = skeleton.header.search.rendered; 228 | 229 | this.remove(); 230 | 231 | delete search_element.temporaryEngine; 232 | 233 | search_element.skeleton.dropDownMenu.results.rendered.update(); 234 | } 235 | } 236 | }); 237 | 238 | search_element.firstChild.after(search_element.temporaryEngine); 239 | 240 | search_element.skeleton.dropDownMenu.results.rendered.update(); 241 | } 242 | } 243 | }, this); 244 | } 245 | } 246 | } 247 | } 248 | } 249 | }, 250 | section_right: { 251 | component: 'section', 252 | variant: 'align-end' 253 | } 254 | }, 255 | main: { 256 | component: 'main', 257 | 258 | sidebar: { 259 | component: 'sidebar' 260 | }, 261 | layers: { 262 | component: 'layers' 263 | } 264 | } 265 | }; 266 | 267 | 268 | /*-------------------------------------------------------------- 269 | # INITIALIZATION 270 | --------------------------------------------------------------*/ 271 | 272 | satus.storage.import(function (items) { 273 | var language = items.language || window.navigator.language; 274 | 275 | satus.locale.import(language, function () { 276 | satus.render(skeleton); 277 | }, '_locales/'); 278 | }); -------------------------------------------------------------------------------- /assets/satus/satus.css: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | # ANIMATIONS 3 | --------------------------------------------------------------*/ 4 | 5 | @keyframes fadeIn { 6 | from { 7 | opacity: 0; 8 | } 9 | to { 10 | opacity: 1; 11 | } 12 | } 13 | 14 | @keyframes fadeOut { 15 | from { 16 | opacity: 1; 17 | } 18 | to { 19 | opacity: 0; 20 | } 21 | } 22 | 23 | @keyframes zoomIn { 24 | from { 25 | transform: scale(.8); 26 | opacity: 0; 27 | } 28 | to { 29 | transform: scale(1); 30 | opacity: 1; 31 | } 32 | } 33 | 34 | @keyframes zoomOut { 35 | from { 36 | transform: scale(1); 37 | opacity: 1; 38 | } 39 | to { 40 | transform: scale(.8); 41 | opacity: 0; 42 | } 43 | } 44 | /*-------------------------------------------------------------- 45 | >>> THEMES 46 | --------------------------------------------------------------*/ 47 | 48 | .satus-base { 49 | --satus-light: 0, 20, 82; 50 | --satus-primary: #ff4158; 51 | 52 | --satus-base-background: #f3f4f6; 53 | --satus-base-text: #565e76; 54 | 55 | --satus-header-background: #fff; 56 | --satus-header-text: #565e76; 57 | --satus-header-shadow: #dcdee5; 58 | 59 | --satus-layers-background: #f3f4f6; 60 | --satus-layers-text: #565e76; 61 | 62 | --satus-section-background: #fff; 63 | --satus-section-border: #dcdee5; 64 | 65 | --satus-sidebar-background: #fff; 66 | --satus-sidebar-text: #565e76; 67 | --satus-sidebar-shadow: #dcdee5; 68 | 69 | --satus-modal-background: 255, 255, 255; 70 | --satus-modal-text: 103, 118, 142; 71 | --satus-modal-shadow: #7d86a1; 72 | 73 | --satus-text-field-background: #edf0f2; 74 | --satus-text-field-border: #dcdee5; 75 | --satus-text-field-color: #7d8ba1; 76 | --satus-text-field-selection: rgb(149, 166, 178, .35); 77 | --satus-text-field-cursor: #fa0; 78 | 79 | --satus-switch-track: #e1e4ea; 80 | --satus-switch-thumb: #fff; 81 | 82 | --satus-main-background: #f0f2f4; 83 | } 84 | /*-------------------------------------------------------------- 85 | >>> NORMALIZE 86 | --------------------------------------------------------------*/ 87 | 88 | :where([class^=satus]) { 89 | box-sizing: border-box; 90 | } 91 | 92 | :where([class^=satus])[hidden]:not([hidden^='hidden']) { 93 | display: none; 94 | } 95 | 96 | :where([class^=satus])[transparent] { 97 | opacity: 0; 98 | } 99 | 100 | 101 | /*-------------------------------------------------------------- 102 | # SCROLLBAR 103 | --------------------------------------------------------------*/ 104 | 105 | .satus-base ::-webkit-scrollbar { 106 | width: 4px; 107 | } 108 | 109 | .satus-base ::-webkit-scrollbar:hover { 110 | width: 8px; 111 | } 112 | 113 | .satus-base ::-webkit-scrollbar-thumb { 114 | background: rgba(var(--satus-light), .2); 115 | } 116 | 117 | .satus-base ::-webkit-scrollbar-thumb:hover { 118 | background: rgba(var(--satus-light), .3); 119 | } 120 | /*-------------------------------------------------------------- 121 | >>> MODAL 122 | ---------------------------------------------------------------- 123 | # Container 124 | # Scrim 125 | # Surface 126 | # Variants 127 | # Vertical menu 128 | --------------------------------------------------------------*/ 129 | 130 | 131 | /*-------------------------------------------------------------- 132 | # CONTAINER 133 | --------------------------------------------------------------*/ 134 | 135 | .satus-modal { 136 | position: fixed; 137 | z-index: 9; 138 | top: 0; 139 | left: 0; 140 | display: flex; 141 | width: 100%; 142 | height: 100vh; 143 | justify-content: center; 144 | align-items: center; 145 | } 146 | 147 | 148 | /*-------------------------------------------------------------- 149 | # SCRIM 150 | --------------------------------------------------------------*/ 151 | 152 | .satus-modal__scrim { 153 | position: absolute; 154 | top: 0; 155 | left: 0; 156 | width: 100%; 157 | height: 100%; 158 | animation: fadeIn 150ms linear forwards; 159 | opacity: 0; 160 | background: rgba(0, 0, 0, .16); 161 | backdrop-filter: blur(8px); 162 | } 163 | 164 | .satus-modal--closing .satus-modal__scrim { 165 | animation: fadeOut 70ms linear forwards; 166 | } 167 | 168 | 169 | /*-------------------------------------------------------------- 170 | # SURFACE 171 | --------------------------------------------------------------*/ 172 | 173 | .satus-modal__surface { 174 | display: flex; 175 | overflow-y: auto; 176 | flex-direction: column; 177 | box-sizing: border-box; 178 | width: 95%; 179 | min-width: 240px; 180 | max-width: 560px; 181 | max-height: 80%; 182 | margin: 8px; 183 | padding: 12px 16px; 184 | transform: scale(.8); 185 | animation: zoomIn 150ms linear forwards; 186 | animation-delay: 20ms; 187 | opacity: 0; 188 | color: rgb(var(--satus-modal-text)); 189 | border-radius: 6px; 190 | background-color: rgb(var(--satus-modal-background)); 191 | box-shadow: 0 1px 4px var(--satus-modal-shadow); 192 | } 193 | 194 | .satus-modal--closing .satus-modal__surface { 195 | animation: zoomOut 70ms linear forwards; 196 | } 197 | 198 | 199 | /*-------------------------------------------------------------- 200 | # VARIANTS 201 | --------------------------------------------------------------*/ 202 | 203 | /*-------------------------------------------------------------- 204 | # VERTICAL MENU 205 | --------------------------------------------------------------*/ 206 | 207 | .satus-modal--vertical-menu .satus-modal__surface { 208 | position: absolute; 209 | top: 8px; 210 | right: 8px; 211 | left: auto; 212 | min-width: 200px; 213 | max-width: 200px; 214 | margin: 0; 215 | padding: 8px 0; 216 | transform-origin: right top; 217 | } 218 | 219 | .satus-modal--vertical-menu .satus-modal__surface>.satus-button, 220 | .satus-modal--vertical-menu .satus-modal__surface>.satus-switch, 221 | .satus-modal--vertical-menu .satus-modal__surface>.satus-select { 222 | height: 36px; 223 | padding: 0 16px; 224 | } 225 | 226 | .satus-modal--vertical-menu .satus-modal__surface>.satus-tabs { 227 | padding: 0 12px; 228 | } 229 | 230 | .satus-modal--vertical-menu .satus-modal__surface>.satus-span { 231 | font-size: 13px; 232 | font-weight: 500; 233 | margin: 6px 0; 234 | padding: 0 12px; 235 | } 236 | 237 | .satus-modal--vertical-menu .satus-button svg { 238 | width: 20px; 239 | height: 18px; 240 | margin: 0 14px 0 0; 241 | opacity: .75; 242 | flex: 0 0 20px; 243 | } 244 | 245 | .satus-modal--vertical-menu .satus-button .satus-span { 246 | overflow: hidden; 247 | white-space: nowrap; 248 | text-overflow: ellipsis; 249 | } 250 | /*-------------------------------------------------------------- 251 | >>> GRID 252 | --------------------------------------------------------------*/ 253 | 254 | .satus-grid { 255 | display: flex; 256 | align-items: stretch; 257 | height: 100%; 258 | padding: 8px; 259 | } 260 | /*-------------------------------------------------------------- 261 | >>> TEXT FIELD 262 | ---------------------------------------------------------------- 263 | # Parts 264 | # Container 265 | # Input 266 | # 267 | # Syntax highlighting 268 | # Regular expression 269 | --------------------------------------------------------------*/ 270 | 271 | .satus-text-field { 272 | display: flex; 273 | 274 | min-width: 240px; 275 | height: 36px; 276 | 277 | color: var(--satus-text-field-color, inherit); 278 | border: 1px solid var(--satus-text-field-border); 279 | border-radius: 4px; 280 | background: var(--satus-text-field-background); 281 | 282 | align-items: center; 283 | justify-content: space-between; 284 | } 285 | 286 | .satus-text-field__container { 287 | position: relative; 288 | 289 | overflow: hidden; 290 | 291 | height: 100%; 292 | 293 | flex: 1; 294 | } 295 | 296 | .satus-text-field__input { 297 | font: inherit; 298 | 299 | position: absolute; 300 | z-index: 9; 301 | top: 0; 302 | left: 0; 303 | 304 | width: 100%; 305 | min-width: 0; 306 | max-width: none; 307 | height: 100%; 308 | min-height: 0; 309 | max-height: none; 310 | margin: 0; 311 | padding: 0 12px; 312 | 313 | opacity: 0; 314 | color: inherit; 315 | border: none; 316 | border-radius: 4px; 317 | outline: none; 318 | 319 | appearance: none; 320 | } 321 | 322 | .satus-text-field__pre { 323 | font: inherit; 324 | 325 | position: absolute; 326 | top: 0; 327 | left: 0; 328 | 329 | display: flex; 330 | 331 | height: 100%; 332 | margin: 0 12px; 333 | 334 | align-items: center; 335 | } 336 | 337 | .satus-text-field__hidden-value { 338 | font: inherit; 339 | 340 | position: absolute; 341 | 342 | pointer-events: none; 343 | 344 | opacity: 0; 345 | } 346 | 347 | .satus-text-field__selection { 348 | position: absolute; 349 | top: 0; 350 | left: 0; 351 | 352 | display: none; 353 | 354 | width: 0; 355 | height: 22px; 356 | margin: 6px 12px; 357 | 358 | border: 1px solid var(--satus-text-field-selection); 359 | border-radius: 3px; 360 | background: var(--satus-text-field-selection); 361 | } 362 | 363 | .satus-text-field__cursor { 364 | position: absolute; 365 | top: 0; 366 | left: 0; 367 | 368 | display: none; 369 | 370 | width: 2px; 371 | height: 24px; 372 | margin: 5px 11px; 373 | 374 | animation: blink 1s step-end 8; 375 | 376 | background: var(--satus-text-field-cursor); 377 | } 378 | 379 | .satus-text-field__input:focus+*+*+*+.satus-text-field__cursor, 380 | .satus-text-field__input:focus+*+*+.satus-text-field__selection:not([disabled]) { 381 | display: block; 382 | } 383 | 384 | @keyframes blink { 385 | 386 | from, 387 | to { 388 | opacity: 1; 389 | } 390 | 391 | 50% { 392 | opacity: 0; 393 | } 394 | } 395 | 396 | 397 | /*-------------------------------------------------------------- 398 | # SYNTAX HIGHLIGHTING 399 | --------------------------------------------------------------*/ 400 | 401 | /*-------------------------------------------------------------- 402 | # REGULAR EXPRESSION 403 | --------------------------------------------------------------*/ 404 | 405 | .satus-text-field__pre>.group { 406 | color: #47ff47; 407 | background-color: rgb(71, 255, 71, .16); 408 | } 409 | 410 | .satus-text-field__pre>.character-class { 411 | color: #ffc247; 412 | background-color: rgb(255, 170, 0, .16); 413 | } 414 | 415 | .satus-text-field__pre>.quantifier { 416 | color: #47c2ff; 417 | background-color: rgb(71, 194, 255, .16); 418 | } 419 | 420 | .satus-text-field__pre>.anchor { 421 | color: #47c2ff; 422 | background-color: rgb(71, 194, 255, .16); 423 | } 424 | 425 | .satus-text-field__pre>.metasequence { 426 | color: #47ff47; 427 | background-color: rgb(71, 255, 71, .16); 428 | } 429 | 430 | .satus-text-field__pre>.text { 431 | color: #c4c4d4; 432 | background-color: rgb(196, 196, 212, .16); 433 | } 434 | /*-------------------------------------------------------------- 435 | >>> SELECT 436 | --------------------------------------------------------------*/ 437 | 438 | .satus-select { 439 | position: relative; 440 | display: flex; 441 | cursor: pointer; 442 | align-items: center; 443 | justify-content: space-between; 444 | } 445 | 446 | .satus-select:hover { 447 | background-color: var(--satus-hover); 448 | } 449 | 450 | .satus-select>span:nth-child(2) { 451 | display: flex; 452 | align-items: center; 453 | } 454 | 455 | .satus-select>span:nth-child(2)>svg { 456 | width: 20px; 457 | height: 18px; 458 | margin: 0 14px 0 0; 459 | opacity: .75; 460 | } 461 | 462 | .satus-select>span:nth-child(3) { 463 | margin-left: 16px; 464 | text-align: right; 465 | opacity: .75; 466 | } 467 | 468 | .satus-select select { 469 | font: inherit; 470 | position: absolute; 471 | top: 0; 472 | left: 0; 473 | width: 100%; 474 | height: 100%; 475 | margin: 0; 476 | padding: inherit; 477 | opacity: 0; 478 | border: none; 479 | outline: none; 480 | appearance: none; 481 | z-index: 1; 482 | cursor: inherit; 483 | color: inherit; 484 | } 485 | 486 | .satus-select option { 487 | color: var(--satus-select-text); 488 | background: var(--satus-select-background); 489 | } 490 | /*-------------------------------------------------------------- 491 | >>> SECTION 492 | ---------------------------------------------------------------- 493 | # Card 494 | --------------------------------------------------------------*/ 495 | 496 | .satus-section { 497 | display: flex; 498 | box-sizing: border-box; 499 | flex-wrap: wrap; 500 | } 501 | 502 | 503 | /*-------------------------------------------------------------- 504 | # CARD 505 | --------------------------------------------------------------*/ 506 | 507 | .satus-section--card { 508 | flex-direction: column; 509 | box-sizing: border-box; 510 | width: 100%; 511 | max-width: 900px; 512 | margin: 8px auto; 513 | padding: 8px 0; 514 | border: 1px solid var(--satus-section-border); 515 | border-radius: 8px; 516 | background: var(--satus-section-background); 517 | justify-content: stretch; 518 | } 519 | 520 | .satus-section--label { 521 | display: block; 522 | width: 100%; 523 | max-width: 900px; 524 | margin: 8px auto; 525 | } 526 | 527 | .satus-section--card>.satus-button, 528 | .satus-section--card>.satus-color-picker, 529 | .satus-section--card>.satus-radio, 530 | .satus-section--card>.satus-select, 531 | .satus-section--card>.satus-shortcut, 532 | .satus-section--card>.satus-slider, 533 | .satus-section--card>.satus-switch, 534 | .satus-section--card>.satus-span { 535 | display: flex; 536 | box-sizing: border-box; 537 | width: 100%; 538 | min-height: 48px; 539 | padding: 8px 16px; 540 | text-align: left; 541 | justify-content: space-between; 542 | align-items: center; 543 | } 544 | 545 | .satus-section--card>.satus-button:hover, 546 | .satus-section--card>.satus-color-picker:hover, 547 | .satus-section--card>.satus-radio:hover, 548 | .satus-section--card>.satus-select:hover, 549 | .satus-section--card>.satus-shortcut:hover, 550 | .satus-section--card>.satus-slider:hover, 551 | .satus-section--card>.satus-switch:hover { 552 | background-color: rgba(var(--satus-light), .06); 553 | } 554 | 555 | .satus-section--card>.satus-button { 556 | justify-content: flex-start; 557 | } 558 | 559 | .satus-section--card>.satus-button>svg { 560 | width: 20px; 561 | margin: 2px 16px 0 0; 562 | color: var(--satus-primary); 563 | } 564 | 565 | .satus-section--card>.satus-span { 566 | display: flex; 567 | align-items: center; 568 | } 569 | 570 | 571 | /*-------------------------------------------------------------- 572 | # ALIGN 573 | --------------------------------------------------------------*/ 574 | 575 | .satus-section--align-start { 576 | display: flex; 577 | align-items: center; 578 | justify-content: flex-start; 579 | } 580 | 581 | .satus-section--align-start>*:not(:last-child) { 582 | margin-right: 8px; 583 | } 584 | 585 | .satus-section--align-end { 586 | display: flex; 587 | align-items: center; 588 | justify-content: flex-end; 589 | } 590 | 591 | .satus-section--align-end>*:not(:first-child) { 592 | margin-left: 8px; 593 | } 594 | /*-------------------------------------------------------------- 595 | >>> BASE 596 | --------------------------------------------------------------*/ 597 | 598 | .satus-base { 599 | display: flex; 600 | flex-direction: column; 601 | width: 100%; 602 | height: 100%; 603 | color: var(--satus-base-text); 604 | background: var(--satus-base-background); 605 | } 606 | /*-------------------------------------------------------------- 607 | >>> MAIN 608 | --------------------------------------------------------------*/ 609 | 610 | .satus-main { 611 | display: flex; 612 | background: var(--satus-main-background); 613 | flex: 1 614 | } 615 | /*-------------------------------------------------------------- 616 | >>> SIDEBAR 617 | --------------------------------------------------------------*/ 618 | 619 | .satus-sidebar { 620 | z-index: 1; 621 | display: flex; 622 | flex-direction: column; 623 | width: 56px; 624 | padding: 12px 0; 625 | color: var(--satus-sidebar-text); 626 | background: var(--satus-sidebar-background); 627 | box-shadow: 1px 0 0 var(--satus-sidebar-shadow) 628 | } 629 | /*-------------------------------------------------------------- 630 | >>> LAYERS 631 | --------------------------------------------------------------*/ 632 | 633 | .satus-layers { 634 | position: relative; 635 | overflow: hidden; 636 | flex: 1; 637 | } 638 | 639 | .satus-layers__layer { 640 | position: absolute; 641 | top: 0; 642 | left: 0; 643 | overflow: auto; 644 | width: 100%; 645 | height: 100%; 646 | padding: 0 12px; 647 | color: var(--satus-layers-text); 648 | background: var(--satus-layers-background); 649 | } 650 | /*-------------------------------------------------------------- 651 | >>> LIST: 652 | --------------------------------------------------------------*/ 653 | 654 | .satus-list { 655 | list-style: none; 656 | margin: 0; 657 | } 658 | 659 | .satus-list__item { 660 | display: flex; 661 | align-items: center; 662 | justify-content: space-between; 663 | min-height: 48px; 664 | padding: 0 16px; 665 | } 666 | 667 | .satus-list__item>*:not(:first-child) { 668 | margin-left: 8px; 669 | } 670 | 671 | .satus-list__item>*:last-child { 672 | text-align: right; 673 | } 674 | /*-------------------------------------------------------------- 675 | >>> COLOR PICKER: 676 | ---------------------------------------------------------------- 677 | # Button 678 | # Modal 679 | --------------------------------------------------------------*/ 680 | 681 | 682 | /*-------------------------------------------------------------- 683 | # BUTTON 684 | --------------------------------------------------------------*/ 685 | 686 | .satus-color-picker { 687 | font-size: inherit; 688 | position: relative; 689 | display: flex; 690 | box-sizing: border-box; 691 | margin: 0; 692 | cursor: pointer; 693 | color: inherit; 694 | border: none; 695 | outline: none; 696 | background-color: var(--satus-theme-button); 697 | justify-content: space-between; 698 | -webkit-tap-highlight-color: transparent; 699 | align-items: center; 700 | -webkit-appearance: none; 701 | } 702 | 703 | .satus-color-picker__value { 704 | width: 22px; 705 | height: 22px; 706 | border: 2px solid rgba(0, 0, 0, .16); 707 | border-radius: 50%; 708 | } 709 | 710 | 711 | /*-------------------------------------------------------------- 712 | # MODAL 713 | --------------------------------------------------------------*/ 714 | 715 | .satus-modal--color-picker { 716 | position: relative; 717 | } 718 | 719 | .satus-modal--color-picker .satus-modal__surface { 720 | padding: 8px; 721 | } 722 | 723 | .satus-color-picker__palette { 724 | position: relative; 725 | overflow: hidden; 726 | width: 100%; 727 | height: 256px; 728 | background-color: #f00; 729 | border-radius: 5px; 730 | } 731 | 732 | .satus-color-picker__palette:before { 733 | position: absolute; 734 | top: 0; 735 | left: 0; 736 | width: 100%; 737 | height: 100%; 738 | content: ''; 739 | background-image: linear-gradient(0deg, black, transparent), linear-gradient(90deg, white, transparent); 740 | } 741 | 742 | .satus-color-picker__cursor { 743 | position: absolute; 744 | width: 5px; 745 | height: 5px; 746 | transform: translate(-50%, -50%); 747 | pointer-events: none; 748 | border: 1px solid #fff; 749 | border-radius: 50%; 750 | box-shadow: 0 0 0 1px #000; 751 | } 752 | 753 | .satus-modal--color-picker .satus-modal__surface .satus-section--color { 754 | margin: 8px 16px 0; 755 | align-items: center; 756 | } 757 | 758 | .satus-color-picker__color { 759 | width: 32px; 760 | height: 32px; 761 | margin: 0 16px 0 0; 762 | border: 2px solid rgba(0, 0, 0, .16); 763 | border-radius: 50%; 764 | background: #f00; 765 | } 766 | 767 | .satus-slider.satus-color-picker__hue { 768 | padding: 0; 769 | flex: 1; 770 | } 771 | 772 | .satus-color-picker__hue .satus-slider__track { 773 | height: 16px; 774 | border-radius: 4px; 775 | background-image: linear-gradient(90deg, #f00, #ff2a00, #f50, #ff7f00, #fa0, #ffd400, #ff0, #d4ff00, #af0, #80ff00, #5f0, #2bff00, #0f0, #00ff2b, #0f5, #00ff80, #0fa, #00ffd5, #0ff, #00d4ff, #0af, #007fff, #05f, #002bff, #00f, #2a00ff, #50f, #7f00ff, #a0f, #d400ff, #f0f, #ff00d4, #f0a, #ff0080, #f05, #ff002b, #f00); 776 | } 777 | 778 | .satus-color-picker__hue .satus-slider__handle { 779 | width: 16px; 780 | height: 16px; 781 | background: #fff; 782 | box-shadow: 0 0 4px rgb(0, 0, 0, .64); 783 | } 784 | 785 | .satus-color-picker__hue::before, 786 | .satus-color-picker__hue .satus-slider__track-fill, 787 | .satus-color-picker__hue .satus-slider__handle:focus::after { 788 | display: none; 789 | } 790 | /*-------------------------------------------------------------- 791 | >>> BUTTON 792 | ---------------------------------------------------------------- 793 | # Base 794 | # Basic 795 | # Icon 796 | --------------------------------------------------------------*/ 797 | 798 | 799 | /*-------------------------------------------------------------- 800 | # BASE 801 | --------------------------------------------------------------*/ 802 | 803 | .satus-button { 804 | font: inherit; 805 | 806 | position: relative; 807 | 808 | display: inline-flex; 809 | overflow: hidden; 810 | 811 | height: 36px; 812 | padding: 8px; 813 | 814 | color: inherit; 815 | border: none; 816 | outline: none; 817 | background: transparent; 818 | 819 | appearance: none; 820 | align-items: center; 821 | } 822 | 823 | .satus-button:hover { 824 | cursor: pointer; 825 | } 826 | 827 | .satus-button svg { 828 | width: 100%; 829 | max-width: 24px; 830 | height: 100%; 831 | max-height: 24px; 832 | } 833 | 834 | 835 | /*-------------------------------------------------------------- 836 | # BASIC 837 | --------------------------------------------------------------*/ 838 | 839 | /*-------------------------------------------------------------- 840 | # ICON 841 | --------------------------------------------------------------*/ 842 | 843 | .satus-button--icon { 844 | width: 40px; 845 | height: 40px; 846 | 847 | transition: background-color .3s cubic-bezier(.25, .8, .5, 1); 848 | 849 | border-radius: 50%; 850 | } 851 | 852 | .satus-button--icon:hover { 853 | background-color: rgba(var(--satus-light), .08); 854 | } 855 | 856 | .satus-button--icon:focus { 857 | background-color: rgba(var(--satus-light), .24); 858 | } 859 | 860 | .satus-button--icon svg { 861 | width: 24px; 862 | height: 24px; 863 | } 864 | /*-------------------------------------------------------------- 865 | >>> HEADER 866 | --------------------------------------------------------------*/ 867 | 868 | .satus-header { 869 | z-index: 1; 870 | display: flex; 871 | height: 56px; 872 | padding: 0 12px; 873 | color: var(--satus-header-text); 874 | background: var(--satus-header-background); 875 | box-shadow: 0 1px 0 var(--satus-header-shadow); 876 | justify-content: space-between; 877 | align-items: center; 878 | } 879 | 880 | .satus-span--title { 881 | font-size: 16px; 882 | } 883 | /*-------------------------------------------------------------- 884 | >>> RADIO 885 | --------------------------------------------------------------*/ 886 | /*-------------------------------------------------------------- 887 | >>> SLIDER 888 | --------------------------------------------------------------*/ 889 | 890 | .satus-slider__track { 891 | height: 32px; 892 | } 893 | 894 | .satus-slider__track__range { 895 | margin: 0; 896 | padding: 0; 897 | width: 100%; 898 | height: 100%; 899 | } 900 | 901 | /*.satus-slider { 902 | display: block; 903 | background: #fff; 904 | } 905 | 906 | .satus-slider__label { 907 | display: flex; 908 | margin: 12px 0 0; 909 | justify-content: space-between; 910 | align-items: center; 911 | } 912 | 913 | .satus-slider .satus-input[type=number] { 914 | font: inherit; 915 | width: 64px; 916 | margin: 0; 917 | padding: 0; 918 | text-align: right; 919 | color: inherit; 920 | background: transparent; 921 | } 922 | 923 | .satus-slider__track { 924 | position: relative; 925 | display: block; 926 | min-width: 128px; 927 | height: 40px; 928 | margin: 0 2px; 929 | } 930 | 931 | .satus-slider__track::before { 932 | position: absolute; 933 | top: 50%; 934 | left: 0; 935 | display: block; 936 | width: 100%; 937 | height: 2px; 938 | content: ''; 939 | transform: translateY(-50%); 940 | opacity: .24; 941 | background: var(--satus-primary); 942 | } 943 | 944 | .satus-slider__slice { 945 | position: absolute; 946 | top: 50%; 947 | left: 0; 948 | height: 2px; 949 | transform: translateY(-50%); 950 | background: var(--satus-primary); 951 | } 952 | 953 | .satus-slider__slice::before { 954 | position: absolute; 955 | top: 50%; 956 | right: 0; 957 | display: block; 958 | width: 22px; 959 | height: 22px; 960 | content: ''; 961 | transition: width 200ms, height 200ms, opacity 200ms; 962 | transform: translate(50%, -50%); 963 | opacity: 0; 964 | border-radius: 50%; 965 | background: var(--satus-primary); 966 | } 967 | 968 | .satus-slider__track:focus .satus-slider__slice::before { 969 | width: 32px; 970 | height: 32px; 971 | opacity: .16; 972 | } 973 | 974 | .satus-slider__slice::after { 975 | position: absolute; 976 | top: 50%; 977 | right: 0; 978 | display: block; 979 | width: 10px; 980 | height: 10px; 981 | content: ''; 982 | transition: width 200ms, height 200ms; 983 | transform: translate(50%, -50%); 984 | border-radius: 50%; 985 | background: var(--satus-primary); 986 | } 987 | 988 | .satus-slider__track:focus .satus-slider__slice::after { 989 | width: 12px; 990 | height: 12px; 991 | }*/ 992 | /*-------------------------------------------------------------- 993 | >>> SHORTCUT: 994 | ---------------------------------------------------------------- 995 | # 996 | --------------------------------------------------------------*/ 997 | 998 | .satus-shortcut__value { 999 | font-size: 11px; 1000 | 1001 | display: flex; 1002 | 1003 | margin-left: 16px; 1004 | 1005 | text-transform: uppercase; 1006 | 1007 | align-items: center; 1008 | flex: 1; 1009 | justify-content: flex-end; 1010 | } 1011 | 1012 | .satus-shortcut__actions { 1013 | display: flex; 1014 | 1015 | justify-content: flex-end; 1016 | } 1017 | 1018 | .satus-shortcut__actions .satus-button { 1019 | height: 32px; 1020 | margin: 8px 4px 0; 1021 | 1022 | border-radius: 8px; 1023 | background: rgba(0, 0, 0, .15); 1024 | } 1025 | 1026 | .satus-shortcut__actions .satus-button:hover { 1027 | background: rgba(0, 0, 0, .25); 1028 | } 1029 | 1030 | .satus-shortcut__primary { 1031 | display: flex; 1032 | 1033 | box-sizing: border-box; 1034 | width: 100%; 1035 | height: 68px; 1036 | padding: 16px; 1037 | 1038 | background: rgba(0, 0, 0, .16); 1039 | 1040 | align-items: center; 1041 | } 1042 | 1043 | .satus-shortcut__key { 1044 | display: flex; 1045 | 1046 | box-sizing: border-box; 1047 | min-width: 32px; 1048 | height: 32px; 1049 | padding: 4px 8px; 1050 | 1051 | border-radius: 4px; 1052 | background: #fff; 1053 | box-shadow: 0 1px 3px rgba(0, 0, 0, .15), inset 0 -3px 0 rgba(0, 0, 0, .1); 1054 | 1055 | align-items: center; 1056 | justify-content: center; 1057 | } 1058 | 1059 | .satus-shortcut__value>.satus-shortcut__key { 1060 | font-size: 14px; 1061 | 1062 | min-width: 24px; 1063 | height: 24px; 1064 | } 1065 | 1066 | .satus-shortcut__plus { 1067 | position: relative; 1068 | 1069 | width: 12px; 1070 | height: 12px; 1071 | margin: 8px; 1072 | } 1073 | 1074 | .satus-shortcut__plus::before { 1075 | position: absolute; 1076 | top: 0; 1077 | left: 5px; 1078 | 1079 | width: 2px; 1080 | height: 12px; 1081 | 1082 | content: ''; 1083 | 1084 | background-color: #aaa; 1085 | } 1086 | 1087 | .satus-shortcut__plus::after { 1088 | position: absolute; 1089 | top: 5px; 1090 | left: 0; 1091 | 1092 | width: 12px; 1093 | height: 2px; 1094 | 1095 | content: ''; 1096 | 1097 | background-color: #aaa; 1098 | } 1099 | 1100 | .satus-shortcut__mouse { 1101 | position: relative; 1102 | 1103 | display: flex; 1104 | 1105 | width: 28px; 1106 | height: 36px; 1107 | 1108 | border-radius: 50%; 1109 | border-top-left-radius: 12px; 1110 | border-top-right-radius: 12px; 1111 | background: #fff; 1112 | box-shadow: 0 1px 3px rgba(0, 0, 0, .15), inset 0 -3px 0 rgba(0, 0, 0, .1); 1113 | } 1114 | 1115 | .satus-shortcut__value>.satus-shortcut__mouse { 1116 | width: 22px; 1117 | height: 28px; 1118 | } 1119 | 1120 | .satus-shortcut__mouse>div { 1121 | position: absolute; 1122 | top: 0; 1123 | left: calc(50% - 1px); 1124 | 1125 | width: 2px; 1126 | height: 11px; 1127 | 1128 | border-radius: 2px; 1129 | background: #ccc; 1130 | } 1131 | 1132 | .satus-shortcut__mouse::before { 1133 | position: absolute; 1134 | top: -16%; 1135 | right: 14%; 1136 | 1137 | width: 2px; 1138 | height: 60%; 1139 | 1140 | content: ''; 1141 | 1142 | background: #f96754; 1143 | } 1144 | 1145 | .satus-shortcut__mouse.false::before { 1146 | top: -6%; 1147 | } 1148 | 1149 | .satus-shortcut__mouse.false::after { 1150 | position: absolute; 1151 | top: -20%; 1152 | right: calc(14% - 4px); 1153 | 1154 | width: 0; 1155 | height: 0; 1156 | 1157 | content: ''; 1158 | 1159 | border-right: 5px solid transparent; 1160 | border-bottom: 8px solid #f96754; 1161 | border-left: 5px solid transparent; 1162 | } 1163 | 1164 | .satus-shortcut__mouse.true::after { 1165 | position: absolute; 1166 | top: 40%; 1167 | right: calc(14% - 4px); 1168 | 1169 | width: 0; 1170 | height: 0; 1171 | 1172 | content: ''; 1173 | 1174 | border-top: 8px solid #f96754; 1175 | border-right: 5px solid transparent; 1176 | border-left: 5px solid transparent; 1177 | } 1178 | 1179 | .satus-shortcut__mouse.click::before { 1180 | position: absolute; 1181 | top: 0; 1182 | left: -1px; 1183 | 1184 | width: 10px; 1185 | height: 10px; 1186 | 1187 | content: ''; 1188 | 1189 | border-radius: 50%; 1190 | background: #f96754; 1191 | } 1192 | 1193 | .satus-shortcut__mouse.middle::before { 1194 | position: absolute; 1195 | z-index: 1; 1196 | top: 0; 1197 | left: 50%; 1198 | 1199 | width: 10px; 1200 | height: 10px; 1201 | 1202 | content: ''; 1203 | transform: translateX(-50%); 1204 | 1205 | border-radius: 50%; 1206 | background: #f96754; 1207 | } 1208 | 1209 | .satus-shortcut__mouse.context::before { 1210 | position: absolute; 1211 | top: 0; 1212 | left: 15px; 1213 | 1214 | width: 10px; 1215 | height: 10px; 1216 | 1217 | content: ''; 1218 | 1219 | border-radius: 50%; 1220 | background: #f96754; 1221 | } 1222 | 1223 | .satus-section_shortcut { 1224 | width: 100%; 1225 | margin: 8px 0 0; 1226 | 1227 | justify-content: flex-end; 1228 | } 1229 | 1230 | .satus-button_shortcut { 1231 | font-weight: 500; 1232 | 1233 | overflow: hidden; 1234 | 1235 | height: 28px; 1236 | min-height: 28px; 1237 | margin-right: 2px; 1238 | padding: 4px 8px; 1239 | 1240 | text-transform: uppercase; 1241 | 1242 | color: #f96754; 1243 | border-radius: 4px; 1244 | } 1245 | /*-------------------------------------------------------------- 1246 | >>> CHECKBOX 1247 | --------------------------------------------------------------*/ 1248 | 1249 | .satus-checkbox { 1250 | position: relative; 1251 | font: inherit; 1252 | display: flex; 1253 | color: inherit; 1254 | border: none; 1255 | background: transparent; 1256 | appearance: none; 1257 | align-items: center; 1258 | justify-content: flex-start; 1259 | } 1260 | 1261 | .satus-checkbox:hover { 1262 | cursor: pointer; 1263 | background-color: var(--satus-hover); 1264 | } 1265 | 1266 | .satus-checkbox:focus { 1267 | outline: none; 1268 | } 1269 | 1270 | .satus-checkbox__content { 1271 | display: block; 1272 | white-space: nowrap; 1273 | text-overflow: ellipsis; 1274 | overflow: hidden; 1275 | } 1276 | 1277 | .satus-checkbox::before { 1278 | display: flex; 1279 | min-width: 16px; 1280 | width: 16px; 1281 | height: 16px; 1282 | margin: 0 12px 0 0; 1283 | content: ''; 1284 | border: 1px solid var(--satus-checkbox--border); 1285 | border-radius: 6px; 1286 | background: var(--satus-checkbox--background); 1287 | align-items: center; 1288 | justify-content: center; 1289 | } 1290 | 1291 | .satus-checkbox[data-value=true]::before { 1292 | background: var(--satus-primary); 1293 | } 1294 | 1295 | .satus-checkbox[data-value=true]::after { 1296 | position: absolute; 1297 | top: 20px; 1298 | left: 20px; 1299 | width: 8px; 1300 | height: 4px; 1301 | content: ''; 1302 | transform: rotate(-45deg); 1303 | border: 2px solid var(--satus-checkbox--mark); 1304 | border-top: none; 1305 | border-right: none; 1306 | } 1307 | /*-------------------------------------------------------------- 1308 | >>> SWITCH 1309 | ---------------------------------------------------------------- 1310 | # Container 1311 | # Track 1312 | # Thumb 1313 | --------------------------------------------------------------*/ 1314 | 1315 | 1316 | /*-------------------------------------------------------------- 1317 | # CONTAINER 1318 | --------------------------------------------------------------*/ 1319 | 1320 | .satus-switch { 1321 | font: inherit; 1322 | display: flex; 1323 | transition: background-color 75ms; 1324 | color: inherit; 1325 | border: none; 1326 | outline: none; 1327 | background-color: transparent; 1328 | justify-content: space-between; 1329 | align-items: center; 1330 | } 1331 | 1332 | .satus-switch:hover { 1333 | cursor: pointer; 1334 | } 1335 | 1336 | .satus-switch__content { 1337 | display: flex; 1338 | align-items: center; 1339 | } 1340 | 1341 | .satus-switch__content>svg { 1342 | width: 20px; 1343 | height: 18px; 1344 | margin: 0 14px 0 0; 1345 | opacity: .75; 1346 | } 1347 | 1348 | 1349 | /*-------------------------------------------------------------- 1350 | # TRACK 1351 | --------------------------------------------------------------*/ 1352 | 1353 | .satus-switch>i { 1354 | width: 32px; 1355 | height: 18px; 1356 | transition: background-color 150ms; 1357 | border-radius: 18px; 1358 | background-color: var(--satus-switch-track); 1359 | flex: 0 0 32px; 1360 | } 1361 | 1362 | .satus-section--card .satus-switch>i { 1363 | margin-left: 16px; 1364 | } 1365 | 1366 | .satus-switch[data-value='true']>i { 1367 | background-color: var(--satus-primary); 1368 | } 1369 | 1370 | 1371 | /*-------------------------------------------------------------- 1372 | # THUMB 1373 | --------------------------------------------------------------*/ 1374 | 1375 | .satus-switch>i::before { 1376 | display: block; 1377 | width: 14px; 1378 | height: 14px; 1379 | margin: 2px; 1380 | content: ''; 1381 | transition: transform 150ms cubic-bezier(.4, 0, .2, 1); 1382 | border-radius: 50%; 1383 | background-color: var(--satus-switch-thumb); 1384 | will-change: transform; 1385 | } 1386 | 1387 | .satus-switch[data-value='true']>i::before { 1388 | transform: translateX(14px); 1389 | } 1390 | -------------------------------------------------------------------------------- /assets/satus/satus.js: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | >>> CORE 3 | ---------------------------------------------------------------- 4 | # Global variable 5 | # Append 6 | # Attr 7 | # Camelize 8 | # Class 9 | # Create element 10 | # CSS 11 | # Empty 12 | # Events 13 | # Get property 14 | # Is 15 | # On 16 | # Render 17 | # Storage 18 | # Clear 19 | # Get 20 | # Import 21 | # Set 22 | # Remove 23 | # Localization 24 | # Log 25 | # Text 26 | --------------------------------------------------------------*/ 27 | 28 | /*-------------------------------------------------------------- 29 | # GLOBAL VARIABLE 30 | --------------------------------------------------------------*/ 31 | 32 | var satus = { 33 | components: {}, 34 | events: { 35 | data: {} 36 | }, 37 | locale: { 38 | data: {} 39 | }, 40 | storage: { 41 | data: {}, 42 | type: 'extension' 43 | } 44 | }; 45 | 46 | 47 | /*-------------------------------------------------------------- 48 | # APPEND 49 | --------------------------------------------------------------*/ 50 | 51 | satus.append = function (child, parent) { 52 | (parent || document.body).appendChild(child); 53 | }; 54 | 55 | 56 | /*-------------------------------------------------------------- 57 | # ATTR 58 | --------------------------------------------------------------*/ 59 | 60 | satus.attr = function (element, attributes) { 61 | if (attributes) { 62 | for (var name in attributes) { 63 | var value = attributes[name]; 64 | 65 | if (typeof value === 'function') { 66 | value = value(); 67 | } 68 | 69 | if (element.namespaceURI) { 70 | if (value === false) { 71 | element.removeAttributeNS(null, name); 72 | } else { 73 | element.setAttributeNS(null, name, value); 74 | } 75 | } else { 76 | if (value === false) { 77 | element.removeAttribute(name); 78 | } else { 79 | element.setAttribute(name, value); 80 | } 81 | } 82 | } 83 | } 84 | }; 85 | 86 | 87 | /*-------------------------------------------------------------- 88 | # CAMELIZE 89 | --------------------------------------------------------------*/ 90 | 91 | satus.camelize = function (string) { 92 | var result = ''; 93 | 94 | for (var i = 0, l = string.length; i < l; i++) { 95 | var character = string[i]; 96 | 97 | if (character === '-') { 98 | i++; 99 | 100 | result += string[i].toUpperCase(); 101 | } else { 102 | result += character; 103 | } 104 | } 105 | 106 | return result; 107 | }; 108 | 109 | 110 | /*-------------------------------------------------------------- 111 | # CLASS 112 | --------------------------------------------------------------*/ 113 | 114 | satus.class = function (element, className) { 115 | if (className) { 116 | element.classList.add(className); 117 | } 118 | }; 119 | 120 | 121 | /*-------------------------------------------------------------- 122 | # CREATE ELEMENT 123 | --------------------------------------------------------------*/ 124 | 125 | satus.createElement = function (tagName, componentName, namespaceURI) { 126 | var camelizedTagName = this.camelize(tagName), 127 | className = 'satus-' + (componentName || tagName), 128 | element, 129 | match = className.match(/__[^__]+/g); 130 | 131 | if (!namespaceURI) { 132 | if (tagName === 'svg') { 133 | namespaceURI = 'http://www.w3.org/2000/svg'; 134 | } 135 | } 136 | 137 | if (namespaceURI) { 138 | element = document.createElementNS(namespaceURI, tagName); 139 | } else if (this.components[camelizedTagName]) { 140 | element = document.createElement('div'); 141 | } else { 142 | element = document.createElement(tagName); 143 | } 144 | 145 | if (match && match.length > 1) { 146 | className = className.slice(0, className.indexOf('__')) + match[match.length - 1]; 147 | } 148 | 149 | element.componentName = componentName; 150 | element.className = className; 151 | 152 | element.createChildElement = function (tagName, componentName, namespaceURI) { 153 | var element = satus.createElement(tagName, this.componentName + '__' + (componentName || tagName), namespaceURI); 154 | 155 | this.appendChild(element); 156 | 157 | return element; 158 | }; 159 | 160 | return element; 161 | }; 162 | 163 | 164 | /*-------------------------------------------------------------- 165 | # CSS 166 | --------------------------------------------------------------*/ 167 | 168 | satus.css = function (element, property) { 169 | return window.getComputedStyle(element).getPropertyValue(property); 170 | }; 171 | 172 | 173 | /*-------------------------------------------------------------- 174 | # DATA 175 | --------------------------------------------------------------*/ 176 | 177 | satus.data = function (element, data) { 178 | if (data) { 179 | for (var key in data) { 180 | element.dataset[key] = data[key]; 181 | } 182 | } 183 | }; 184 | 185 | 186 | /*-------------------------------------------------------------- 187 | # EMPTY 188 | --------------------------------------------------------------*/ 189 | 190 | satus.empty = function (element, exclude = []) { 191 | for (var i = element.childNodes.length - 1; i > -1; i--) { 192 | var child = element.childNodes[i]; 193 | 194 | if (exclude.indexOf(child) === -1) { 195 | child.remove(); 196 | } 197 | } 198 | }; 199 | 200 | 201 | /*-------------------------------------------------------------- 202 | # EVENTS 203 | --------------------------------------------------------------*/ 204 | 205 | /*-------------------------------------------------------------- 206 | # ON 207 | --------------------------------------------------------------*/ 208 | 209 | satus.events.on = function (type, handler) { 210 | if (!this.data[type]) { 211 | this.data[type] = []; 212 | } 213 | 214 | this.data[type].push(handler); 215 | }; 216 | 217 | 218 | /*-------------------------------------------------------------- 219 | # TRIGGER 220 | --------------------------------------------------------------*/ 221 | 222 | satus.events.trigger = function (type) { 223 | var handlers = this.data[type]; 224 | 225 | if (handlers) { 226 | for (var i = 0, l = handlers.length; i < l; i++) { 227 | handlers[i](); 228 | } 229 | } 230 | }; 231 | 232 | 233 | /*-------------------------------------------------------------- 234 | # FETCH 235 | --------------------------------------------------------------*/ 236 | 237 | satus.fetch = function (url, success, error) { 238 | fetch(url).then(function (response) { 239 | if (response.ok) { 240 | response.json().then(success); 241 | } else { 242 | error(); 243 | } 244 | }).catch(function () { 245 | error(success); 246 | }); 247 | }; 248 | 249 | 250 | /*-------------------------------------------------------------- 251 | # GET PROPERTY 252 | --------------------------------------------------------------*/ 253 | 254 | satus.getProperty = function (object, string) { 255 | var properties = string.split('.'); 256 | 257 | for (var i = 0, l = properties.length; i < l; i++) { 258 | var property = properties[i]; 259 | 260 | console.log(object); 261 | 262 | if (object = object[property]) { 263 | if (i === l - 1) { 264 | return object; 265 | } 266 | } else { 267 | return false; 268 | } 269 | } 270 | }; 271 | 272 | 273 | /*-------------------------------------------------------------- 274 | # ISSET 275 | --------------------------------------------------------------*/ 276 | 277 | satus.isset = function (variable) { 278 | if (variable === null || variable === undefined) { 279 | return false; 280 | } 281 | 282 | return true; 283 | }; 284 | 285 | 286 | /*-------------------------------------------------------------- 287 | # IS 288 | --------------------------------------------------------------*/ 289 | 290 | satus.isArray = function (array) { 291 | if (Array.isArray(array)) { 292 | return true; 293 | } else { 294 | return false; 295 | } 296 | }; 297 | 298 | satus.isElement = function (object) { 299 | return object instanceof Element || object instanceof HTMLDocument; 300 | }; 301 | 302 | satus.isNumber = function (number) { 303 | if (typeof number === 'number' && isNaN(number) === false) { 304 | return true; 305 | } else { 306 | return false; 307 | } 308 | }; 309 | 310 | satus.isObject = function (object) { 311 | return object instanceof Object; 312 | }; 313 | 314 | 315 | /*-------------------------------------------------------------- 316 | # ON 317 | --------------------------------------------------------------*/ 318 | 319 | satus.on = function (element, listeners) { 320 | if (listeners) { 321 | for (var type in listeners) { 322 | var listener = listeners[type], 323 | listener_type = typeof listener; 324 | 325 | if (type === 'selectionchange') { 326 | element = document; 327 | } 328 | 329 | if (listener_type === 'function') { 330 | element.addEventListener(type, listener); 331 | } else if (listener_type === 'object') { 332 | element.addEventListener(type, function (event) { 333 | var target = this.skeleton.on[event.type], 334 | parent = this.parentNode; 335 | 336 | target.parentSkeleton = this.skeleton; 337 | target.parentElement = this; 338 | 339 | while (parent.componentName !== 'layers' && parent.componentName !== 'base' && parent !== document.body && parent.parentNode) { 340 | parent = parent.parentNode; 341 | } 342 | 343 | if (parent.componentName === 'layers' && target.component !== 'modal') { 344 | parent.open(target); 345 | } else if (this.baseProvider && this.baseProvider.layers.length === 1) { 346 | satus.render(target, this.baseProvider.layers[0]); 347 | } else { 348 | satus.render(target, this.baseProvider); 349 | } 350 | }); 351 | } else if (listener_type === 'string') { 352 | element.addEventListener(type, function () { 353 | var match = this.skeleton.on[event.type].match(/(["'`].+["'`]|[^.()]+)/g), 354 | target = this.baseProvider; 355 | 356 | for (var i = 0, l = match.length; i < l; i++) { 357 | var key = match[i]; 358 | 359 | if (target.skeleton[key]) { 360 | target = target.skeleton[key]; 361 | } else { 362 | if (typeof target[key] === 'function') { 363 | target[key](); 364 | } else { 365 | target = target[key]; 366 | } 367 | } 368 | 369 | if (target.rendered) { 370 | target = target.rendered; 371 | } 372 | } 373 | }); 374 | } 375 | } 376 | } 377 | }; 378 | 379 | 380 | /*-------------------------------------------------------------- 381 | # PARENTIFY 382 | --------------------------------------------------------------*/ 383 | 384 | satus.parentify = function (parentObject, exclude) { 385 | for (var key in parentObject) { 386 | if (exclude.indexOf(key) === -1) { 387 | var child = parentObject[key]; 388 | 389 | child.parentObject = parentObject; 390 | 391 | if (typeof child === 'object' && child.component !== 'shortcut') { 392 | this.parentify(child, exclude); 393 | } 394 | } 395 | } 396 | }; 397 | 398 | 399 | /*-------------------------------------------------------------- 400 | # PREPEND 401 | --------------------------------------------------------------*/ 402 | 403 | satus.prepend = function (child, parent) { 404 | if (this.isElement(child)) { 405 | parent.prepend(child); 406 | } else if (this.isObject(child)) { 407 | this.render(child, parent, undefined, undefined, true); 408 | } 409 | }; 410 | 411 | 412 | /*-------------------------------------------------------------- 413 | # PROPERTIES 414 | --------------------------------------------------------------*/ 415 | 416 | satus.properties = function (element, properties) { 417 | if (properties) { 418 | for (var key in properties) { 419 | var property = properties[key]; 420 | 421 | if (['placeholder', 'title'].indexOf(key) !== -1) { 422 | property = satus.locale.get(property); 423 | } 424 | 425 | element[key] = property; 426 | } 427 | } 428 | }; 429 | 430 | 431 | /*-------------------------------------------------------------- 432 | # RENDER 433 | --------------------------------------------------------------*/ 434 | 435 | satus.render = function (skeleton, container, property, childrenOnly, prepend) { 436 | var element; 437 | 438 | if (skeleton.component && childrenOnly !== true) { 439 | var tagName = skeleton.component, 440 | camelizedTagName = this.camelize(tagName), 441 | namespaceURI = skeleton.namespaceURI; 442 | 443 | if (!namespaceURI) { 444 | if (tagName === 'svg') { 445 | namespaceURI = 'http://www.w3.org/2000/svg'; 446 | } else if (skeleton.parentSkeleton && skeleton.parentSkeleton.namespaceURI) { 447 | namespaceURI = skeleton.parentSkeleton.namespaceURI; 448 | } 449 | 450 | skeleton.namespaceURI = namespaceURI; 451 | } 452 | 453 | element = this.createElement(tagName, tagName, namespaceURI); 454 | 455 | skeleton.rendered = element; 456 | element.skeleton = skeleton; 457 | element.childrenContainer = element; 458 | element.componentName = tagName; 459 | 460 | if (skeleton.variant) { 461 | element.className += ' satus-' + tagName + '--' + skeleton.variant; 462 | } 463 | 464 | if (container) { 465 | element.baseProvider = container.baseProvider; 466 | } 467 | 468 | this.attr(element, skeleton.attr); 469 | this.style(element, skeleton.style); 470 | this.data(element, skeleton.data); 471 | this.class(element, skeleton.class); 472 | this.properties(element, skeleton.properties); 473 | this.text(element, skeleton.text); 474 | this.on(element, skeleton.on); 475 | this.prepend(skeleton.before, element); 476 | 477 | if (prepend) { 478 | this.prepend(element, container); 479 | } else { 480 | this.append(element, container); 481 | } 482 | 483 | element.storage = (function () { 484 | var parent = element, 485 | key = skeleton.storage || property || false, 486 | value; 487 | 488 | if (skeleton.storage !== false) { 489 | if (key) { 490 | value = satus.storage.get(key); 491 | } 492 | 493 | if (skeleton.hasOwnProperty('value') && value === undefined) { 494 | value = skeleton.value; 495 | } 496 | } 497 | 498 | return Object.defineProperties({}, { 499 | key: { 500 | get: function () { 501 | return key; 502 | }, 503 | set: function (string) { 504 | key = string; 505 | } 506 | }, 507 | value: { 508 | get: function () { 509 | return value; 510 | }, 511 | set: function (val) { 512 | value = val; 513 | 514 | if (skeleton.storage !== false) { 515 | satus.storage.set(key, val); 516 | 517 | parent.dispatchEvent(new CustomEvent('change')); 518 | } 519 | } 520 | } 521 | }); 522 | }()); 523 | 524 | if (this.components[camelizedTagName]) { 525 | this.components[camelizedTagName](element, skeleton); 526 | } 527 | 528 | element.dispatchEvent(new CustomEvent('render')); 529 | 530 | container = element.childrenContainer || element; 531 | } 532 | 533 | if (!element || element.renderChildren !== false) { 534 | for (var key in skeleton) { 535 | var item = skeleton[key]; 536 | 537 | if (key !== 'parentSkeleton' && key !== 'parentElement' && key !== 'parentObject') { 538 | if (item && item.component) { 539 | item.parentSkeleton = skeleton; 540 | 541 | if (element) { 542 | item.parentElement = element; 543 | } 544 | 545 | this.render(item, container, key, undefined, prepend); 546 | } 547 | } 548 | } 549 | } 550 | 551 | return element; 552 | }; 553 | 554 | 555 | /*-------------------------------------------------------------- 556 | # STORAGE 557 | --------------------------------------------------------------*/ 558 | 559 | /*-------------------------------------------------------------- 560 | # CLEAR 561 | --------------------------------------------------------------*/ 562 | 563 | satus.storage.clear = function (callback) { 564 | this.data = {}; 565 | 566 | chrome.storage.local.clear(function () { 567 | satus.events.trigger('storage-clear'); 568 | 569 | if (callback) { 570 | callback(); 571 | } 572 | }); 573 | }; 574 | 575 | 576 | /*-------------------------------------------------------------- 577 | # GET 578 | --------------------------------------------------------------*/ 579 | 580 | satus.storage.get = function (key, callback) { 581 | var target = this.data; 582 | 583 | if (typeof key !== 'string') { 584 | return; 585 | } 586 | 587 | key = key.split('/').filter(function (value) { 588 | return value != ''; 589 | }); 590 | 591 | for (var i = 0, l = key.length; i < l; i++) { 592 | if (satus.isset(target[key[i]])) { 593 | target = target[key[i]]; 594 | } else { 595 | return undefined; 596 | } 597 | } 598 | 599 | if (typeof target === 'function') { 600 | return target(); 601 | } else { 602 | return target; 603 | } 604 | }; 605 | 606 | 607 | /*-------------------------------------------------------------- 608 | # IMPORT 609 | --------------------------------------------------------------*/ 610 | 611 | satus.storage.import = function (keys, callback) { 612 | var self = this; 613 | 614 | if (typeof keys === 'function') { 615 | callback = keys; 616 | 617 | keys = undefined; 618 | } 619 | 620 | chrome.storage.local.get(keys, function (items) { 621 | for (var key in items) { 622 | self.data[key] = items[key]; 623 | } 624 | 625 | satus.log('STORAGE: data was successfully imported'); 626 | 627 | satus.events.trigger('storage-import'); 628 | 629 | if (callback) { 630 | callback(items); 631 | } 632 | }); 633 | }; 634 | 635 | 636 | /*-------------------------------------------------------------- 637 | # REMOVE 638 | --------------------------------------------------------------*/ 639 | 640 | satus.storage.remove = function (key) { 641 | delete this.data[key]; 642 | 643 | chrome.storage.local.remove(key, function () { 644 | satus.events.trigger('storage-remove'); 645 | }); 646 | }; 647 | 648 | 649 | /*-------------------------------------------------------------- 650 | # SET 651 | --------------------------------------------------------------*/ 652 | 653 | satus.storage.set = function (key, value, callback) { 654 | var items = {}, 655 | target = this.data; 656 | 657 | if (typeof key !== 'string') { 658 | return; 659 | } 660 | 661 | key = key.split('/').filter(function (value) { 662 | return value != ''; 663 | }); 664 | 665 | for (var i = 0, l = key.length; i < l; i++) { 666 | var item = key[i]; 667 | 668 | if (i < l - 1) { 669 | 670 | if (target[item]) { 671 | target = target[item]; 672 | } else { 673 | target[item] = {}; 674 | 675 | target = target[item]; 676 | } 677 | } else { 678 | target[item] = value; 679 | } 680 | } 681 | 682 | for (var key in this.data) { 683 | if (typeof this.data[key] !== 'function') { 684 | items[key] = this.data[key]; 685 | } 686 | } 687 | 688 | chrome.storage.local.set(items, function () { 689 | satus.events.trigger('storage-set'); 690 | 691 | if (callback) { 692 | callback(); 693 | } 694 | }); 695 | }; 696 | 697 | 698 | /*-------------------------------------------------------------- 699 | # LOCALIZATION 700 | --------------------------------------------------------------*/ 701 | 702 | /*-------------------------------------------------------------- 703 | # GET 704 | --------------------------------------------------------------*/ 705 | 706 | satus.locale.get = function (string) { 707 | return this.data[string] || string; 708 | }; 709 | 710 | 711 | /*-------------------------------------------------------------- 712 | # IMPORT 713 | ---------------------------------------------------------------- 714 | satus.locale.import(url, onload, onsuccess); 715 | --------------------------------------------------------------*/ 716 | 717 | satus.locale.import = function (code, callback, path) { 718 | var language = code || window.navigator.language; 719 | 720 | if (language.indexOf('en') === 0) { 721 | language = 'en'; 722 | } 723 | 724 | if (!path) { 725 | path = '_locales/'; 726 | } 727 | 728 | satus.fetch(chrome.runtime.getURL(path + language + '/messages.json'), function (response) { 729 | for (var key in response) { 730 | satus.locale.data[key] = response[key].message; 731 | } 732 | 733 | satus.log('LOCALE: data was successfully imported'); 734 | 735 | if (callback) { 736 | callback(); 737 | } 738 | }, function (success) { 739 | satus.fetch(chrome.runtime.getURL(path + 'en/messages.json'), success, function () { 740 | success(); 741 | }); 742 | }); 743 | }; 744 | 745 | 746 | /*-------------------------------------------------------------- 747 | # LOG 748 | --------------------------------------------------------------*/ 749 | 750 | satus.log = function () { 751 | console.log.apply(null, arguments); 752 | }; 753 | 754 | 755 | /*-------------------------------------------------------------- 756 | # STYLE 757 | --------------------------------------------------------------*/ 758 | 759 | satus.style = function (element, object) { 760 | if (object) { 761 | for (var key in object) { 762 | element.style[key] = object[key]; 763 | } 764 | } 765 | }; 766 | 767 | 768 | /*-------------------------------------------------------------- 769 | # TEXT 770 | --------------------------------------------------------------*/ 771 | 772 | satus.text = function (element, value) { 773 | if (value) { 774 | element.appendChild(document.createTextNode(this.locale.get(value))); 775 | } 776 | }; 777 | /*-------------------------------------------------------------- 778 | >>> MODAL 779 | --------------------------------------------------------------*/ 780 | 781 | satus.components.modal = function (component, skeleton) { 782 | component.scrim = component.createChildElement('div', 'scrim'); 783 | component.surface = component.createChildElement('div', 'surface'); 784 | 785 | component.close = function () { 786 | var component = this; 787 | 788 | this.classList.add('satus-modal--closing'); 789 | 790 | setTimeout(function () { 791 | component.remove(); 792 | 793 | component.dispatchEvent(new CustomEvent('close')); 794 | }, Number(satus.css(this.surface, 'animation-duration').replace(/[^0-9.]/g, '')) * 1000); 795 | }; 796 | 797 | component.scrim.addEventListener('click', function () { 798 | this.parentNode.close(); 799 | }); 800 | 801 | component.childrenContainer = component.surface; 802 | }; 803 | /*-------------------------------------------------------------- 804 | >>> GRID 805 | --------------------------------------------------------------*/ 806 | 807 | satus.components.grid = function (component, skeleton) { 808 | console.log(component, skeleton); 809 | }; 810 | /*-------------------------------------------------------------- 811 | >>> TEXT FIELD 812 | --------------------------------------------------------------*/ 813 | 814 | satus.components.textField = function (component, skeleton) { 815 | var container = component.createChildElement('div', 'container'), 816 | input = container.createChildElement('input'), 817 | pre = container.createChildElement('pre'), 818 | hiddenValue = container.createChildElement('pre', 'hidden-value'), 819 | selection = container.createChildElement('div', 'selection'), 820 | cursor = container.createChildElement('div', 'cursor'); 821 | 822 | component.placeholder = skeleton.placeholder; 823 | component.input = input; 824 | component.pre = pre; 825 | component.hiddenValue = hiddenValue; 826 | component.selection = selection; 827 | component.cursor = cursor; 828 | component.syntax = { 829 | current: 'text', 830 | handlers: { 831 | regex: function (value, target) { 832 | var regex_token = /\[\^?]?(?:[^\\\]]+|\\[\S\s]?)*]?|\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9][0-9]*|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)|\((?:\?[:=!]?)?|(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??|[^.?*+^${[()|\\]+|./g, 833 | char_class_token = /[^\\-]+|-|\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)/g, 834 | char_class_parts = /^(\[\^?)(]?(?:[^\\\]]+|\\[\S\s]?)*)(]?)$/, 835 | quantifier = /^(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??$/, 836 | matches = value.match(regex_token); 837 | 838 | function create(type, string) { 839 | var span = document.createElement('span'); 840 | 841 | span.className = type; 842 | span.textContent = string; 843 | 844 | target.appendChild(span); 845 | } 846 | 847 | for (var i = 0, l = matches.length; i < l; i++) { 848 | var match = matches[i]; 849 | 850 | if (match[0] === '[') { 851 | create('character-class', match); 852 | } else if (match[0] === '(') { 853 | create('group', match); 854 | } else if (match[0] === ')') { 855 | create('group', match); 856 | } else if (match[0] === '\\' || match === '^') { 857 | create('anchor', match); 858 | } else if (quantifier.test(match)) { 859 | create('quantifier', match); 860 | } else if (match === '|' || match === '.') { 861 | create('metasequence', match); 862 | } else { 863 | create('text', match); 864 | } 865 | } 866 | } 867 | }, 868 | set: function (syntax) { 869 | if (this.handlers[syntax]) { 870 | this.current = syntax; 871 | } else { 872 | this.current = 'text'; 873 | } 874 | 875 | pre.update(); 876 | } 877 | }; 878 | component.focus = function () { 879 | this.input.focus(); 880 | }; 881 | 882 | Object.defineProperty(component, 'value', { 883 | get: function () { 884 | return this.input.value; 885 | }, 886 | set: function (value) { 887 | this.input.value = value; 888 | } 889 | }); 890 | 891 | if (skeleton.syntax) { 892 | component.syntax.set(skeleton.syntax); 893 | } 894 | 895 | input.type = 'text'; 896 | 897 | selection.setAttribute('disabled', ''); 898 | 899 | pre.update = function () { 900 | var component = this.parentNode.parentNode, 901 | handler = component.syntax.handlers[component.syntax.current], 902 | value = component.value || ''; 903 | 904 | for (var i = this.childNodes.length - 1; i > -1; i--) { 905 | this.childNodes[i].remove(); 906 | } 907 | 908 | if (handler) { 909 | handler(value, this); 910 | } else { 911 | this.textContent = value; 912 | } 913 | 914 | if (value.length === 0) { 915 | var placeholder = component.placeholder; 916 | 917 | if (typeof placeholder === 'function') { 918 | placeholder = component.placeholder(); 919 | } else { 920 | placeholder = satus.locale.get(placeholder); 921 | } 922 | 923 | this.textContent = placeholder; 924 | } 925 | }; 926 | 927 | cursor.update = function () { 928 | var component = this.parentNode.parentNode, 929 | input = component.input, 930 | start = input.selectionStart, 931 | end = input.selectionEnd, 932 | value = input.value; 933 | 934 | this.style.animation = 'none'; 935 | 936 | if (start === end) { 937 | component.selection.setAttribute('disabled', ''); 938 | } else { 939 | component.selection.removeAttribute('disabled'); 940 | 941 | component.hiddenValue.textContent = value.substring(0, start); 942 | 943 | component.selection.style.left = component.hiddenValue.offsetWidth - input.scrollLeft + 'px'; 944 | 945 | component.hiddenValue.textContent = value.substring(start, end); 946 | 947 | component.selection.style.width = component.hiddenValue.offsetWidth + 'px'; 948 | } 949 | 950 | if (input.selectionDirection === 'forward') { 951 | component.hiddenValue.textContent = value.substring(0, end); 952 | } else { 953 | component.hiddenValue.textContent = value.substring(0, start); 954 | } 955 | 956 | this.style.left = component.hiddenValue.offsetWidth - input.scrollLeft + 'px'; 957 | 958 | this.style.animation = ''; 959 | 960 | component.hiddenValue.textContent = ''; 961 | }; 962 | 963 | document.addEventListener('selectionchange', function (event) { 964 | component.pre.update(); 965 | component.cursor.update(); 966 | }); 967 | 968 | input.addEventListener('input', function () { 969 | var component = this.parentNode.parentNode; 970 | 971 | component.storage.value = this.value; 972 | 973 | component.pre.update(); 974 | component.cursor.update(); 975 | }); 976 | 977 | input.addEventListener('scroll', function (event) { 978 | var component = this.parentNode.parentNode; 979 | 980 | component.pre.style.left = -this.scrollLeft + 'px'; 981 | 982 | component.pre.update(); 983 | component.cursor.update(); 984 | }); 985 | 986 | component.addEventListener('change', function () { 987 | this.pre.update(); 988 | this.cursor.update(); 989 | }); 990 | 991 | component.value = component.storage.value || ''; 992 | 993 | component.addEventListener('render', function () { 994 | component.pre.update(); 995 | component.cursor.update(); 996 | }); 997 | 998 | if (skeleton.on) { 999 | for (var type in skeleton.on) { 1000 | input.addEventListener(type, function (event) { 1001 | this.parentNode.parentNode.dispatchEvent(new Event(event.type)); 1002 | }); 1003 | } 1004 | } 1005 | }; 1006 | /*-------------------------------------------------------------- 1007 | >>> SELECT 1008 | --------------------------------------------------------------*/ 1009 | 1010 | satus.components.select = function (component, skeleton) { 1011 | var component_content = document.createElement('span'), 1012 | component_value = document.createElement('span'), 1013 | component_select = document.createElement('select'); 1014 | 1015 | for (var i = 0, l = skeleton.options.length; i < l; i++) { 1016 | var option = document.createElement('option'); 1017 | 1018 | option.value = skeleton.options[i].value; 1019 | 1020 | satus.text(option, skeleton.options[i].text); 1021 | 1022 | component_select.appendChild(option); 1023 | } 1024 | 1025 | component.inner = component_content; 1026 | component.select = component_select; 1027 | 1028 | Object.defineProperty(component, 'value', { 1029 | get() { 1030 | return this.select.value; 1031 | }, 1032 | set(value) { 1033 | this.select.value = value; 1034 | } 1035 | }); 1036 | 1037 | component.render = function () { 1038 | var value_element = this.children[2]; 1039 | 1040 | satus.empty(value_element); 1041 | 1042 | satus.text(value_element, this.select.options[this.select.selectedIndex].text); 1043 | 1044 | this.dataset.value = this.value; 1045 | }; 1046 | 1047 | component.addEventListener('render', function () { 1048 | this.value = this.storage.value || this.skeleton.options[0].value; 1049 | 1050 | this.render(); 1051 | }); 1052 | 1053 | component_select.addEventListener('change', function () { 1054 | var component = this.parentNode; 1055 | 1056 | component.storage.value = this.value; 1057 | 1058 | component.render(); 1059 | }); 1060 | 1061 | component.appendChild(component_select); 1062 | component.appendChild(component_content); 1063 | component.appendChild(component_value); 1064 | }; 1065 | /*-------------------------------------------------------------- 1066 | >>> BASE 1067 | --------------------------------------------------------------*/ 1068 | 1069 | satus.components.base = function (component) { 1070 | component.baseProvider = component; 1071 | component.layers = []; 1072 | }; 1073 | /*-------------------------------------------------------------- 1074 | >>> SIDEBAR 1075 | --------------------------------------------------------------*/ 1076 | 1077 | satus.components.sidebar = function (component, skeleton) {}; 1078 | /*-------------------------------------------------------------- 1079 | >>> LAYERS 1080 | --------------------------------------------------------------*/ 1081 | 1082 | satus.components.layers = function (component, skeleton) { 1083 | component.path = []; 1084 | component.renderChildren = false; 1085 | component.baseProvider.layers.push(component); 1086 | 1087 | component.back = function () { 1088 | if (this.path.length > 1) { 1089 | this.path.pop(); 1090 | 1091 | this.open(this.path[this.path.length - 1], false); 1092 | } 1093 | }; 1094 | 1095 | component.open = function (skeleton, history) { 1096 | satus.empty(this); 1097 | 1098 | var layer = this.createChildElement('div', 'layer'); 1099 | 1100 | if (history !== false) { 1101 | this.path.push(skeleton); 1102 | } 1103 | 1104 | layer.skeleton = skeleton; 1105 | layer.baseProvider = this.baseProvider; 1106 | 1107 | satus.render(skeleton, layer, undefined, skeleton.component === 'layers'); 1108 | 1109 | this.dispatchEvent(new Event('open')); 1110 | }; 1111 | 1112 | component.update = function () { 1113 | var layer = this.querySelector('.satus-layers__layer'); 1114 | 1115 | satus.empty(layer); 1116 | satus.render(layer.skeleton, layer); 1117 | }; 1118 | 1119 | component.open(skeleton); 1120 | }; 1121 | /*-------------------------------------------------------------- 1122 | >>> LIST 1123 | --------------------------------------------------------------*/ 1124 | 1125 | satus.components.list = function (component, skeleton) { 1126 | for (var i = 0, l = skeleton.items.length; i < l; i++) { 1127 | var li = component.createChildElement('div', 'item'), 1128 | item = skeleton.items[i]; 1129 | 1130 | for (var j = 0, k = item.length; j < k; j++) { 1131 | var child = item[j]; 1132 | 1133 | if (typeof child === 'string') { 1134 | var span = li.createChildElement('span'); 1135 | 1136 | span.textContent = satus.locale.get(child); 1137 | } else { 1138 | satus.render(child, li); 1139 | } 1140 | } 1141 | } 1142 | }; 1143 | /*-------------------------------------------------------------- 1144 | >>> COLOR PICKER 1145 | --------------------------------------------------------------*/ 1146 | 1147 | satus.components.colorPicker = function (component, skeleton) { 1148 | var component_label = component.createChildElement('span', 'label'), 1149 | component_value = component.createChildElement('span', 'value'); 1150 | 1151 | component.inner = component_label; 1152 | component.valueElement = component_value; 1153 | 1154 | component.className = 'satus-button'; 1155 | 1156 | component.addEventListener('click', function () { 1157 | var rgb = this.rgb, 1158 | hsl = satus.color.rgbToHsl(rgb), 1159 | s = hsl[1] / 100, 1160 | l = hsl[2] / 100; 1161 | 1162 | s *= l < .5 ? l : 1 - l; 1163 | 1164 | var v = l + s; 1165 | 1166 | s = 2 * s / (l + s); 1167 | 1168 | satus.render({ 1169 | component: 'modal', 1170 | variant: 'color-picker', 1171 | value: hsl, 1172 | parentElement: this, 1173 | 1174 | palette: { 1175 | component: 'div', 1176 | class: 'satus-color-picker__palette', 1177 | style: { 1178 | 'backgroundColor': 'hsl(' + hsl[0] + 'deg, 100%, 50%)' 1179 | }, 1180 | on: { 1181 | mousedown: function () { 1182 | var palette = this, 1183 | rect = this.getBoundingClientRect(), 1184 | cursor = this.children[0]; 1185 | 1186 | function mousemove(event) { 1187 | var hsl = palette.skeleton.parentSkeleton.storage.value, 1188 | x = event.clientX - rect.left, 1189 | y = event.clientY - rect.top, 1190 | s; 1191 | 1192 | x = Math.min(Math.max(x, 0), rect.width) / (rect.width / 100); 1193 | y = Math.min(Math.max(y, 0), rect.height) / (rect.height / 100); 1194 | 1195 | var v = 100 - y, 1196 | l = (2 - x / 100) * v / 2; 1197 | 1198 | hsl[1] = x * v / (l < 50 ? l * 2 : 200 - l * 2); 1199 | hsl[2] = l; 1200 | 1201 | cursor.style.left = x + '%'; 1202 | cursor.style.top = y + '%'; 1203 | 1204 | palette.nextSibling.children[0].style.backgroundColor = 'hsl(' + hsl[0] + 'deg,' + hsl[1] + '%, ' + hsl[2] + '%)'; 1205 | 1206 | event.preventDefault(); 1207 | } 1208 | 1209 | function mouseup() { 1210 | window.removeEventListener('mousemove', mousemove); 1211 | window.removeEventListener('mouseup', mouseup); 1212 | } 1213 | 1214 | window.addEventListener('mousemove', mousemove); 1215 | window.addEventListener('mouseup', mouseup); 1216 | } 1217 | }, 1218 | 1219 | cursor: { 1220 | component: 'div', 1221 | class: 'satus-color-picker__cursor', 1222 | style: { 1223 | 'left': s * 100 + '%', 1224 | 'top': 100 - v * 100 + '%' 1225 | } 1226 | } 1227 | }, 1228 | section: { 1229 | component: 'section', 1230 | variant: 'color', 1231 | 1232 | color: { 1233 | component: 'div', 1234 | class: 'satus-color-picker__color', 1235 | style: { 1236 | 'backgroundColor': 'rgb(' + this.rgb.join(',') + ')' 1237 | } 1238 | }, 1239 | hue: { 1240 | component: 'slider', 1241 | class: 'satus-color-picker__hue', 1242 | storage: false, 1243 | value: hsl[0], 1244 | max: 360, 1245 | on: { 1246 | change: function () { 1247 | var modal = this.skeleton.parentSkeleton.parentSkeleton, 1248 | hsl = modal.storage.value; 1249 | 1250 | hsl[0] = this.values[0]; 1251 | 1252 | this.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg,' + hsl[1] + '%, ' + hsl[2] + '%)'; 1253 | this.parentSkeletonNode.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg, 100%, 50%)'; 1254 | } 1255 | } 1256 | } 1257 | }, 1258 | actions: { 1259 | component: 'section', 1260 | variant: 'actions', 1261 | 1262 | reset: { 1263 | component: 'button', 1264 | text: 'reset', 1265 | on: { 1266 | click: function () { 1267 | var modal = this.skeleton.parentSkeleton.parentSkeleton, 1268 | component = modal.parentSkeleton; 1269 | 1270 | component.rgb = component.skeleton.value; 1271 | 1272 | component.storage.value = component.rgb; 1273 | 1274 | component.valueElement.style.backgroundColor = 'rgb(' + component.rgb.join(',') + ')'; 1275 | 1276 | modal.rendered.close(); 1277 | } 1278 | } 1279 | }, 1280 | cancel: { 1281 | component: 'button', 1282 | text: 'cancel', 1283 | on: { 1284 | click: function () { 1285 | this.skeleton.parentSkeleton.parentSkeleton.rendered.close(); 1286 | } 1287 | } 1288 | }, 1289 | ok: { 1290 | component: 'button', 1291 | text: 'OK', 1292 | on: { 1293 | click: function () { 1294 | var modal = this.skeleton.parentSkeleton.parentSkeleton, 1295 | component = modal.parentSkeleton; 1296 | 1297 | component.rgb = satus.color.hslToRgb(modal.storage.value); 1298 | 1299 | component.storage.value = component.rgb; 1300 | 1301 | component.valueElement.style.backgroundColor = 'rgb(' + component.rgb.join(',') + ')'; 1302 | 1303 | modal.rendered.close(); 1304 | } 1305 | } 1306 | } 1307 | } 1308 | }, this.baseProvider.layers[0]); 1309 | }); 1310 | 1311 | component.addEventListener('render', function () { 1312 | component.rgb = this.storage.value || [0, 100, 50]; 1313 | 1314 | component_value.style.backgroundColor = 'rgb(' + component.rgb.join(',') + ')'; 1315 | }); 1316 | }; 1317 | 1318 | satus.components.colorPicker = function (component, skeleton) { 1319 | component.color = (function (element) { 1320 | var array; 1321 | 1322 | Object.defineProperty(element, 'value', { 1323 | get: function () { 1324 | return array; 1325 | }, 1326 | set: function (value) { 1327 | array = value; 1328 | 1329 | this.parentNode.storage.value = array; 1330 | 1331 | element.style.backgroundColor = 'rgb(' + value.join(',') + ')'; 1332 | } 1333 | }); 1334 | 1335 | element.value = component.storage.value || component.skeleton.value || [0, 0, 0]; 1336 | 1337 | return element; 1338 | })(component.createChildElement('span', 'value')); 1339 | 1340 | component.addEventListener('click', function () { 1341 | var hsl = satus.color.rgbToHsl(this.color.value), 1342 | s = hsl[1] / 100, 1343 | l = hsl[2] / 100; 1344 | 1345 | s *= l < .5 ? l : 1 - l; 1346 | 1347 | var v = l + s; 1348 | 1349 | s = 2 * s / (l + s); 1350 | 1351 | satus.render({ 1352 | component: 'modal', 1353 | variant: 'color-picker', 1354 | value: hsl, 1355 | parentElement: this, 1356 | 1357 | palette: { 1358 | component: 'div', 1359 | class: 'satus-color-picker__palette', 1360 | style: { 1361 | 'backgroundColor': 'hsl(' + hsl[0] + 'deg, 100%, 50%)' 1362 | }, 1363 | on: { 1364 | mousedown: function (event) { 1365 | if (event.button !== 0) { 1366 | return false; 1367 | } 1368 | 1369 | var palette = this, 1370 | rect = this.getBoundingClientRect(), 1371 | cursor = this.children[0]; 1372 | 1373 | function mousemove(event) { 1374 | var hsl = palette.skeleton.parentSkeleton.value, 1375 | x = event.clientX - rect.left, 1376 | y = event.clientY - rect.top, 1377 | s; 1378 | 1379 | x = Math.min(Math.max(x, 0), rect.width) / (rect.width / 100); 1380 | y = Math.min(Math.max(y, 0), rect.height) / (rect.height / 100); 1381 | 1382 | var v = 100 - y, 1383 | l = (2 - x / 100) * v / 2; 1384 | 1385 | hsl[1] = x * v / (l < 50 ? l * 2 : 200 - l * 2); 1386 | hsl[2] = l; 1387 | 1388 | cursor.style.left = x + '%'; 1389 | cursor.style.top = y + '%'; 1390 | 1391 | palette.nextSibling.children[0].style.backgroundColor = 'hsl(' + hsl[0] + 'deg,' + hsl[1] + '%, ' + hsl[2] + '%)'; 1392 | 1393 | event.preventDefault(); 1394 | } 1395 | 1396 | function mouseup() { 1397 | window.removeEventListener('mousemove', mousemove); 1398 | window.removeEventListener('mouseup', mouseup); 1399 | } 1400 | 1401 | window.addEventListener('mousemove', mousemove); 1402 | window.addEventListener('mouseup', mouseup); 1403 | } 1404 | }, 1405 | 1406 | cursor: { 1407 | component: 'div', 1408 | class: 'satus-color-picker__cursor', 1409 | style: { 1410 | 'left': s * 100 + '%', 1411 | 'top': 100 - v * 100 + '%' 1412 | } 1413 | } 1414 | }, 1415 | section: { 1416 | component: 'section', 1417 | variant: 'color', 1418 | 1419 | color: { 1420 | component: 'div', 1421 | class: 'satus-color-picker__color', 1422 | style: { 1423 | 'backgroundColor': 'rgb(' + this.color.value.join(',') + ')' 1424 | } 1425 | }, 1426 | hue: { 1427 | component: 'slider', 1428 | class: 'satus-color-picker__hue', 1429 | storage: false, 1430 | value: hsl[0], 1431 | max: 360, 1432 | on: { 1433 | input: function () { 1434 | var modal = this.skeleton.parentSkeleton.parentSkeleton, 1435 | hsl = modal.value; 1436 | 1437 | hsl[0] = this.storage.value; 1438 | 1439 | this.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg,' + hsl[1] + '%, ' + hsl[2] + '%)'; 1440 | this.parentNode.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg, 100%, 50%)'; 1441 | } 1442 | } 1443 | } 1444 | }, 1445 | actions: { 1446 | component: 'section', 1447 | variant: 'actions', 1448 | 1449 | reset: { 1450 | component: 'button', 1451 | text: 'reset', 1452 | on: { 1453 | click: function () { 1454 | var modal = this.skeleton.parentSkeleton.parentSkeleton, 1455 | component = modal.parentElement; 1456 | 1457 | component.color.value = component.skeleton.value || [0, 0, 0]; 1458 | 1459 | modal.rendered.close(); 1460 | } 1461 | } 1462 | }, 1463 | cancel: { 1464 | component: 'button', 1465 | text: 'cancel', 1466 | on: { 1467 | click: function () { 1468 | this.skeleton.parentSkeleton.parentSkeleton.rendered.close(); 1469 | } 1470 | } 1471 | }, 1472 | ok: { 1473 | component: 'button', 1474 | text: 'OK', 1475 | on: { 1476 | click: function () { 1477 | var modal = this.skeleton.parentSkeleton.parentSkeleton, 1478 | component = modal.parentElement; 1479 | 1480 | component.color.value = satus.color.hslToRgb(modal.value); 1481 | 1482 | modal.rendered.close(); 1483 | } 1484 | } 1485 | } 1486 | } 1487 | }, this.baseProvider.layers[0]); 1488 | }); 1489 | }; 1490 | /*-------------------------------------------------------------- 1491 | >>> RADIO 1492 | --------------------------------------------------------------*/ 1493 | 1494 | satus.components.radio = function (component, skeleton) { 1495 | var content = document.createElement('span'), 1496 | radio = document.createElement('input'); 1497 | 1498 | component.inner = content; 1499 | 1500 | radio.type = 'radio'; 1501 | 1502 | if (skeleton.group) { 1503 | component.storage.key = skeleton.group; 1504 | radio.name = skeleton.group; 1505 | } 1506 | 1507 | if (skeleton.value) { 1508 | radio.value = skeleton.value; 1509 | } 1510 | 1511 | component.addEventListener('render', function () { 1512 | this.storage.value = satus.storage.get(this.storage.key); 1513 | 1514 | if (satus.isset(this.storage.value)) { 1515 | radio.checked = this.storage.value === skeleton.value; 1516 | } else if (skeleton.checked) { 1517 | radio.checked = true; 1518 | } 1519 | }); 1520 | 1521 | radio.addEventListener('change', function () { 1522 | this.parentNode.storage.value = this.value; 1523 | }); 1524 | 1525 | component.appendChild(content); 1526 | component.appendChild(radio); 1527 | }; 1528 | /*-------------------------------------------------------------- 1529 | >>> SLIDER 1530 | --------------------------------------------------------------*/ 1531 | 1532 | satus.components.slider = function (component, skeleton) { 1533 | var label = component.createChildElement('div', 'label'); 1534 | 1535 | component.min = skeleton.min || 0; 1536 | component.max = skeleton.max || 1; 1537 | component.step = skeleton.step || 1; 1538 | component.percentageStep = 100 / ((component.max - component.min) / component.step); 1539 | component.precision = String(component.step).replace(/[0-9]./, '').length; 1540 | component.inner = label.createChildElement('div', 'inner'); 1541 | component.track = component.createChildElement('div', 'track'); 1542 | component.track.tabIndex = 0; 1543 | component.slice = component.track.createChildElement('div', 'slice'); 1544 | 1545 | component.valueElement = satus.render({ 1546 | component: 'input', 1547 | type: 'number', 1548 | properties: { 1549 | min: component.min, 1550 | max: component.max, 1551 | step: component.step 1552 | }, 1553 | on: { 1554 | input: function () { 1555 | var component = this.parentNode.parentNode; 1556 | 1557 | component.storage.value = Math.min(component.max, Math.max(this.value, component.min)); 1558 | 1559 | component.update(); 1560 | } 1561 | } 1562 | }, label); 1563 | 1564 | if (satus.isset(skeleton.value)) { 1565 | component.storage.value = skeleton.value; 1566 | } else { 1567 | component.storage.value = (component.max < component.min) ? component.min : component.min + (component.max - component.min) / 2; 1568 | } 1569 | 1570 | component.update = function () { 1571 | this.dataset.value = this.value; 1572 | 1573 | this.valueElement.value = this.value; 1574 | 1575 | this.slice.style.width = (this.value - this.min) / ((this.max - this.min) / 100) + '%'; 1576 | }; 1577 | 1578 | component.move = function (event) { 1579 | var track = this.track.getBoundingClientRect(), 1580 | x = Math.min(track.width, Math.max(event.clientX - track.left, 0)); 1581 | 1582 | this.value = x / track.width * 100 / this.percentageStep * this.step + this.min; 1583 | this.value = Math.round(this.value / this.step) * this.step; 1584 | this.value = Number(this.value.toFixed(this.precision)); 1585 | 1586 | this.storage.value = this.value; 1587 | 1588 | this.update(); 1589 | }; 1590 | 1591 | component.track.addEventListener('keydown', function (event) { 1592 | if (event.keyCode === 37) { 1593 | this.value -= this.step; 1594 | } else if (event.keyCode === 39) { 1595 | this.value += this.step; 1596 | } 1597 | 1598 | this.value = Math.min(this.max, Math.max(this.value, this.min)); 1599 | 1600 | this.storage.value = this.value; 1601 | 1602 | this.update(); 1603 | }); 1604 | 1605 | component.track.addEventListener('mousedown', function (event) { 1606 | if (event.button === 0) { 1607 | var component = this.parentNode; 1608 | 1609 | component.move(event); 1610 | 1611 | function mousemove(event) { 1612 | event.preventDefault(); 1613 | event.stopPropagation(); 1614 | 1615 | component.move(event); 1616 | 1617 | return false; 1618 | } 1619 | 1620 | function mouseup() { 1621 | window.removeEventListener('mousemove', mousemove); 1622 | window.removeEventListener('mouseup', mouseup); 1623 | } 1624 | 1625 | window.addEventListener('mousemove', mousemove); 1626 | window.addEventListener('mouseup', mouseup); 1627 | } 1628 | }); 1629 | 1630 | component.update(); 1631 | }; 1632 | 1633 | 1634 | satus.components.slider = function (component, skeleton) { 1635 | var track = component.createChildElement('div', 'track'), 1636 | range = track.createChildElement('input', 'range'); 1637 | 1638 | range.type = 'range'; 1639 | range.min = skeleton.min || 0; 1640 | range.max = skeleton.max || 1; 1641 | range.step = skeleton.step || 1; 1642 | range.value = component.storage.value || skeleton.value; 1643 | 1644 | range.addEventListener('input', function () { 1645 | component.storage.value = Number(this.value); 1646 | }); 1647 | 1648 | if (skeleton.on) { 1649 | for (var type in skeleton.on) { 1650 | range.addEventListener(type, function (event) { 1651 | this.parentNode.dispatchEvent(new Event(event.type)); 1652 | }); 1653 | } 1654 | } 1655 | }; 1656 | /*-------------------------------------------------------------- 1657 | >>> SHORTCUT 1658 | --------------------------------------------------------------*/ 1659 | 1660 | satus.components.shortcut = function (component, skeleton) { 1661 | var content = document.createElement('span'), 1662 | value = document.createElement('div'); 1663 | 1664 | component.inner = content; 1665 | 1666 | component.className = 'satus-button'; 1667 | value.className = 'satus-shortcut__value'; 1668 | 1669 | component.render = function (parent) { 1670 | var self = this, 1671 | parent = parent || self.primary, 1672 | children = parent.children; 1673 | 1674 | satus.empty(parent); 1675 | 1676 | function createElement(name) { 1677 | var element = document.createElement('div'); 1678 | 1679 | element.className = 'satus-shortcut__' + name; 1680 | 1681 | parent.appendChild(element); 1682 | 1683 | return element; 1684 | } 1685 | 1686 | if (this.data.alt) { 1687 | createElement('key').textContent = 'Alt'; 1688 | } 1689 | 1690 | if (this.data.ctrl) { 1691 | if (children.length && children[children.length - 1].className.indexOf('plus') === -1) { 1692 | createElement('plus'); 1693 | } 1694 | 1695 | createElement('key').textContent = 'Ctrl'; 1696 | } 1697 | 1698 | if (this.data.shift) { 1699 | if (children.length && children[children.length - 1].className.indexOf('plus') === -1) { 1700 | createElement('plus'); 1701 | } 1702 | 1703 | createElement('key').textContent = 'Shift'; 1704 | } 1705 | 1706 | for (var code in this.data.keys) { 1707 | var key = this.data.keys[code].key, 1708 | arrows = ['ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft'], 1709 | index = arrows.indexOf(key); 1710 | 1711 | if (children.length && children[children.length - 1].className.indexOf('plus') === -1) { 1712 | createElement('plus'); 1713 | } 1714 | 1715 | if (index !== -1) { 1716 | createElement('key').textContent = ['↑', '→', '↓', '←'][index]; 1717 | } else if (key === ' ') { 1718 | createElement('key').textContent = '␣'; 1719 | } else if (key) { 1720 | createElement('key').textContent = key.toUpperCase(); 1721 | } 1722 | } 1723 | 1724 | if (this.data.wheel) { 1725 | if (children.length && children[children.length - 1].className.indexOf('plus') === -1) { 1726 | createElement('plus'); 1727 | } 1728 | 1729 | var mouse = createElement('mouse'), 1730 | div = document.createElement('div'); 1731 | 1732 | mouse.appendChild(div); 1733 | 1734 | mouse.className += ' ' + (this.data.wheel > 0); 1735 | } 1736 | 1737 | if (this.data.click) { 1738 | if (children.length && children[children.length - 1].className.indexOf('plus') === -1) { 1739 | createElement('plus'); 1740 | } 1741 | 1742 | var mouse = createElement('mouse'), 1743 | div = document.createElement('div'); 1744 | 1745 | mouse.appendChild(div); 1746 | 1747 | mouse.className += ' click'; 1748 | } 1749 | 1750 | if (this.data.middle) { 1751 | if (children.length && children[children.length - 1].className.indexOf('plus') === -1) { 1752 | createElement('plus'); 1753 | } 1754 | 1755 | var mouse = createElement('mouse'), 1756 | div = document.createElement('div'); 1757 | 1758 | mouse.appendChild(div); 1759 | 1760 | mouse.className += ' middle'; 1761 | } 1762 | 1763 | if (this.data.context) { 1764 | if (children.length && children[children.length - 1].className.indexOf('plus') === -1) { 1765 | createElement('plus'); 1766 | } 1767 | 1768 | var mouse = createElement('mouse'), 1769 | div = document.createElement('div'); 1770 | 1771 | mouse.appendChild(div); 1772 | 1773 | mouse.className += ' context'; 1774 | } 1775 | }; 1776 | 1777 | component.valueElement = value; 1778 | 1779 | component.appendChild(content); 1780 | component.appendChild(value); 1781 | 1782 | component.keydown = function (event) { 1783 | event.preventDefault(); 1784 | event.stopPropagation(); 1785 | 1786 | component.data = { 1787 | alt: event.altKey, 1788 | ctrl: event.ctrlKey, 1789 | shift: event.shiftKey, 1790 | keys: {} 1791 | }; 1792 | 1793 | if (['control', 'alt', 'altgraph', 'shift'].indexOf(event.key.toLowerCase()) === -1) { 1794 | component.data.keys[event.keyCode] = { 1795 | code: event.code, 1796 | key: event.key 1797 | }; 1798 | } 1799 | 1800 | component.data.wheel = 0; 1801 | 1802 | component.render(); 1803 | 1804 | return false; 1805 | }; 1806 | 1807 | if (skeleton.wheel !== false) { 1808 | component.mousewheel = function (event) { 1809 | event.stopPropagation(); 1810 | 1811 | if ( 1812 | ( 1813 | component.data.wheel === 0 && 1814 | ( 1815 | Object.keys(component.data.keys).length === 0 && 1816 | component.data.alt === false && 1817 | component.data.ctrl === false && 1818 | component.data.shift === false 1819 | ) 1820 | ) || 1821 | component.data.wheel < 0 && event.deltaY > 0 || 1822 | component.data.wheel > 0 && event.deltaY < 0) { 1823 | component.data = { 1824 | alt: false, 1825 | ctrl: false, 1826 | shift: false, 1827 | keys: {} 1828 | }; 1829 | } 1830 | 1831 | component.data.wheel = event.deltaY < 0 ? -1 : 1; 1832 | 1833 | component.render(); 1834 | 1835 | return false; 1836 | }; 1837 | } 1838 | 1839 | component.addEventListener('click', function () { 1840 | satus.render({ 1841 | component: 'modal', 1842 | properties: { 1843 | parent: this 1844 | }, 1845 | on: { 1846 | close: function () { 1847 | window.removeEventListener('keydown', component.keydown); 1848 | window.removeEventListener('wheel', component.mousewheel); 1849 | } 1850 | }, 1851 | 1852 | primary: { 1853 | component: 'div', 1854 | class: 'satus-shortcut__primary', 1855 | on: { 1856 | render: function () { 1857 | component.primary = this; 1858 | 1859 | if (component.skeleton.mouseButtons === true) { 1860 | this.addEventListener('mousedown', function (event) { 1861 | if ( 1862 | component.data.click && event.button === 0 || 1863 | component.data.middle && event.button === 1 1864 | ) { 1865 | component.data = { 1866 | alt: false, 1867 | ctrl: false, 1868 | shift: false, 1869 | keys: {} 1870 | }; 1871 | } 1872 | 1873 | component.data.click = false; 1874 | component.data.middle = false; 1875 | component.data.context = false; 1876 | 1877 | if (event.button === 0) { 1878 | component.data.click = true; 1879 | 1880 | component.render(); 1881 | } else if (event.button === 1) { 1882 | component.data.middle = true; 1883 | 1884 | component.render(); 1885 | } 1886 | }); 1887 | 1888 | this.addEventListener('contextmenu', function (event) { 1889 | event.preventDefault(); 1890 | event.stopPropagation(); 1891 | 1892 | if (component.data.context) { 1893 | component.data = { 1894 | alt: false, 1895 | ctrl: false, 1896 | shift: false, 1897 | keys: {} 1898 | }; 1899 | } 1900 | 1901 | component.data.context = true; 1902 | component.data.middle = false; 1903 | component.data.click = false; 1904 | 1905 | component.render(); 1906 | 1907 | return false; 1908 | }); 1909 | } 1910 | 1911 | component.render(); 1912 | } 1913 | } 1914 | }, 1915 | actions: { 1916 | component: 'section', 1917 | variant: 'actions', 1918 | 1919 | reset: { 1920 | component: 'button', 1921 | text: 'reset', 1922 | on: { 1923 | click: function () { 1924 | var component = this.parentNode.parentNode.parentNode.parent; 1925 | 1926 | component.data = component.skeleton.value || {}; 1927 | 1928 | component.render(component.valueElement); 1929 | 1930 | satus.storage.remove(component.storage); 1931 | 1932 | this.parentNode.parentNode.parentNode.close(); 1933 | 1934 | window.removeEventListener('keydown', component.keydown); 1935 | window.removeEventListener('wheel', component.mousewheel); 1936 | } 1937 | } 1938 | }, 1939 | cancel: { 1940 | component: 'button', 1941 | text: 'cancel', 1942 | on: { 1943 | click: function () { 1944 | component.data = satus.storage.get(component.storage) || component.skeleton.value || {}; 1945 | 1946 | component.render(component.valueElement); 1947 | 1948 | this.parentNode.parentNode.parentNode.close(); 1949 | 1950 | window.removeEventListener('keydown', component.keydown); 1951 | window.removeEventListener('wheel', component.mousewheel); 1952 | } 1953 | } 1954 | }, 1955 | save: { 1956 | component: 'button', 1957 | text: 'save', 1958 | on: { 1959 | click: function () { 1960 | component.storage.value = component.data; 1961 | 1962 | component.render(component.valueElement); 1963 | 1964 | this.parentNode.parentNode.parentNode.close(); 1965 | 1966 | window.removeEventListener('keydown', component.keydown); 1967 | window.removeEventListener('wheel', component.mousewheel); 1968 | } 1969 | } 1970 | } 1971 | } 1972 | }, this.baseProvider); 1973 | 1974 | window.addEventListener('keydown', this.keydown); 1975 | window.addEventListener('wheel', this.mousewheel); 1976 | }); 1977 | 1978 | component.data = component.storage.value || { 1979 | alt: false, 1980 | ctrl: false, 1981 | shift: false, 1982 | keys: {}, 1983 | wheel: 0 1984 | }; 1985 | 1986 | component.render(component.valueElement); 1987 | }; 1988 | /*-------------------------------------------------------------- 1989 | >>> CHECKBOX 1990 | --------------------------------------------------------------*/ 1991 | 1992 | satus.components.checkbox = function (component, skeleton) { 1993 | var content = component.add('span', 'checkbox__content'); 1994 | 1995 | component.inner = content; 1996 | 1997 | component.addEventListener('click', function () { 1998 | if (this.dataset.value === 'true') { 1999 | this.storage.value = false; 2000 | this.dataset.value = 'false'; 2001 | } else { 2002 | this.storage.value = true; 2003 | this.dataset.value = 'true'; 2004 | } 2005 | }); 2006 | 2007 | component.addEventListener('render', function () { 2008 | this.dataset.value = this.storage.value; 2009 | }); 2010 | }; 2011 | /*-------------------------------------------------------------- 2012 | >>> SWITCH 2013 | --------------------------------------------------------------*/ 2014 | 2015 | satus.components.switch = function (component, skeleton) { 2016 | var component_thumb = document.createElement('i'); 2017 | 2018 | component.addEventListener('click', function () { 2019 | if (this.dataset.value === 'true') { 2020 | this.storage.value = false; 2021 | this.dataset.value = 'false'; 2022 | } else { 2023 | this.storage.value = true; 2024 | this.dataset.value = 'true'; 2025 | } 2026 | }, true); 2027 | 2028 | component.addEventListener('render', function () { 2029 | this.dataset.value = this.storage.value; 2030 | }); 2031 | 2032 | component.appendChild(component_thumb); 2033 | }; 2034 | /*-------------------------------------------------------------- 2035 | >>> COLOR: 2036 | ---------------------------------------------------------------- 2037 | # RGB to HSL 2038 | # HUE to RGB 2039 | # HSL to RGB 2040 | --------------------------------------------------------------*/ 2041 | 2042 | satus.color = {}; 2043 | 2044 | 2045 | /*-------------------------------------------------------------- 2046 | # RGB TO HSL 2047 | --------------------------------------------------------------*/ 2048 | 2049 | satus.color.rgbToHsl = function (array) { 2050 | var r = array[0] / 255, 2051 | g = array[1] / 255, 2052 | b = array[2] / 255, 2053 | min = Math.min(r, g, b), 2054 | max = Math.max(r, g, b), 2055 | h = 0, 2056 | s = 0, 2057 | l = (min + max) / 2; 2058 | 2059 | if (min === max) { 2060 | h = 0; 2061 | s = 0; 2062 | } else { 2063 | var delta = max - min; 2064 | 2065 | s = l <= 0.5 ? delta / (max + min) : delta / (2 - max - min); 2066 | 2067 | if (max === r) { 2068 | h = (g - b) / delta + (g < b ? 6 : 0); 2069 | } else if (max === g) { 2070 | h = (b - r) / delta + 2; 2071 | } else if (max === b) { 2072 | h = (r - g) / delta + 4; 2073 | } 2074 | 2075 | h /= 6; 2076 | } 2077 | 2078 | h *= 360; 2079 | s *= 100; 2080 | l *= 100; 2081 | 2082 | if (array.length === 3) { 2083 | return [h, s, l]; 2084 | } else { 2085 | return [h, s, l, array[3]]; 2086 | } 2087 | }; 2088 | 2089 | 2090 | /*-------------------------------------------------------------- 2091 | # HUE TO RGB 2092 | --------------------------------------------------------------*/ 2093 | 2094 | satus.color.hueToRgb = function (array) { 2095 | var t1 = array[0], 2096 | t2 = array[1], 2097 | hue = array[2]; 2098 | 2099 | if (hue < 0) { 2100 | hue += 6; 2101 | } 2102 | 2103 | if (hue >= 6) { 2104 | hue -= 6; 2105 | } 2106 | 2107 | if (hue < 1) { 2108 | return (t2 - t1) * hue + t1; 2109 | } else if (hue < 3) { 2110 | return t2; 2111 | } else if (hue < 4) { 2112 | return (t2 - t1) * (4 - hue) + t1; 2113 | } else { 2114 | return t1; 2115 | } 2116 | }; 2117 | 2118 | 2119 | /*-------------------------------------------------------------- 2120 | # HSL TO RGB 2121 | --------------------------------------------------------------*/ 2122 | 2123 | satus.color.hslToRgb = function (array) { 2124 | var h = array[0] / 360, 2125 | s = array[1] / 100, 2126 | l = array[2] / 100, 2127 | r, g, b; 2128 | 2129 | if (s == 0) { 2130 | r = g = b = l; 2131 | } else { 2132 | var hue2rgb = function hue2rgb(p, q, t) { 2133 | if (t < 0) t += 1; 2134 | if (t > 1) t -= 1; 2135 | if (t < 1 / 6) return p + (q - p) * 6 * t; 2136 | if (t < 1 / 2) return q; 2137 | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; 2138 | return p; 2139 | } 2140 | 2141 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 2142 | var p = 2 * l - q; 2143 | r = hue2rgb(p, q, h + 1 / 3); 2144 | g = hue2rgb(p, q, h); 2145 | b = hue2rgb(p, q, h - 1 / 3); 2146 | } 2147 | 2148 | return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; 2149 | }; 2150 | /*-------------------------------------------------------------- 2151 | >>> USER 2152 | --------------------------------------------------------------*/ 2153 | 2154 | satus.user = function () { 2155 | /*-------------------------------------------------------------- 2156 | 1.0 VARIABLES 2157 | --------------------------------------------------------------*/ 2158 | 2159 | var user_agent = navigator.userAgent, 2160 | random_cookie = 'ta{t`nX6cMXK,Wsc', 2161 | video = document.createElement('video'), 2162 | video_formats = { 2163 | ogg: 'video/ogg; codecs="theora"', 2164 | h264: 'video/mp4; codecs="avc1.42E01E"', 2165 | webm: 'video/webm; codecs="vp8, vorbis"', 2166 | vp9: 'video/webm; codecs="vp9"', 2167 | hls: 'application/x-mpegURL; codecs="avc1.42E01E"' 2168 | }, 2169 | audio = document.createElement('audio'), 2170 | audio_formats = { 2171 | mp3: 'audio/mpeg', 2172 | mp4: 'audio/mp4', 2173 | aif: 'audio/x-aiff' 2174 | }, 2175 | cvs = document.createElement('canvas'), 2176 | ctx = cvs.getContext('webgl'), 2177 | data = { 2178 | browser: { 2179 | audio: null, 2180 | cookies: null, 2181 | flash: null, 2182 | java: null, 2183 | languages: null, 2184 | name: null, 2185 | platform: null, 2186 | version: null, 2187 | video: null, 2188 | webgl: null 2189 | }, 2190 | os: { 2191 | name: null, 2192 | type: null 2193 | }, 2194 | device: { 2195 | connection: { 2196 | type: null, 2197 | speed: null 2198 | }, 2199 | cores: null, 2200 | gpu: null, 2201 | max_touch_points: null, 2202 | ram: null, 2203 | screen: null, 2204 | touch: null 2205 | } 2206 | }; 2207 | 2208 | 2209 | /*-------------------------------------------------------------- 2210 | 2.0 SOFTWARE 2211 | --------------------------------------------------------------*/ 2212 | 2213 | /*-------------------------------------------------------------- 2214 | 2.1.0 OS 2215 | --------------------------------------------------------------*/ 2216 | 2217 | /*-------------------------------------------------------------- 2218 | 2.1.1 NAME 2219 | --------------------------------------------------------------*/ 2220 | 2221 | if (navigator.appVersion.indexOf('Win') !== -1) { 2222 | if (navigator.appVersion.match(/(Windows 10.0|Windows NT 10.0)/)) { 2223 | data.os.name = 'Windows 10'; 2224 | } else if (navigator.appVersion.match(/(Windows 8.1|Windows NT 6.3)/)) { 2225 | data.os.name = 'Windows 8.1'; 2226 | } else if (navigator.appVersion.match(/(Windows 8|Windows NT 6.2)/)) { 2227 | data.os.name = 'Windows 8'; 2228 | } else if (navigator.appVersion.match(/(Windows 7|Windows NT 6.1)/)) { 2229 | data.os.name = 'Windows 7'; 2230 | } else if (navigator.appVersion.match(/(Windows NT 6.0)/)) { 2231 | data.os.name = 'Windows Vista'; 2232 | } else if (navigator.appVersion.match(/(Windows NT 5.1|Windows XP)/)) { 2233 | data.os.name = 'Windows XP'; 2234 | } else { 2235 | data.os.name = 'Windows'; 2236 | } 2237 | } else if (navigator.appVersion.indexOf('(iPhone|iPad|iPod)') !== -1) { 2238 | data.os.name = 'iOS'; 2239 | } else if (navigator.appVersion.indexOf('Mac') !== -1) { 2240 | data.os.name = 'macOS'; 2241 | } else if (navigator.appVersion.indexOf('Android') !== -1) { 2242 | data.os.name = 'Android'; 2243 | } else if (navigator.appVersion.indexOf('OpenBSD') !== -1) { 2244 | data.os.name = 'OpenBSD'; 2245 | } else if (navigator.appVersion.indexOf('SunOS') !== -1) { 2246 | data.os.name = 'SunOS'; 2247 | } else if (navigator.appVersion.indexOf('Linux') !== -1) { 2248 | data.os.name = 'Linux'; 2249 | } else if (navigator.appVersion.indexOf('X11') !== -1) { 2250 | data.os.name = 'UNIX'; 2251 | } 2252 | 2253 | /*-------------------------------------------------------------- 2254 | 2.1.2 TYPE 2255 | --------------------------------------------------------------*/ 2256 | 2257 | if (navigator.appVersion.match(/(Win64|x64|x86_64|WOW64)/)) { 2258 | data.os.type = '64-bit'; 2259 | } else { 2260 | data.os.type = '32-bit'; 2261 | } 2262 | 2263 | 2264 | /*-------------------------------------------------------------- 2265 | 2.2.0 BROWSER 2266 | --------------------------------------------------------------*/ 2267 | 2268 | /*-------------------------------------------------------------- 2269 | 2.2.1 NAME 2270 | --------------------------------------------------------------*/ 2271 | 2272 | if (user_agent.indexOf('Opera') !== -1) { 2273 | data.browser.name = 'Opera'; 2274 | } else if (user_agent.indexOf('Vivaldi') !== -1) { 2275 | data.browser.name = 'Vivaldi'; 2276 | } else if (user_agent.indexOf('Edge') !== -1) { 2277 | data.browser.name = 'Edge'; 2278 | } else if (user_agent.indexOf('Chrome') !== -1) { 2279 | data.browser.name = 'Chrome'; 2280 | } else if (user_agent.indexOf('Safari') !== -1) { 2281 | data.browser.name = 'Safari'; 2282 | } else if (user_agent.indexOf('Firefox') !== -1) { 2283 | data.browser.name = 'Firefox'; 2284 | } else if (user_agent.indexOf('MSIE') !== -1) { 2285 | data.browser.name = 'IE'; 2286 | } 2287 | 2288 | 2289 | /*-------------------------------------------------------------- 2290 | 2.2.2 VERSION 2291 | --------------------------------------------------------------*/ 2292 | 2293 | var browser_version = user_agent.match(new RegExp(data.browser.name + '/([0-9.]+)')); 2294 | 2295 | if (browser_version[1]) { 2296 | data.browser.version = browser_version[1]; 2297 | } 2298 | 2299 | 2300 | /*-------------------------------------------------------------- 2301 | 2.2.3 PLATFORM 2302 | --------------------------------------------------------------*/ 2303 | 2304 | data.browser.platform = navigator.platform || null; 2305 | 2306 | 2307 | /*-------------------------------------------------------------- 2308 | 2.2.4 LANGUAGES 2309 | --------------------------------------------------------------*/ 2310 | 2311 | data.browser.languages = navigator.languages || null; 2312 | 2313 | 2314 | /*-------------------------------------------------------------- 2315 | 2.2.5 COOKIES 2316 | --------------------------------------------------------------*/ 2317 | 2318 | if (document.cookie) { 2319 | document.cookie = random_cookie; 2320 | 2321 | if (document.cookie.indexOf(random_cookie) !== -1) { 2322 | data.browser.cookies = true; 2323 | } 2324 | } 2325 | 2326 | 2327 | /*-------------------------------------------------------------- 2328 | 2.2.6 FLASH 2329 | --------------------------------------------------------------*/ 2330 | 2331 | try { 2332 | if (new ActiveXObject('ShockwaveFlash.ShockwaveFlash')) { 2333 | data.browser.flash = true; 2334 | } 2335 | } catch (e) { 2336 | if (navigator.mimeTypes['application/x-shockwave-flash']) { 2337 | data.browser.flash = true; 2338 | } 2339 | } 2340 | 2341 | 2342 | /*-------------------------------------------------------------- 2343 | 2.2.7 JAVA 2344 | --------------------------------------------------------------*/ 2345 | 2346 | if (typeof navigator.javaEnabled === 'function' && navigator.javaEnabled()) { 2347 | data.browser.java = true; 2348 | } 2349 | 2350 | 2351 | /*-------------------------------------------------------------- 2352 | 2.2.8 VIDEO FORMATS 2353 | --------------------------------------------------------------*/ 2354 | 2355 | if (typeof video.canPlayType === 'function') { 2356 | data.browser.video = {}; 2357 | 2358 | for (var i in video_formats) { 2359 | var can_play_type = video.canPlayType(video_formats[i]); 2360 | 2361 | if (can_play_type === '') { 2362 | data.browser.video[i] = false; 2363 | } else { 2364 | data.browser.video[i] = can_play_type; 2365 | } 2366 | } 2367 | } 2368 | 2369 | 2370 | /*-------------------------------------------------------------- 2371 | 2.2.9 AUDIO FORMATS 2372 | --------------------------------------------------------------*/ 2373 | 2374 | if (typeof audio.canPlayType === 'function') { 2375 | data.browser.audio = {}; 2376 | 2377 | for (var i in audio_formats) { 2378 | var can_play_type = audio.canPlayType(audio_formats[i]); 2379 | 2380 | if (can_play_type == '') { 2381 | data.browser.audio[i] = false; 2382 | } else { 2383 | data.browser.audio[i] = can_play_type; 2384 | } 2385 | } 2386 | } 2387 | 2388 | 2389 | /*-------------------------------------------------------------- 2390 | 2.2.10 WEBGL 2391 | --------------------------------------------------------------*/ 2392 | 2393 | if (ctx && ctx instanceof WebGLRenderingContext) { 2394 | data.browser.webgl = true; 2395 | } 2396 | 2397 | 2398 | /*-------------------------------------------------------------- 2399 | 3.0 HARDWARE 2400 | --------------------------------------------------------------*/ 2401 | 2402 | /*-------------------------------------------------------------- 2403 | 3.1 SCREEN 2404 | --------------------------------------------------------------*/ 2405 | 2406 | if (screen) { 2407 | data.device.screen = screen.width + 'x' + screen.height; 2408 | } 2409 | 2410 | 2411 | /*-------------------------------------------------------------- 2412 | 3.2 RAM 2413 | --------------------------------------------------------------*/ 2414 | 2415 | if ('deviceMemory' in navigator) { 2416 | data.device.ram = navigator.deviceMemory + ' GB'; 2417 | } 2418 | 2419 | 2420 | /*-------------------------------------------------------------- 2421 | 3.3 GPU 2422 | --------------------------------------------------------------*/ 2423 | 2424 | if ( 2425 | ctx && 2426 | ctx instanceof WebGLRenderingContext && 2427 | 'getParameter' in ctx && 2428 | 'getExtension' in ctx 2429 | ) { 2430 | var info = ctx.getExtension('WEBGL_debug_renderer_info'); 2431 | 2432 | if (info) { 2433 | data.device.gpu = ctx.getParameter(info.UNMASKED_RENDERER_WEBGL); 2434 | } 2435 | } 2436 | 2437 | 2438 | /*-------------------------------------------------------------- 2439 | 3.4 CORES 2440 | --------------------------------------------------------------*/ 2441 | 2442 | if (navigator.hardwareConcurrency) { 2443 | data.device.cores = navigator.hardwareConcurrency; 2444 | } 2445 | 2446 | 2447 | /*-------------------------------------------------------------- 2448 | 3.5 TOUCH 2449 | --------------------------------------------------------------*/ 2450 | 2451 | if ( 2452 | window.hasOwnProperty('ontouchstart') || 2453 | window.DocumentTouch && document instanceof window.DocumentTouch || 2454 | navigator.maxTouchPoints > 0 || 2455 | window.navigator.msMaxTouchPoints > 0 2456 | ) { 2457 | data.device.touch = true; 2458 | data.device.max_touch_points = navigator.maxTouchPoints; 2459 | } 2460 | 2461 | 2462 | /*-------------------------------------------------------------- 2463 | 3.6 CONNECTION 2464 | --------------------------------------------------------------*/ 2465 | 2466 | if (typeof navigator.connection === 'object') { 2467 | data.device.connection.type = navigator.connection.effectiveType || null; 2468 | 2469 | if (navigator.connection.downlink) { 2470 | data.device.connection.speed = navigator.connection.downlink + ' Mbps'; 2471 | } 2472 | } 2473 | 2474 | 2475 | /*-------------------------------------------------------------- 2476 | 4.0 CLEARING 2477 | --------------------------------------------------------------*/ 2478 | 2479 | video.remove(); 2480 | audio.remove(); 2481 | cvs.remove(); 2482 | 2483 | 2484 | return data; 2485 | }; 2486 | /*-------------------------------------------------------------- 2487 | # SEARCH 2488 | --------------------------------------------------------------*/ 2489 | 2490 | satus.search = function (query, object, callback) { 2491 | var elements = ['switch', 'select', 'slider', 'shortcut', 'radio', 'color-picker'], 2492 | threads = 0, 2493 | results = {}, 2494 | excluded = [ 2495 | 'baseProvider', 2496 | 'childrenContainer', 2497 | 'parentElement', 2498 | 'parentObject', 2499 | 'parentSkeleton', 2500 | 'rendered', 2501 | 'namespaceURI' 2502 | ]; 2503 | 2504 | query = query.toLowerCase(); 2505 | 2506 | function parse(items, parent) { 2507 | threads++; 2508 | 2509 | for (var key in items) { 2510 | if (excluded.indexOf(key) === -1) { 2511 | var item = items[key]; 2512 | 2513 | if (item.component) { 2514 | //console.log(key, item.component); 2515 | 2516 | if (elements.indexOf(item.component) !== -1 && key.indexOf(query) !== -1) { 2517 | results[key] = Object.assign({}, item); 2518 | } 2519 | } 2520 | 2521 | if (typeof item === 'object') { 2522 | parse(item, items); 2523 | } 2524 | } 2525 | } 2526 | 2527 | threads--; 2528 | 2529 | if (threads === 0) { 2530 | callback(results); 2531 | } 2532 | } 2533 | 2534 | parse(object); 2535 | }; 2536 | --------------------------------------------------------------------------------