diff --git a/manifest.json b/manifest.json index 95c69b1..de17451 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "ToDo", "description": "__MSG_description_ext__", - "version": "3.0.0", + "version": "3.0.10", "default_locale": "en", "icons": { "16": "icons/16.png", diff --git a/ui/satus/satus.css b/ui/satus/satus.css index fc4fc67..99bc97b 100644 --- a/ui/satus/satus.css +++ b/ui/satus/satus.css @@ -1 +1,1472 @@ -body{--satus-primary:#f6b465;--satus-switch-background:rgba(0,0,0,.08);--satus-header-background:#fff;--satus-header-text:#848471;--satus-layers-background:#f6f6f4;--satus-layers-text:#848471;--satus-section-card-background:#fff;--satus-modal-background:#f6f6f4;--satus-modal-text:#848471;--satus-hover:rgba(0, 0, 0, .04);--satus-text-field--background:#e8e8e3;--satus-text-field--border:#d6d6cd;--satus-text-field--text:#848471;--satus-switch-track:#b8b8b8;--satus-switch-track--active:var(--satus-primary);--satus-switch-thumb:#fff;--satus-tabs:#fff;--satus-tooltip:rgba(0,0,0,.4);--satus-sortable-ghost:rgba(0, 0, 0, .80);--satus-sortable-background:#f9cf9f;--satus-sortable-text:#fff;--satus-divider:#e0e0e0;--satus-tabs-background:#e8e8e3;--satus-tabs-foreground:#fff;--satus-context-menu--border:#ccc;--satus-checkbox--background:rgb(90, 90, 73, .08);--satus-checkbox--border:rgb(90, 90, 73, .16);--satus-checkbox--mark:#fff}body[theme=dark]{--satus-primary:#90a5e0;--satus-switch-background:rgba(0,0,0,.08);--satus-header-background:#232b43;--satus-header-text:#bcc4dc;--satus-layers-background:#0e111b;--satus-layers-text:#bcc4dc;--satus-section-card-background:#1d2335;--satus-modal-background:#252e46;--satus-modal-text:#bcc4dc;--satus-hover:rgba(255, 255, 255, .08);--satus-text-field--background:#20273c;--satus-text-field--border:#2e3957;--satus-text-field--text:#c4c4d4;--satus-switch-track:#101219;--satus-switch-track--active:var(--satus-primary);--satus-switch-thumb:#d7dcea;--satus-tooltip:rgba(255,255,255,.4);--satus-sortable-ghost:rgba(255, 255, 255, .8);--satus-sortable-background:var(--satus-primary);--satus-sortable-text:#fff;--satus-divider:#3e4865;--satus-tabs-background:#1f2433;--satus-tabs-foreground:#344165;--satus-context-menu--border:#2e3957;--satus-checkbox--background:rgb(233, 234, 237, .08);--satus-checkbox--border:rgb(233, 234, 237, .16);--satus-checkbox--mark:#fff}body[theme=black]{--satus-primary:#8f8f8f;--satus-switch-background:rgba(255,255,255,.08);--satus-header-background:#1f1f1f;--satus-header-text:#ccc;--satus-layers-background:#000;--satus-layers-text:#ccc;--satus-section-card-background:#1a1a1a;--satus-modal-background:#212121;--satus-modal-text:#ccc;--satus-hover:rgba(255, 255, 255, .08);--satus-text-field--background:#333333;--satus-text-field--border:#525252;--satus-text-field--text:#ccc;--satus-switch-track:#111;--satus-switch-track--active:var(--satus-primary);--satus-switch-thumb:#ddd;--satus-tooltip:rgba(255,255,255,.4);--satus-sortable-ghost:rgba(255, 255, 255, .8);--satus-sortable-background:var(--satus-primary);--satus-sortable-text:#fff;--satus-divider:#444;--satus-tabs-background:#111;--satus-tabs-foreground:#2e2e2e;--satus-context-menu--border:#525252;--satus-checkbox--background:rgb(235, 235, 235, .08);--satus-checkbox--border:rgb(235, 235, 235, .16);--satus-checkbox--mark:#fff}.satus-color-picker{font-size:inherit;position:relative;display:flex;box-sizing:border-box;margin:0;cursor:pointer;color:inherit;border:none;outline:0;background-color:var(--satus-theme-button);justify-content:space-between;-webkit-tap-highlight-color:transparent;align-items:center;-webkit-appearance:none}.satus-color-picker__value{width:22px;height:22px;border:2px solid rgba(0,0,0,.16);border-radius:50%;background:#fff}.satus-modal--color-picker{position:relative}.satus-modal--color-picker .satus-modal__surface{padding-top:0}.satus-color-picker__palette{position:relative;overflow:hidden;width:100%;height:256px;background-color:red}.satus-color-picker__palette:before{position:absolute;top:0;left:0;width:100%;height:100%;content:'';background-image:linear-gradient(0deg,#000,transparent),linear-gradient(90deg,#fff,transparent)}.satus-color-picker__cursor{position:absolute;width:5px;height:5px;transform:translate(-50%,-50%);pointer-events:none;border:1px solid #fff;border-radius:50%;box-shadow:0 0 0 1px #000}.satus-modal--color-picker .satus-modal__surface .satus-section--color{margin:8px 16px 0;align-items:center}.satus-color-picker__color{width:32px;height:32px;margin:0 16px 0 0;border:2px solid rgba(0,0,0,.16);border-radius:50%;background:red}.satus-slider.satus-color-picker__hue{padding:0;flex:1}.satus-color-picker__hue .satus-slider__track{height:16px;border-radius:4px;background-image:linear-gradient(90deg,red,#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,red)}.satus-color-picker__hue .satus-slider__handle{width:16px;height:16px;background:#fff;box-shadow:0 0 4px rgb(0,0,0,.64)}.satus-color-picker__hue .satus-slider__handle:focus::after,.satus-color-picker__hue .satus-slider__track-fill,.satus-color-picker__hue::before{display:none}.satus-main{color:var(--satus-main-text);background:var(--satus-main-background);overflow-y:auto;box-sizing:border-box}.satus-tabs{display:flex;height:26px!important}.satus-tabs__content{position:relative;overflow:hidden;width:100%;height:100%;border:2px solid var(--satus-tabs-background);border-radius:4px;background:var(--satus-tabs-background)}.satus-tabs__selection{position:absolute;z-index:0;top:0;left:0;height:100%;transition:left .25s;border-radius:4px;background:var(--satus-tabs-foreground)}.satus-tabs__button{font:inherit;position:relative;z-index:1;overflow:hidden;height:100%;padding:0 4px;white-space:nowrap;text-overflow:ellipsis;color:inherit;border:none;background:0 0;flex:1}.satus-tabs__button:hover{cursor:pointer}.satus-menubar,.satus-menubar ul{margin:0;padding:0;list-style:none;background:var(--satus-menubar-background);color:var(--satus-menubar-text)}.satus-menubar li>.satus-button{height:32px;font-size:14px}.satus-menubar>li>ul{display:none;position:absolute}.satus-menubar>li>:focus+ul,.satus-menubar>li>ul:hover{display:block}.satus-button{font:inherit;position:relative;overflow:hidden;height:48px;margin:0;padding:8px;text-align:left;white-space:nowrap;text-overflow:ellipsis;color:var(--satus-button-text,inherit);border:none;background:var(--satus-button-background,transparent);appearance:none}.satus-button:hover{cursor:pointer;background-color:var(--satus-hover)}.satus-list{list-style:none;margin:0}.satus-list__item{display:flex;align-items:center;justify-content:space-between;min-height:48px}.satus-list__item>:last-child{text-align:right}.satus-section{display:flex;box-sizing:border-box;flex-wrap:wrap}.satus-section--align-start{align-items:center;justify-content:flex-start}.satus-section--align-end{align-items:center;justify-content:flex-end}.satus-section--space-between{align-items:center;justify-content:space-between}.satus-section--column{flex-direction:column}.satus-section--card{flex-direction:column;box-sizing:border-box;width:100%;max-width:900px;margin:8px auto;padding:8px 0;color:var(--satus-section-card-text);border:1px solid rgba(0,0,0,.1);border-radius:8px;background:var(--satus-section-card-background)}.satus-section--card>:not(.satus-button){box-sizing:border-box;width:100%;min-height:48px;padding:0 16px;text-align:left}.satus-section--card>.satus-radio,.satus-section--card>.satus-select,.satus-section--card>.satus-switch{display:flex;justify-content:space-between;align-items:center}.satus-section--card>.satus-button:hover,.satus-section--card>.satus-radio:hover,.satus-section--card>.satus-select:hover,.satus-section--card>.satus-slider:hover,.satus-section--card>.satus-switch:hover{background-color:var(--satus-hover)}.satus-section--card>.satus-button{padding:0 16px;width:100%}.satus-section--card>.satus-button>svg{width:20px;margin:2px 16px 0 0;color:var(--satus-primary)}::-webkit-scrollbar{width:4px}::-webkit-scrollbar:hover{width:8px}::-webkit-scrollbar-thumb{background:rgba(0,0,0,.3)}.satus-modal{position:absolute;z-index:100;top:0;left:0;display:flex;width:100%;height:100vh;justify-content:center;align-items:center}.satus-modal__scrim{position:absolute;top:0;left:0;width:100%;height:100%;animation:modalFadeIn 150ms linear forwards;opacity:0;background:rgba(0,0,0,.16);backdrop-filter:blur(8px)}.satus-modal__surface{display:flex;overflow-y:auto;flex-direction:column;width:95%;min-width:240px;max-width:560px;max-height:80%;margin:0 16px;padding:12px 16px;transform:scale(.8);animation:modalZoomIn 150ms linear forwards;animation-delay:20ms;opacity:0;color:var(--satus-modal-text);border-radius:6px;background-color:var(--satus-modal-background);box-shadow:inset 0 -1px 1px 1px rgb(0,0,0,.1),0 2px 6px rgb(0,0,0,.15)}.satus-modal__surface .satus-section--actions{display:flex;width:100%;margin:8px 0 0;padding:0;justify-content:flex-end;align-items:center}.satus-modal__surface .satus-section--actions .satus-button{font-weight:500;height:32px;margin-left:8px;padding:0 8px;letter-spacing:.5px;color:var(--satus-primary);border-radius:4px}.satus-modal--closing .satus-modal__scrim{animation:modalFadeOut 70ms linear forwards}.satus-modal--closing .satus-modal__surface{animation:modalZoomOut 70ms linear forwards}.satus-modal--vertical .satus-modal__surface{position:absolute;top:8px;right:8px;left:auto;min-width:200px;max-width:200px;margin:0;padding:8px 0;transform-origin:right top}.satus-modal--vertical .satus-modal__surface>.satus-button,.satus-modal--vertical .satus-modal__surface>.satus-select,.satus-modal--vertical .satus-modal__surface>.satus-switch{display:flex;height:36px;padding:0 16px;align-items:center}.satus-modal--vertical .satus-modal__surface>.satus-tabs{padding:0 12px}.satus-modal--vertical .satus-modal__surface>.satus-span{font-size:13px;font-weight:500;margin:6px 0;padding:0 12px}.satus-modal--vertical .satus-modal__surface>.satus-button:hover,.satus-modal--vertical .satus-modal__surface>.satus-select:hover,.satus-modal--vertical .satus-modal__surface>.satus-switch:hover{background-color:var(--satus-hover)}.satus-modal--vertical .satus-button svg{width:20px;height:18px;margin:0 14px 0 0;opacity:.75;flex:0 0 20px}.satus-modal--vertical .satus-button .satus-span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}@keyframes modalFadeIn{from{opacity:0}to{opacity:1}}@keyframes modalFadeOut{from{opacity:1}to{opacity:0}}@keyframes modalZoomIn{from{transform:scale(.8);opacity:0}to{transform:scale(1);opacity:1}}@keyframes modalZoomOut{from{transform:scale(1);opacity:1}to{transform:scale(.8);opacity:0}}.satus-checkbox{position:relative;font:inherit;display:flex;color:inherit;border:none;background:0 0;appearance:none;align-items:center;justify-content:flex-start}.satus-checkbox:hover{cursor:pointer;background-color:var(--satus-hover)}.satus-checkbox:focus{outline:0}.satus-checkbox__content{display:block;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.satus-checkbox::before{display:flex;min-width:16px;width:16px;height:16px;margin:0 12px 0 0;content:'';border:1px solid var(--satus-checkbox--border);border-radius:6px;background:var(--satus-checkbox--background);align-items:center;justify-content:center}.satus-checkbox[data-value=true]::before{background:var(--satus-primary)}.satus-checkbox[data-value=true]::after{position:absolute;top:20px;left:20px;width:8px;height:4px;content:'';transform:rotate(-45deg);border:2px solid var(--satus-checkbox--mark);border-top:none;border-right:none}.satus-switch{font:inherit;display:flex;transition:background-color 75ms;color:inherit;border:none;outline:0;background-color:transparent;justify-content:space-between;align-items:center}.satus-switch:hover{cursor:pointer}.satus-switch__content{display:flex;align-items:center}.satus-switch__content>svg{width:20px;height:18px;margin:0 14px 0 0;opacity:.75}.satus-switch>i{width:38px;height:20px;transition:background-color 150ms;border-radius:20px;background-color:var(--satus-switch-track);flex:0 0 38px}.satus-section--card .satus-switch>i{margin-left:16px}.satus-switch[data-value=true]>i{background-color:var(--satus-switch-track--active)}.satus-switch>i::before{display:block;width:16px;height:16px;margin:2px;content:'';transition:transform 150ms cubic-bezier(.4,0,.2,1);border-radius:50%;background-color:var(--satus-switch-thumb);will-change:transform}.satus-switch[data-value=true]>i::before{transform:translateX(18px)}.satus-slider{box-sizing:border-box;width:100%;padding:8px 16px 0;outline:0}.satus-slider__container{position:relative;height:32px;width:100%}.satus-slider__track{position:absolute;top:50%;left:0;height:2px;width:100%;transform:translateY(-50%)}.satus-slider__track::before{position:absolute;left:0;top:0;width:100%;height:100%;background:var(--satus-primary);opacity:.24;content:''}.satus-slider__handle:focus::after{content:attr(data-value);position:absolute;left:50%;top:-2px;background:var(--satus-primary);color:#fff;display:block;transform:translate(-50%,-100%);padding:1px 4px;border-radius:4px}.satus-slider__track-fill{position:absolute;left:0;top:0;height:100%;background:var(--satus-primary)}.satus-slider__handle{position:absolute;width:10px;height:10px;background:var(--satus-primary);border-radius:50%;transform:translate(-50%,-50%);top:50%;left:0}.satus-slider__handle::before{content:'';background:var(--satus-primary);position:absolute;left:50%;top:50%;width:1px;height:1px;opacity:0;border-radius:50%;transform:translate(-50%,-50%) scale(1);transition:.2s}.satus-slider:focus .satus-slider__handle::before{transform:translate(-50%,-50%) scale(26);opacity:.24}.satus-shortcut{justify-content:space-between}.satus-shortcut__value{text-transform:uppercase;font-size:11px;opacity:.5}.satus-shortcut__actions{display:flex;justify-content:flex-end}.satus-shortcut__actions .satus-button{height:32px;background:rgba(0,0,0,.15);margin:8px 4px 0;border-radius:8px}.satus-shortcut__actions .satus-button:hover{background:rgba(0,0,0,.25)}.satus-shortcut__primary{display:flex;box-sizing:border-box;width:100%;height:68px;padding:16px;background:rgba(0,0,0,.16);align-items:center}.satus-shortcut__key{display:flex;box-sizing:border-box;min-width:32px;height:32px;padding:4px 8px;border-radius:4px;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.15),inset 0 -3px 0 rgba(0,0,0,.1);align-items:center;justify-content:center}.satus-shortcut__plus{position:relative;width:12px;height:12px;margin:8px}.satus-shortcut__plus::before{position:absolute;top:0;left:5px;width:2px;height:12px;content:'';background-color:#aaa}.satus-shortcut__plus::after{position:absolute;top:5px;left:0;width:12px;height:2px;content:'';background-color:#aaa}.satus-shortcut__mouse{position:relative;display:flex;width:28px;height:36px;border-radius:50%;border-top-left-radius:12px;border-top-right-radius:12px;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.15),inset 0 -3px 0 rgba(0,0,0,.1)}.satus-shortcut__mouse>div{position:absolute;top:0;left:13px;width:2px;height:11px;border-radius:2px;background:#ccc}.satus-shortcut__mouse::before{position:absolute;top:-4px;left:21px;width:2px;height:18px;content:'';background:#f96754}.satus-shortcut__mouse.false::after{position:absolute;top:-12px;left:17px;width:0;height:0;content:'';border-right:5px solid transparent;border-bottom:8px solid #f96754;border-left:5px solid transparent}.satus-shortcut__mouse.true::after{position:absolute;top:14px;left:17px;width:0;height:0;content:'';border-top:8px solid #f96754;border-right:5px solid transparent;border-left:5px solid transparent}.satus-section_shortcut{width:100%;margin:8px 0 0;justify-content:flex-end}.satus-button_shortcut{font-weight:500;overflow:hidden;height:28px;min-height:28px;margin-right:2px;padding:4px 8px;text-transform:uppercase;color:#f96754;border-radius:4px}.satus-base{display:flex;flex-direction:column;width:100%;height:100%}.satus-text-field{position:relative;padding:0 16px;background-color:var(--satus-text-field--background);border-radius:8px;color:var(--satus-text-field--text);overflow:hidden;display:flex}.satus-text-field__pre{display:flex;position:relative;height:100%;margin:0;padding:0;overflow:hidden;align-items:center;flex:1}.satus-text-field__input{font:inherit;position:absolute;top:0;left:0;width:100%;min-width:0;max-width:none;height:100%;min-height:0;max-height:none;margin:0;padding:0;opacity:0;border:none;appearance:none;z-index:9}.satus-text-field__hidden-text{position:absolute;pointer-events:none;opacity:0}.satus-text-field__text{position:absolute;top:0;left:0;display:flex;height:100%;margin:0;align-items:center}.satus-text-field__cursor{position:absolute;top:6px;left:0;display:none;width:2px;height:25px;animation:blink 1s step-end 8;background:#fa0}.satus-text-field__selection{position:absolute;top:5px;left:0;display:none;width:0;height:25px;border:1px solid rgba(255,255,255,.2);border-radius:3px;background:rgba(255,255,255,.1)}.satus-text-field__input:focus+*+*+*+.satus-text-field__cursor,.satus-text-field__selection:not([disabled]){display:block}@keyframes blink{from,to{opacity:1}50%{opacity:0}}.satus-text-field__text>.group{color:#47ff47;background-color:rgb(71,255,71,.16)}.satus-text-field__text>.character-class{color:#ffc247;background-color:rgb(255,170,0,.16)}.satus-text-field__text>.quantifier{color:#47c2ff;background-color:rgb(71,194,255,.16)}.satus-text-field__text>.anchor{color:#47c2ff;background-color:rgb(71,194,255,.16)}.satus-text-field__text>.metasequence{color:#47ff47;background-color:rgb(71,255,71,.16)}.satus-text-field__text>.text{color:#c4c4d4;background-color:rgb(196,196,212,.16)}.satus-header{z-index:1;display:flex;box-sizing:border-box;height:56px;padding:0 12px;color:var(--satus-header-text);background:var(--satus-header-background);box-shadow:0 1px 2px rgb(47,41,34,.16);align-items:center;justify-content:space-between}.satus-header .satus-button{width:40px;min-width:40px;height:40px;padding:8px;color:inherit;border-radius:50%}.satus-header .satus-section--align-start>*{margin-right:8px}.satus-header .satus-span--title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;flex:1}.satus-alert{display:flex;box-sizing:border-box;min-height:48px;margin:8px;padding:8px 16px;border-radius:8px;align-items:center}.satus-alert--error{color:#c55959;border:1px solid #641616;background:#430f0f}.satus-layers{position:relative;overflow:hidden;color:var(--satus-layers-text);background:var(--satus-layers-background);flex:1}.satus-layer{position:absolute;top:0;left:0;display:flex;overflow-y:auto;box-sizing:border-box;width:100%;height:100%;padding:0 12px;flex-wrap:wrap;align-content:flex-start}.satus-divider{height:1px;margin:16px 0 12px;background:var(--satus-divider)}.satus-input[type=password],.satus-input[type=text]{font:inherit;box-sizing:border-box;width:100%;margin:0;padding:0;padding:0 8px;color:var(--satus-text-field--text,inherit);border:none;outline:0;background:0 0;appearance:none;font-size:16px;margin:8px 0;padding:4px 8px;border:1px solid var(--satus-text-field--border);border-radius:4px;background-color:var(--satus-text-field--background)}.satus-aside{color:var(--satus-aside-text);background:var(--satus-aside-background);box-sizing:border-box}.satus-select{position:relative;display:flex;box-sizing:border-box;align-items:center;justify-content:space-between}.satus-select__content{display:flex;align-items:center}.satus-select__content>svg{width:20px;height:18px;margin:0 14px 0 0;opacity:.75}.satus-select__value{margin-left:16px;text-align:right;opacity:.75}.satus-select select{font:inherit;position:absolute;top:0;left:0;width:100%;height:100%;margin:0;padding:0;padding:inherit;cursor:pointer;opacity:0;color:inherit;border:none;outline:0;background:0 0;appearance:none}.satus-select:hover{cursor:pointer;background-color:var(--satus-hover)}.satus-modal--contextmenu .satus-modal__scrim{background:0 0;backdrop-filter:none;animation:none;visibility:visible;opacity:1;transform:none}.satus-modal--contextmenu .satus-modal__surface{position:absolute;margin:0;box-sizing:border-box;min-width:200px;max-width:200px;padding:4px 0;border-radius:4px;border:1px solid var(--satus-context-menu--border);box-shadow:none;animation:none;visibility:visible;opacity:1;transform:none}.satus-modal--contextmenu .satus-modal__surface>*{display:flex;height:32px;padding:0 16px;align-items:center}.satus-modal--contextmenu .satus-modal__surface .satus-button svg{width:20px;height:18px;margin:0 14px 0 0;opacity:.75;fill:none;stroke:var(--satus-primary);flex:0 0 20px}.satus-modal--contextmenu .satus-modal__surface .satus-button .satus-span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.satus-pluviam{position:absolute;transform:scale(0);animation-name:pluviam;animation-duration:1s;opacity:var(--satus-pluviam-opacity,.08);border-radius:50%;background:var(--satus-pluviam-background,#000);animation-fill-mode:forwards}@keyframes pluviam{0%{transform:scale(0);opacity:var(--satus-pluviam-opacity,.08)}70%{transform:scale(.8);opacity:var(--satus-pluviam-opacity,.08)}100%{transform:scale(1);opacity:0}}.satus-sortable__chosen{color:var(--satus-sortable-text)!important;background-color:var(--satus-sortable-background)!important}.satus-sortable__ghost{position:fixed!important;z-index:999!important;top:0!important;left:0!important;pointer-events:none!important;box-shadow:0 1px 3px rgb(0,0,0,.2),0 4px 8px rgb(0,0,0,.1),inset 0 0 0 1px rgb(0,0,0,.16);will-change:transform!important;opacity:.8!important} \ No newline at end of file + +/*-------------------------------------------------------------- +>>> ELEMENTS: +---------------------------------------------------------------- +# Hidden +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +# HIDDEN +--------------------------------------------------------------*/ + +[hidden] { + display: none; +} +/*-------------------------------------------------------------- +>>> THEMES: +---------------------------------------------------------------- +# Default +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +# DEFAULT +--------------------------------------------------------------*/ + +body { + --satus-primary: #f6b465; + --satus-switch-background: rgba(0,0,0,.08); + --satus-header-background: #fff; + --satus-header-text: #848471; + --satus-layers-background: #f6f6f4; + --satus-layers-text: #848471; + --satus-section-card-background: #fff; + --satus-modal-background: #f6f6f4; + --satus-modal-text: #848471; + --satus-hover: rgba(0, 0, 0, .04); + --satus-text-field--background: #e8e8e3; + --satus-text-field--border: #d6d6cd; + --satus-text-field--text: #848471; + --satus-switch-track: #e0e0e0; + --satus-switch-track--active: var(--satus-primary); + --satus-switch-thumb: #fff; + --satus-tabs: #fff; + --satus-tooltip: rgba(0,0,0,.4); + --satus-sortable-ghost: rgba(0, 0, 0, .80); + --satus-sortable-background: #f9cf9f; + --satus-sortable-text: #fff; + --satus-divider: #e0e0e0; + --satus-tabs-background: #e8e8e3; + --satus-tabs-foreground: #fff; + --satus-context-menu--border: #ccc; + --satus-checkbox--background: rgb(90, 90, 73, .08); + --satus-checkbox--border: rgb(90, 90, 73, .16); + --satus-checkbox--mark: #fff; + --satus-alert-error-background: #fde6e6; + --satus-alert-error-border: #ecd7d7; + --satus-alert-error-color: #ad5f5f; +} + +body[theme=dark] { + --satus-primary: #90a5e0; + --satus-switch-background: rgba(0,0,0,.08); + --satus-header-background: #232b43; + --satus-header-text: #bcc4dc; + --satus-layers-background: #0e111b; + --satus-layers-text: #bcc4dc; + --satus-section-card-background: #1d2335; + --satus-modal-background: #252e46; + --satus-modal-text: #bcc4dc; + --satus-hover: rgba(255, 255, 255, .08); + --satus-text-field--background: #20273c; + --satus-text-field--border: #2e3957; + --satus-text-field--text: #c4c4d4; + --satus-switch-track: #101219; + --satus-switch-track--active: var(--satus-primary); + --satus-switch-thumb: #d7dcea; + --satus-tooltip: rgba(255,255,255,.4); + --satus-sortable-ghost: rgba(255, 255, 255, .8); + --satus-sortable-background: var(--satus-primary); + --satus-sortable-text: #fff; + --satus-divider: #3e4865; + --satus-tabs-background: #1f2433; + --satus-tabs-foreground: #344165; + --satus-context-menu--border: #2e3957; + --satus-checkbox--background: rgb(233, 234, 237, .08); + --satus-checkbox--border: rgb(233, 234, 237, .16); + --satus-checkbox--mark: #fff; + --satus-alert-error-background: #501616; + --satus-alert-error-border: #6f1f1f; + --satus-alert-error-color: #cf7777; +} + +body[theme=black] { + --satus-primary: #8f8f8f; + --satus-switch-background: rgba(255,255,255,.08); + --satus-header-background: #1f1f1f; + --satus-header-text: #ccc; + --satus-layers-background: #000; + --satus-layers-text: #ccc; + --satus-section-card-background: #1a1a1a; + --satus-modal-background: #212121; + --satus-modal-text: #ccc; + --satus-hover: rgba(255, 255, 255, .08); + --satus-text-field--background: #333333; + --satus-text-field--border: #525252; + --satus-text-field--text: #ccc; + --satus-switch-track: #111; + --satus-switch-track--active: var(--satus-primary); + --satus-switch-thumb: #ddd; + --satus-tooltip: rgba(255,255,255,.4); + --satus-sortable-ghost: rgba(255, 255, 255, .8); + --satus-sortable-background: var(--satus-primary); + --satus-sortable-text: #fff; + --satus-divider: #444; + --satus-tabs-background: #111; + --satus-tabs-foreground: #2e2e2e; + --satus-context-menu--border: #525252; + --satus-checkbox--background: rgb(235, 235, 235, .08); + --satus-checkbox--border: rgb(235, 235, 235, .16); + --satus-checkbox--mark: #fff; + --satus-alert-error-background: #501616; + --satus-alert-error-border: #6f1f1f; + --satus-alert-error-color: #cf7777; +} +/*-------------------------------------------------------------- +>>> COLOR PICKER: +---------------------------------------------------------------- +# Button +# Modal +--------------------------------------------------------------*/ + + +/*-------------------------------------------------------------- +# BUTTON +--------------------------------------------------------------*/ + +.satus-color-picker { + font-size: inherit; + position: relative; + display: flex; + box-sizing: border-box; + margin: 0; + cursor: pointer; + color: inherit; + border: none; + outline: none; + background-color: var(--satus-theme-button); + justify-content: space-between; + -webkit-tap-highlight-color: transparent; + align-items: center; + -webkit-appearance: none; +} + +.satus-color-picker__value { + width: 22px; + height: 22px; + border: 2px solid rgba(0, 0, 0, .16); + border-radius: 50%; + background: #fff; +} + + +/*-------------------------------------------------------------- +# MODAL +--------------------------------------------------------------*/ + +.satus-modal--color-picker { + position: relative; +} + +.satus-modal--color-picker .satus-modal__surface { + padding-top: 0; +} + +.satus-color-picker__palette { + position: relative; + overflow: hidden; + width: 100%; + height: 256px; + background-color: #f00; +} + +.satus-color-picker__palette:before { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + content: ''; + background-image: linear-gradient(0deg, black, transparent), linear-gradient(90deg, white, transparent); +} + +.satus-color-picker__cursor { + position: absolute; + width: 5px; + height: 5px; + transform: translate(-50%, -50%); + pointer-events: none; + border: 1px solid #fff; + border-radius: 50%; + box-shadow: 0 0 0 1px #000; +} + +.satus-modal--color-picker .satus-modal__surface .satus-section--color { + margin: 8px 16px 0; + align-items: center; +} + +.satus-color-picker__color { + width: 32px; + height: 32px; + margin: 0 16px 0 0; + border: 2px solid rgba(0, 0, 0, .16); + border-radius: 50%; + background: #f00; +} + +.satus-slider.satus-color-picker__hue { + padding: 0; + flex: 1; +} + +.satus-color-picker__hue .satus-slider__track { + height: 16px; + border-radius: 4px; + 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); +} + +.satus-color-picker__hue .satus-slider__handle { + width: 16px; + height: 16px; + background: #fff; + box-shadow: 0 0 4px rgb(0, 0, 0, .64); +} + +.satus-color-picker__hue::before, +.satus-color-picker__hue .satus-slider__track-fill, +.satus-color-picker__hue .satus-slider__handle:focus::after { + display: none; +} +/*-------------------------------------------------------------- +# MAIN +--------------------------------------------------------------*/ + +.satus-main { + color: var(--satus-main-text); + background: var(--satus-main-background); + overflow-y: auto; + box-sizing: border-box; +} +/*-------------------------------------------------------------- +>>> TABS +--------------------------------------------------------------*/ + +.satus-tabs { + display: flex; + height: 26px !important; +} + +.satus-tabs__content { + position: relative; + overflow: hidden; + width: 100%; + height: 100%; + border: 2px solid var(--satus-tabs-background); + border-radius: 4px; + background: var(--satus-tabs-background); +} + +.satus-tabs__selection { + position: absolute; + z-index: 0; + top: 0; + left: 0; + height: 100%; + transition: left .25s; + border-radius: 4px; + background: var(--satus-tabs-foreground); +} + +.satus-tabs__button { + font: inherit; + position: relative; + z-index: 1; + overflow: hidden; + height: 100%; + padding: 0 4px; + white-space: nowrap; + text-overflow: ellipsis; + color: inherit; + border: none; + background: transparent; + flex: 1; +} + +.satus-tabs__button:hover { + cursor: pointer; +} +/*-------------------------------------------------------------- +>>> MENUBAR +--------------------------------------------------------------*/ + +.satus-menubar, +.satus-menubar ul { + margin: 0; + padding: 0; + list-style: none; + background: var(--satus-menubar-background); + color: var(--satus-menubar-text); +} + +.satus-menubar li > .satus-button { + height: 32px; + font-size: 14px; +} + +.satus-menubar > li > ul { + display: none; + position: absolute; +} + +.satus-menubar > li > ul:hover, +.satus-menubar > li > *:focus + ul { + display: block; +} +/*-------------------------------------------------------------- +>>> RADIO +--------------------------------------------------------------*/ +/*-------------------------------------------------------------- +# BUTTON +--------------------------------------------------------------*/ + +.satus-button { + font: inherit; + position: relative; + display: flex; + overflow: hidden; + height: 48px; + margin: 0; + padding: 8px; + text-align: left; + color: var(--satus-button-text, inherit); + border: none; + background: var(--satus-button-background, transparent); + appearance: none; + align-items: center; +} + +.satus-button:hover { + cursor: pointer; + background-color: var(--satus-hover); +} + +.satus-button[hidden] { + display: none; +} + +.satus-button>svg { + min-width: 20px; +} + +.satus-button>.satus-span--label { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +/*-------------------------------------------------------------- +>>> LIST: +--------------------------------------------------------------*/ + +.satus-list { + list-style: none; + margin: 0; +} + +.satus-list__item { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 48px; +} + +.satus-list__item>*:last-child { + text-align: right; +} +/*-------------------------------------------------------------- +>>> SECTION: +---------------------------------------------------------------- +# Align +# Card +--------------------------------------------------------------*/ + +.satus-section { + display: flex; + box-sizing: border-box; + flex-wrap: wrap; +} + + +/*-------------------------------------------------------------- +# ALIGN +--------------------------------------------------------------*/ + +.satus-section--align-start { + align-items: center; + justify-content: flex-start; +} + +.satus-section--align-end { + align-items: center; + justify-content: flex-end; +} + +.satus-section--space-between { + align-items: center; + justify-content: space-between; +} + +.satus-section--column { + flex-direction: column; +} + + +/*-------------------------------------------------------------- +# CARD +--------------------------------------------------------------*/ + +.satus-section--card { + flex-direction: column; + box-sizing: border-box; + width: 100%; + max-width: 900px; + margin: 8px auto; + padding: 8px 0; + color: var(--satus-section-card-text); + border: 1px solid rgba(0, 0, 0, .1); + border-radius: 8px; + background: var(--satus-section-card-background); + justify-content: stretch; +} + +.satus-section--card>*:not(.satus-button) { + box-sizing: border-box; + min-height: 48px; + padding: 0 16px; + text-align: left; +} + +.satus-section--card>.satus-switch, +.satus-section--card>.satus-select, +.satus-section--card>.satus-radio { + display: flex; + justify-content: space-between; + align-items: center; +} + +.satus-section--card>.satus-button:hover, +.satus-section--card>.satus-switch:hover, +.satus-section--card>.satus-select:hover, +.satus-section--card>.satus-slider:hover, +.satus-section--card>.satus-radio:hover { + background-color: var(--satus-hover); +} + +.satus-section--card>.satus-button { + width: 100%; + padding: 0 16px; +} + +.satus-section--card>.satus-button>svg { + width: 20px; + margin: 2px 16px 0 0; + color: var(--satus-primary); +} + +.satus-section--card>.satus-span { + display: flex; + align-items: center; +} +/*-------------------------------------------------------------- +# SCROLLBAR +--------------------------------------------------------------*/ + +::-webkit-scrollbar { + width: 4px; +} + +::-webkit-scrollbar:hover { + width: 8px; +} + +::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, .3); +} +/*-------------------------------------------------------------- +>>> MODAL +--------------------------------------------------------------*/ + +.satus-modal { + position: absolute; + z-index: 100; + top: 0; + left: 0; + display: flex; + width: 100%; + height: 100vh; + justify-content: center; + align-items: center; +} + +.satus-modal__scrim { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + animation: modalFadeIn 150ms linear forwards; + opacity: 0; + background: rgba(0, 0, 0, .16); + backdrop-filter: blur(8px); +} + +.satus-modal__surface { + display: flex; + overflow-y: auto; + flex-direction: column; + width: 95%; + min-width: 240px; + max-width: 560px; + max-height: 80%; + margin: 0 16px; + padding: 12px 16px; + transform: scale(.8); + animation: modalZoomIn 150ms linear forwards; + animation-delay: 20ms; + opacity: 0; + color: var(--satus-modal-text); + border-radius: 6px; + background-color: var(--satus-modal-background); + box-shadow: inset 0 -1px 1px 1px rgb(0, 0, 0, .1), 0 2px 6px rgb(0, 0, 0, .15); +} + +.satus-modal__surface .satus-section--actions { + display: flex; + width: 100%; + margin: 8px 0 0; + padding: 0; + justify-content: flex-end; + align-items: center; +} + +.satus-modal__surface .satus-section--actions .satus-button { + font-weight: 500; + height: 32px; + margin-left: 8px; + padding: 0 8px; + letter-spacing: .5px; + color: var(--satus-primary); + border-radius: 4px; +} + +.satus-modal--closing .satus-modal__scrim { + animation: modalFadeOut 70ms linear forwards; +} + +.satus-modal--closing .satus-modal__surface { + animation: modalZoomOut 70ms linear forwards; +} + +.satus-modal--vertical .satus-modal__surface { + position: absolute; + top: 8px; + right: 8px; + left: auto; + min-width: 200px; + max-width: 200px; + margin: 0; + padding: 8px 0; + transform-origin: right top; +} + +.satus-modal--vertical .satus-modal__surface>.satus-button, +.satus-modal--vertical .satus-modal__surface>.satus-switch, +.satus-modal--vertical .satus-modal__surface>.satus-select { + display: flex; + height: 36px; + padding: 0 16px; + align-items: center; +} + +.satus-modal--vertical .satus-modal__surface>.satus-tabs { + padding: 0 12px; +} + +.satus-modal--vertical .satus-modal__surface>.satus-span { + font-size: 13px; + font-weight: 500; + margin: 6px 0; + padding: 0 12px; +} + +.satus-modal--vertical .satus-modal__surface>.satus-button:hover, +.satus-modal--vertical .satus-modal__surface>.satus-switch:hover, +.satus-modal--vertical .satus-modal__surface>.satus-select:hover { + background-color: var(--satus-hover); +} + +.satus-modal--vertical .satus-button svg { + width: 20px; + height: 18px; + margin: 0 14px 0 0; + opacity: .75; + flex: 0 0 20px; +} + +.satus-modal--vertical .satus-button .satus-span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + + +/*-------------------------------------------------------------- +# ANIMATIONS +--------------------------------------------------------------*/ + +@keyframes modalFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes modalFadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes modalZoomIn { + from { + transform: scale(.8); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } +} + +@keyframes modalZoomOut { + from { + transform: scale(1); + opacity: 1; + } + to { + transform: scale(.8); + opacity: 0; + } +} +/*-------------------------------------------------------------- +>>> CHECKBOX +--------------------------------------------------------------*/ + +.satus-checkbox { + position: relative; + font: inherit; + display: flex; + color: inherit; + border: none; + background: transparent; + appearance: none; + align-items: center; + justify-content: flex-start; +} + +.satus-checkbox:hover { + cursor: pointer; + background-color: var(--satus-hover); +} + +.satus-checkbox:focus { + outline: none; +} + +.satus-checkbox__content { + display: block; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.satus-checkbox::before { + display: flex; + min-width: 16px; + width: 16px; + height: 16px; + margin: 0 12px 0 0; + content: ''; + border: 1px solid var(--satus-checkbox--border); + border-radius: 6px; + background: var(--satus-checkbox--background); + align-items: center; + justify-content: center; +} + +.satus-checkbox[data-value=true]::before { + background: var(--satus-primary); +} + +.satus-checkbox[data-value=true]::after { + position: absolute; + top: 20px; + left: 20px; + width: 8px; + height: 4px; + content: ''; + transform: rotate(-45deg); + border: 2px solid var(--satus-checkbox--mark); + border-top: none; + border-right: none; +} +/*-------------------------------------------------------------- +>>> SWITCH +---------------------------------------------------------------- +# Container +# Track +# Thumb +--------------------------------------------------------------*/ + + +/*-------------------------------------------------------------- +# CONTAINER +--------------------------------------------------------------*/ + +.satus-switch { + font: inherit; + display: flex; + transition: background-color 75ms; + color: inherit; + border: none; + outline: none; + background-color: transparent; + justify-content: space-between; + align-items: center; +} + +.satus-switch:hover { + cursor: pointer; +} + +.satus-switch__content { + display: flex; + align-items: center; +} + +.satus-switch__content>svg { + width: 20px; + height: 18px; + margin: 0 14px 0 0; + opacity: .75; +} + + +/*-------------------------------------------------------------- +# TRACK +--------------------------------------------------------------*/ + +.satus-switch>i { + width: 32px; + height: 18px; + transition: background-color 150ms; + border-radius: 18px; + background-color: var(--satus-switch-track); + flex: 0 0 32px; +} + +.satus-section--card .satus-switch>i { + margin-left: 16px; +} + +.satus-switch[data-value='true']>i { + background-color: var(--satus-switch-track--active); +} + + +/*-------------------------------------------------------------- +# THUMB +--------------------------------------------------------------*/ + +.satus-switch>i::before { + display: block; + width: 14px; + height: 14px; + margin: 2px; + content: ''; + transition: transform 150ms cubic-bezier(.4, 0, .2, 1); + border-radius: 50%; + background-color: var(--satus-switch-thumb); + will-change: transform; +} + +.satus-switch[data-value='true']>i::before { + transform: translateX(14px); +} +/*-------------------------------------------------------------- +>>> SLIDER +--------------------------------------------------------------*/ + +.satus-slider { + box-sizing: border-box; + width: 100%; + padding: 8px 16px 0; + outline: none; +} + +.satus-slider__container { + position: relative; + height: 32px; + width: 100%; +} + +.satus-slider__track { + position: absolute; + top: 50%; + left: 0; + height: 2px; + width: 100%; + transform: translateY(-50%); +} + +.satus-slider__track::before { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + background: var(--satus-primary); + opacity: .24; + content: ''; +} + +.satus-slider__handle:focus::after { + content: attr(data-value); + position: absolute; + left: 50%; + top: -2px; + background: var(--satus-primary); + color: #fff; + display: block; + transform: translate(-50%, -100%); + padding: 1px 4px; + border-radius: 4px; +} + +.satus-slider__track-fill { + position: absolute; + left: 0; + top: 0; + height: 100%; + background: var(--satus-primary); +} + +.satus-slider__handle { + position: absolute; + width: 10px; + height: 10px; + background: var(--satus-primary); + border-radius: 50%; + transform: translate(-50%, -50%); + top: 50%; + left: 0; +} + +.satus-slider__handle::before { + content: ''; + background: var(--satus-primary); + position: absolute; + left: 50%; + top: 50%; + width: 1px; + height: 1px; + opacity: 0; + border-radius: 50%; + transform: translate(-50%, -50%) scale(1); + transition: .2s; +} + +.satus-slider:focus .satus-slider__handle::before { + transform: translate(-50%, -50%) scale(26); + opacity: .24; +} +/*-------------------------------------------------------------- +>>> SHORTCUT: +---------------------------------------------------------------- +# +--------------------------------------------------------------*/ + +.satus-shortcut { + justify-content: space-between; +} + +.satus-shortcut__value { + text-transform: uppercase; + font-size: 11px; + opacity: .5; +} + +.satus-shortcut__actions { + display: flex; + justify-content: flex-end; +} + +.satus-shortcut__actions .satus-button { + height: 32px; + background: rgba(0,0,0,.15); + margin: 8px 4px 0; + border-radius: 8px; +} + +.satus-shortcut__actions .satus-button:hover { + background: rgba(0,0,0,.25); +} + +.satus-shortcut__primary { + display: flex; + box-sizing: border-box; + width: 100%; + height: 68px; + padding: 16px; + background: rgba(0,0,0,.16); + align-items: center; +} + +.satus-shortcut__key { + display: flex; + box-sizing: border-box; + min-width: 32px; + height: 32px; + padding: 4px 8px; + border-radius: 4px; + background: #fff; + box-shadow: 0 1px 3px rgba(0, 0, 0, .15), inset 0 -3px 0 rgba(0, 0, 0, .1); + align-items: center; + justify-content: center; +} + +.satus-shortcut__plus { + position: relative; + width: 12px; + height: 12px; + margin: 8px; +} + +.satus-shortcut__plus::before { + position: absolute; + top: 0; + left: 5px; + width: 2px; + height: 12px; + content: ''; + background-color: #aaa; +} + +.satus-shortcut__plus::after { + position: absolute; + top: 5px; + left: 0; + width: 12px; + height: 2px; + content: ''; + background-color: #aaa; +} + +.satus-shortcut__mouse { + position: relative; + display: flex; + width: 28px; + height: 36px; + border-radius: 50%; + border-top-left-radius: 12px; + border-top-right-radius: 12px; + background: #fff; + box-shadow: 0 1px 3px rgba(0, 0, 0, .15), inset 0 -3px 0 rgba(0, 0, 0, .1); +} + +.satus-shortcut__mouse>div { + position: absolute; + top: 0; + left: 13px; + width: 2px; + height: 11px; + border-radius: 2px; + background: #ccc; +} + +.satus-shortcut__mouse::before { + position: absolute; + top: -4px; + left: 21px; + width: 2px; + height: 18px; + content: ''; + background: #f96754; +} + +.satus-shortcut__mouse.false::after { + position: absolute; + top: -12px; + left: 17px; + width: 0; + height: 0; + content: ''; + border-right: 5px solid transparent; + border-bottom: 8px solid #f96754; + border-left: 5px solid transparent; +} + +.satus-shortcut__mouse.true::after { + position: absolute; + top: 14px; + left: 17px; + width: 0; + height: 0; + content: ''; + border-top: 8px solid #f96754; + border-right: 5px solid transparent; + border-left: 5px solid transparent; +} + +.satus-section_shortcut { + width: 100%; + margin: 8px 0 0; + justify-content: flex-end; +} + +.satus-button_shortcut { + font-weight: 500; + overflow: hidden; + height: 28px; + min-height: 28px; + margin-right: 2px; + padding: 4px 8px; + text-transform: uppercase; + color: #f96754; + border-radius: 4px; +} +/*-------------------------------------------------------------- +>>> BASE +--------------------------------------------------------------*/ + +.satus-base{ + display: flex; + flex-direction: column; + width: 100%; + height: 100%; +} +/*-------------------------------------------------------------- +>>> TEXT FIELD +--------------------------------------------------------------*/ + +.satus-text-field { + position: relative; + padding: 0 16px; + background-color: var(--satus-text-field--background); + border-radius: 8px; + color: var(--satus-text-field--text); + overflow: hidden; + display: flex; +} + +.satus-text-field__pre { + display: flex; + position: relative; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + align-items: center; + flex: 1; +} + +.satus-text-field__input { + font: inherit; + position: absolute; + top: 0; + left: 0; + width: 100%; + min-width: 0; + max-width: none; + height: 100%; + min-height: 0; + max-height: none; + margin: 0; + padding: 0; + opacity: 0; + border: none; + appearance: none; + z-index: 9; +} + +.satus-text-field__hidden-text { + position: absolute; + pointer-events: none; + opacity: 0; +} + +.satus-text-field__text { + position: absolute; + top: 0; + left: 0; + display: flex; + height: 100%; + margin: 0; + align-items: center; +} + +.satus-text-field__cursor { + position: absolute; + top: 6px; + left: 0; + display: none; + width: 2px; + height: 25px; + animation: blink 1s step-end 8; + background: #fa0; +} + +.satus-text-field__selection { + position: absolute; + top: 5px; + left: 0; + display: none; + width: 0; + height: 25px; + border: 1px solid rgba(255, 255, 255, .2); + border-radius: 3px; + background: rgba(255, 255, 255, .1); +} + +.satus-text-field__input:focus + * + * + * + .satus-text-field__cursor, +.satus-text-field__selection:not([disabled]) { + display: block; +} + +@keyframes blink { + from, + to { + opacity: 1; + } + 50% { + opacity: 0; + } +} + + +/*-------------------------------------------------------------- +# SYNTAX HIGHLIGHTING +--------------------------------------------------------------*/ + +.satus-text-field__text>.group { + color: #47ff47; + background-color: rgb(71, 255, 71, .16); +} + +.satus-text-field__text>.character-class { + color: #ffc247; + background-color: rgb(255, 170, 0, .16); +} + +.satus-text-field__text>.quantifier { + color: #47c2ff; + background-color: rgb(71, 194, 255, .16); +} + +.satus-text-field__text>.anchor { + color: #47c2ff; + background-color: rgb(71, 194, 255, .16); +} + +.satus-text-field__text>.metasequence { + color: #47ff47; + background-color: rgb(71, 255, 71, .16); +} + +.satus-text-field__text>.text { + color: #c4c4d4; + background-color: rgb(196, 196, 212, .16); +} +/*-------------------------------------------------------------- +>>> HEADER +--------------------------------------------------------------*/ + +.satus-header { + z-index: 1; + display: flex; + box-sizing: border-box; + height: 56px; + padding: 0 12px; + color: var(--satus-header-text); + background: var(--satus-header-background); + box-shadow: 0 0 3px rgb(0, 0, 0, .1); + align-items: center; + justify-content: space-between; +} + +.satus-header .satus-button { + width: 40px; + min-width: 40px; + height: 40px; + padding: 8px; + color: inherit; + border-radius: 50%; +} + +.satus-header .satus-section--align-start > * { + margin-right: 8px; +} + +.satus-header .satus-span--title { + font-size: 15px; + overflow: hidden; + white-space: nowrap; + letter-spacing: .0125em; + text-overflow: ellipsis; + flex: 1; +} + +/*-------------------------------------------------------------- +>>> ALERT +--------------------------------------------------------------*/ + +.satus-alert { + display: flex; + box-sizing: border-box; + min-height: 48px; + margin: 8px 0 0; + padding: 8px 16px; + border-radius: 8px; + align-items: center; +} + +.satus-alert--error { + color: var(--satus-alert-error-color); + border: 1px solid var(--satus-alert-error-border); + background: var(--satus-alert-error-background); +} +/*-------------------------------------------------------------- +>>> LAYERS +--------------------------------------------------------------*/ + +.satus-layers { + position: relative; + overflow: hidden; + color: var(--satus-layers-text); + background: var(--satus-layers-background); + flex: 1; +} + +.satus-layer { + position: absolute; + top: 0; + left: 0; + display: flex; + overflow-y: auto; + box-sizing: border-box; + width: 100%; + height: 100%; + padding: 0 12px; + flex-wrap: wrap; + align-content: flex-start; +} +/*-------------------------------------------------------------- +>>> DIVIDER +--------------------------------------------------------------*/ + +.satus-divider { + height: 1px; + margin: 16px 0 12px; + background: var(--satus-divider); +} +/*-------------------------------------------------------------- +# INPUT +--------------------------------------------------------------*/ + +.satus-input[type=text], +.satus-input[type=password] { + font: inherit; + box-sizing: border-box; + width: 100%; + margin: 0; + padding: 0 8px; + color: var(--satus-text-field--text, inherit); + border: none; + outline: none; + background: none; + appearance: none; + font-size: 16px; + margin: 8px 0; + border: 1px solid var(--satus-text-field--border); + border-radius: 4px; + background-color: var(--satus-text-field--background); +} +/**/ + +.satus-aside { + color: var(--satus-aside-text); + background: var(--satus-aside-background); + box-sizing: border-box; +} +/*-------------------------------------------------------------- +>>> SELECT +--------------------------------------------------------------*/ + +.satus-select { + position: relative; + display: flex; + box-sizing: border-box; + align-items: center; + justify-content: space-between; +} + +.satus-select__content { + display: flex; + align-items: center; +} + +.satus-select__content > svg { + width: 20px; + + height: 18px; + margin: 0 14px 0 0; + opacity: .75; +} + +.satus-select__value { + margin-left: 16px; + text-align: right; + opacity: .75; +} + +.satus-select select { + font: inherit; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + padding: inherit; + cursor: pointer; + opacity: 0; + color: inherit; + border: none; + outline: none; + background: none; + appearance: none; +} + +.satus-select:hover { + cursor: pointer; + background-color: var(--satus-hover); +} +/*-------------------------------------------------------------- +>>> CONTEXT MENU +--------------------------------------------------------------*/ + +.satus-modal--contextmenu .satus-modal__scrim { + background: none; + backdrop-filter: none; + animation: none; + visibility: visible; + opacity: 1; + transform: none; +} + +.satus-modal--contextmenu .satus-modal__surface { + position: absolute; + margin: 0; + box-sizing: border-box; + min-width: 200px; + max-width: 200px; + padding: 4px 0; + border-radius: 4px; + border: 1px solid var(--satus-context-menu--border); + box-shadow: none; + animation: none; + visibility: visible; + opacity: 1; + transform: none; +} + +.satus-modal--contextmenu .satus-modal__surface > * { + +} + +.satus-modal--contextmenu .satus-modal__surface>* { + display: flex; + height: 32px; + padding: 0 16px; + align-items: center; +} + +.satus-modal--contextmenu .satus-modal__surface .satus-button svg { + width: 20px; + height: 18px; + margin: 0 14px 0 0; + opacity: .75; + fill: none; + stroke: var(--satus-primary); + flex: 0 0 20px; +} + +.satus-modal--contextmenu .satus-modal__surface .satus-button .satus-span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +/*-------------------------------------------------------------- +>>> PLUVIAM +--------------------------------------------------------------*/ + +.satus-pluviam { + position: absolute; + transform: scale(0); + animation-name: pluviam; + animation-duration: 1000ms; + opacity: var(--satus-pluviam-opacity, .08); + border-radius: 50%; + background: var(--satus-pluviam-background, #000); + animation-fill-mode: forwards; +} + +@keyframes pluviam { + 0% { + transform: scale(0); + opacity: var(--satus-pluviam-opacity, .08); + } + 70% { + transform: scale(.8); + opacity: var(--satus-pluviam-opacity, .08); + } + 100% { + transform: scale(1); + opacity: 0; + } +} +/*-------------------------------------------------------------- +>>> SORTABLE +--------------------------------------------------------------*/ + +.satus-sortable__chosen { + color: var(--satus-sortable-text) !important; + background-color: var(--satus-sortable-background) !important; +} + +.satus-sortable__ghost { + position: fixed !important; + z-index: 999 !important; + top: 0 !important; + left: 0 !important; + pointer-events: none !important; + box-shadow: 0 1px 3px rgb(0, 0, 0, .2), 0 4px 8px rgb(0, 0, 0, .1), inset 0 0 0 1px rgb(0, 0, 0, .16); + will-change: transform !important; + opacity: .8 !important; +} \ No newline at end of file diff --git a/ui/satus/satus.js b/ui/satus/satus.js index fc28a04..b2e5842 100644 --- a/ui/satus/satus.js +++ b/ui/satus/satus.js @@ -1 +1,2647 @@ -var satus={components:{},events:{},locale:{strings:{}},storage:{attributes:{},data:{}},append:function(e,t){(t||document.body).appendChild(e)},getAnimationDuration:function(e){return 1e3*Number(window.getComputedStyle(e).getPropertyValue("animation-duration").replace(/[^0-9.]/g,""))},attr:function(e,t){if(t)for(var n in t){var a;e.is_svg?e.setAttributeNS(null,n,t[n]):(a=t[n],-1!==["placeholder","title"].indexOf(n)&&(a=satus.locale.get(a)),e.setAttribute(n,a))}},elementIndex:function(e){return Array.prototype.slice.call(e.parentNode.children).indexOf(e)},data:function(e,t){if(t)for(var n in t)e.dataset[n]=t[n]},properties:function(e,t){if(t)for(var n in t)e[n]=t[n]},camelize:function(e){for(var t="",n=0,a=e.length;nparseInt(e,16)),a={name:"AES-GCM",iv:new Uint8Array(n)};try{var s=(new TextDecoder).decode(await crypto.subtle.decrypt(a,await crypto.subtle.importKey("raw",await crypto.subtle.digest("SHA-256",(new TextEncoder).encode(t)),a,!1,["decrypt"]),new Uint8Array(atob(e.slice(24)).match(/[\s\S]/g).map(e=>e.charCodeAt(0)))))}catch(e){return!1}return s},satus.encrypt=async function(e,t){var n=crypto.getRandomValues(new Uint8Array(12)),a={name:"AES-GCM",iv:n};return Array.from(n).map(e=>("00"+e.toString(16)).slice(-2)).join("")+btoa(Array.from(new Uint8Array(await crypto.subtle.encrypt(a,await crypto.subtle.importKey("raw",await crypto.subtle.digest("SHA-256",(new TextEncoder).encode(t)),a,!1,["encrypt"]),(new TextEncoder).encode(e)))).map(e=>String.fromCharCode(e)).join(""))},satus.isArray=function(e){return!!Array.isArray(e)},satus.isNumber=function(e){return"number"==typeof e&&!1===isNaN(e)},satus.indexOf=function(e,t){var n=0;if(satus.isArray(t))n=t.indexOf(e);else for(;e=e.previousElementSibling;)n++;return n},satus.toIndex=function(e,t,n){satus.isArray(n)&&n.splice(e,0,n.splice(satus.indexOf(t,n),1)[0])},satus.clone=function(e){for(var t=e.cloneNode(!0),n=window.getComputedStyle(e.parentNode),a=window.getComputedStyle(e),s="",o=0,r=a.length;o>> CORE: +---------------------------------------------------------------- +# Global variable +# Functions +# Render +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +# GLOBAL VARIABLE +--------------------------------------------------------------*/ + +var satus = { + components: {}, + events: {}, + locale: { + strings: {} + }, + storage: { + attributes: {}, + data: {} + } +}; + + +/*-------------------------------------------------------------- +# FUNCTIONS +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +# APPEND +--------------------------------------------------------------*/ + +satus.append = function (element, container) { + (container || document.body).appendChild(element); +}; + + +/*-------------------------------------------------------------- +# ANIMATION DURATION +--------------------------------------------------------------*/ + +satus.getAnimationDuration = function (element) { + return Number(window.getComputedStyle(element).getPropertyValue('animation-duration').replace(/[^0-9.]/g, '')) * 1000; +}; + + +/*-------------------------------------------------------------- +# APPEND +--------------------------------------------------------------*/ + +satus.attr = function (element, attributes) { + if (attributes) { + for (var key in attributes) { + if (element.is_svg) { + element.setAttributeNS(null, key, attributes[key]); + } else { + var value = attributes[key]; + + if (['placeholder', 'title'].indexOf(key) !== -1) { + value = satus.locale.get(value); + } + + element.setAttribute(key, value); + } + } + } +}; + +satus.elementIndex = function (element) { + return Array.prototype.slice.call(element.parentNode.children).indexOf(element); +}; + + +/*-------------------------------------------------------------- +# DATA +--------------------------------------------------------------*/ + +satus.data = function (element, data) { + if (data) { + for (var key in data) { + element.dataset[key] = data[key]; + } + } +}; + + +/*-------------------------------------------------------------- +# PROPERTIES +--------------------------------------------------------------*/ + +satus.properties = function (element, properties) { + if (properties) { + for (var key in properties) { + element[key] = properties[key]; + } + } +}; + + +/*-------------------------------------------------------------- +# CAMELIZE +--------------------------------------------------------------*/ + +satus.camelize = function (string) { + var result = ''; + + for (var i = 0, l = string.length; i < l; i++) { + var character = string[i]; + + if (character === '-') { + i++; + + result += string[i].toUpperCase(); + } else { + result += character; + } + } + + return result; +}; + + +/*-------------------------------------------------------------- +# SNAKELIZE +--------------------------------------------------------------*/ + +satus.snakelize = function (string) { + return string.replace(/([A-Z])/g, '-$1').toLowerCase(); +}; + + +/*-------------------------------------------------------------- +# CLASS +--------------------------------------------------------------*/ + +satus.class = function (element, string) { + if (string) { + element.className += ' ' + string; + } +}; + + +/*-------------------------------------------------------------- +# EMPTY +--------------------------------------------------------------*/ + +satus.empty = function (element, exclude = []) { + for (var i = element.childNodes.length - 1; i > -1; i--) { + var child = element.childNodes[i]; + + if (exclude.indexOf(child) === -1) { + child.remove(); + } + } +}; + + +/*-------------------------------------------------------------- +# EVENTS +--------------------------------------------------------------*/ + +Object.defineProperty(satus.events, 'add', { + value: function (type, listener) { + if (this.hasOwnProperty(type) === false) { + this[type] = []; + } + + this[type].push(listener); + } +}); + + +/*-------------------------------------------------------------- +# ISSET +--------------------------------------------------------------*/ + +satus.isset = function (variable) { + if (variable === null || variable === undefined) { + return false; + } + + return true; +}; + + +/*-------------------------------------------------------------- +# FETCH +--------------------------------------------------------------*/ + +satus.fetch = function (url, success, error) { + fetch(url).then(function (response) { + if (response.ok) { + response.json().then(success); + } else { + error(); + } + }); +}; + + +/*-------------------------------------------------------------- +# AJAX +--------------------------------------------------------------*/ + +satus.ajax = function (url, success, error) { + var xhr = new XMLHttpRequest(); + + xhr.onload = function () { + success(this.response); + }; + xhr.onerror = function () { + error(success); + }; + + xhr.open('GET', url, true); + xhr.send(); +}; + + +/*-------------------------------------------------------------- +# STORAGE +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +# GET +--------------------------------------------------------------*/ + +satus.storage.get = function (name) { + var target = satus.storage.data; + + if (typeof name !== 'string') { + return; + } + + name = name.split('/').filter(function (value) { + return value != ''; + }); + + for (var i = 0, l = name.length; i < l; i++) { + if (satus.isset(target[name[i]])) { + target = target[name[i]]; + } else { + return undefined; + } + } + + return target; +}; + + +/*-------------------------------------------------------------- +# SET +--------------------------------------------------------------*/ + +satus.storage.set = function (name, value) { + var items = {}, + target = satus.storage.data; + + if (typeof name !== 'string') { + return; + } + + name = name.split('/').filter(function (value) { + return value != ''; + }); + + for (var i = 0, l = name.length; i < l; i++) { + var item = name[i]; + + if (i < l - 1) { + + if (target[item]) { + target = target[item]; + } else { + target[item] = {}; + + target = target[item]; + } + } else { + target[item] = value; + } + } + + for (var key in this.data) { + if (typeof this.data[key] !== 'function') { + items[key] = this.data[key]; + } + } + + if (satus.storage.attributes[name]) { + document.body.setAttribute(name, value); + } + + chrome.storage.local.set(items); +}; + + +/*-------------------------------------------------------------- +# REMOVE +--------------------------------------------------------------*/ + +satus.storage.remove = function (name) { + delete this.data[name]; + + chrome.storage.local.remove(name); +}; + + +/*-------------------------------------------------------------- +# IMPORT +--------------------------------------------------------------*/ + +satus.storage.import = function (callback) { + chrome.storage.local.get(function (items) { + for (var key in items) { + if (satus.storage.attributes[key]) { + document.body.setAttribute(key, items[key]); + } + + satus.storage.data[key] = items[key]; + } + + if (callback) { + callback(items); + } + }); +}; + + +/*-------------------------------------------------------------- +# CLEAR +--------------------------------------------------------------*/ + +satus.storage.clear = function (callback) { + this.data = {}; + + chrome.storage.local.clear(callback); +}; + + +/*-------------------------------------------------------------- +# LOCALIZATION +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +# GET +--------------------------------------------------------------*/ + +satus.locale.get = function (string) { + return this.strings[string] || string; +}; + + +/*-------------------------------------------------------------- +# IMPORT +--------------------------------------------------------------*/ + +satus.locale.import = function (string, path, callback) { + var language = string || window.navigator.language; + + if (language.indexOf('en') === 0) { + language = 'en'; + } + + if (!path) { + path = '_locales/'; + } + + satus.ajax(path + language + '/messages.json', function (response) { + try { + response = JSON.parse(response); + + for (var key in response) { + satus.locale.strings[key] = response[key].message; + } + } catch (error) { + console.error(error); + } + + callback(); + }, function (success) { + satus.ajax(path + 'en/messages.json', success); + }); +}; + + +/*-------------------------------------------------------------- +# ON +--------------------------------------------------------------*/ + +satus.on = function (element, events) { + if (this.isset(events) && typeof events === 'object') { + for (var selector in events) { + var type = typeof events[selector]; + + if (selector === 'selectionchange') { + element = document; + } + + if (type === 'function') { + element.addEventListener(selector, events[selector]); + } else if (type === 'object') { + element.addEventListener(selector, function (event) { + this.skeleton.on[event.type].parent = this.skeleton; + + if (this.skeleton.on[event.type].component !== 'modal' && this.base && this.base.layers) { + this.base.layers.open(this.skeleton.on[event.type]); + } else { + satus.render(this.skeleton.on[event.type], this.base); + } + }); + } else if (type === 'string') { + element.addEventListener(selector, function () { + var match = this.skeleton.on[event.type].match(/(["'`].+["'`]|[^.()]+)/g), + target = this.base; + + for (var i = 0, l = match.length; i < l; i++) { + var key = match[i]; + + if (target.skeleton[key]) { + target = target.skeleton[key]; + } else { + if (typeof target[key] === 'function') { + target[key](); + } else { + target = target[key]; + } + } + + if (target.rendered) { + target = target.rendered; + } + } + }); + } + } + } +}; + + +/*-------------------------------------------------------------- +# STYLE +--------------------------------------------------------------*/ + +satus.style = function (component, object) { + for (var key in object) { + component.style[key] = object[key]; + } +}; + + +/*-------------------------------------------------------------- +# SEARCH +--------------------------------------------------------------*/ + +satus.search = function (query, object, callback) { + var elements = ['switch', 'select', 'slider', 'shortcut', 'radio', 'color-picker'], + threads = 0, + results = {}; + + query = query.toLowerCase(); + + function parse(items, parent) { + threads++; + + for (var key in items) { + if (key !== 'rendered' && key !== 'base' && key !== 'parent') { + var item = items[key]; + + if (elements.indexOf(item.component) !== -1 && key.indexOf(query) !== -1) { + results[key] = Object.assign({}, item); + } + + if (typeof item === 'object') { + parse(item, items); + } + } + } + + threads--; + + if (threads === 0) { + callback(results); + } + } + + parse(object); +}; + + +/*-------------------------------------------------------------- +# PARENTS +--------------------------------------------------------------*/ + +satus.parents = function (object, components_only) { + function parse(items, parent) { + for (var key in items) { + if (key !== 'rendered' && key !== 'base' && key !== 'parent') { + var item = items[key]; + + if (components_only !== true || item.component) { + item.parent = items; + } + + if (typeof item === 'object' && item.component !== 'shortcut') { + parse(item, items); + } + } + } + } + + parse(object); +}; + + +/*-------------------------------------------------------------- +# TEXT +--------------------------------------------------------------*/ + +satus.text = function (component, value) { + if (typeof value === 'function') { + value = value(); + } + + if (value) { + component.appendChild(document.createTextNode(this.locale.get(value))); + } +}; + + +/*-------------------------------------------------------------- +# DECRYPTION +--------------------------------------------------------------*/ + +satus.decrypt = async function (text, password) { + var iv = text.slice(0, 24).match(/.{2}/g).map(byte => parseInt(byte, 16)), + algorithm = { + name: 'AES-GCM', + iv: new Uint8Array(iv) + }; + + try { + var data = new TextDecoder().decode(await crypto.subtle.decrypt( + algorithm, + await crypto.subtle.importKey( + 'raw', + await crypto.subtle.digest('SHA-256', new TextEncoder().encode(password)), + algorithm, + false, ['decrypt'] + ), + new Uint8Array(atob(text.slice(24)).match(/[\s\S]/g).map(ch => ch.charCodeAt(0))) + )); + } catch (err) { + return false; + } + + return data; +}; + + +/*-------------------------------------------------------------- +# ENCRYPTION +--------------------------------------------------------------*/ + +satus.encrypt = async function (text, password) { + var iv = crypto.getRandomValues(new Uint8Array(12)), + algorithm = { + name: 'AES-GCM', + iv: iv + }; + + return Array.from(iv).map(b => ('00' + b.toString(16)).slice(-2)).join('') + btoa(Array.from(new Uint8Array(await crypto.subtle.encrypt( + algorithm, + await crypto.subtle.importKey('raw', await crypto.subtle.digest('SHA-256', new TextEncoder().encode(password)), algorithm, false, ['encrypt']), + new TextEncoder().encode(text) + ))).map(byte => String.fromCharCode(byte)).join('')); +}; + + +/*-------------------------------------------------------------- +# IS +--------------------------------------------------------------*/ + +satus.isArray = function (array) { + if (Array.isArray(array)) { + return true; + } else { + return false; + } +}; + +satus.isNumber = function (number) { + if (typeof number === 'number' && isNaN(number) === false) { + return true; + } else { + return false; + } +}; + + +/*-------------------------------------------------------------- +# INDEX OF +--------------------------------------------------------------*/ + +satus.indexOf = function (child, parent) { + var index = 0; + + if (satus.isArray(parent)) { + index = parent.indexOf(child); + } else { + while ((child = child.previousElementSibling)) { + index++; + } + } + + return index; +}; + + +/*-------------------------------------------------------------- +# TO INDEX +--------------------------------------------------------------*/ + +satus.toIndex = function (index, child, parent) { + if (satus.isArray(parent)) { + parent.splice(index, 0, parent.splice(satus.indexOf(child, parent), 1)[0]) + } +}; + + +/*-------------------------------------------------------------- +# CLONE +--------------------------------------------------------------*/ + +satus.clone = function (item) { + var clone = item.cloneNode(true), + parent_css = window.getComputedStyle(item.parentNode), + css = window.getComputedStyle(item), + style = ''; + + for (var i = 0, l = css.length; i < l; i++) { + var property = css[i], + value = css.getPropertyValue(property); + + if (property === 'background-color') { + value = parent_css.getPropertyValue('background-color'); + } + + if (['box-shadow', 'left', 'top', 'bottom', 'right', 'opacity'].indexOf(property) === -1) { + style += property + ':' + value + ';'; + } + } + + + clone.setAttribute('style', style); + + return clone; +}; + + +/*-------------------------------------------------------------- +# REMOVE +--------------------------------------------------------------*/ + +satus.remove = function (child, parent) { + if (satus.isArray(parent)) { + parent.splice(satus.indexOf(child, parent), 1); + } +}; + + +/*-------------------------------------------------------------- +# RENDER +--------------------------------------------------------------*/ + +satus.render = function (skeleton, container, skip, property) { + var component; + + if (skeleton.hasOwnProperty('component') && skip !== true) { + var name = skeleton.component, + camelized_name = this.camelize(name); + + if (skeleton.on && skeleton.on.beforerender) { + skeleton.on.beforerender(skeleton); + } + + if (this.components[camelized_name]) { + component = this.components[camelized_name](skeleton); + + if (this.isset(component.inner) === false) { + component.inner = component; + } + } else if (name === 'svg' || container && container.is_svg) { + component = document.createElementNS('http://www.w3.org/2000/svg', name); + + component.is_svg = true; + + component.inner = component; + } else { + component = document.createElement(skeleton.component); + + component.inner = component; + } + + if (component.inner.hasOwnProperty('base') === false && container) { + component.inner.base = container.base; + } + + if (component.inner.base && name === 'layers') { + component.inner.base.layers = component; + } + + skeleton.rendered = component; + component.skeleton = skeleton; + + component.className = (component.className + ' satus-' + skeleton.component).trim(); + + if (skeleton.variant) { + component.className += ' satus-' + skeleton.component + '--' + skeleton.variant; + } + + this.append(component, container); + + container = component.inner || component; + + this.class(component, skeleton.class); + this.style(component, skeleton.style); + this.attr(component, skeleton.attr); + this.data(component, skeleton.data); + this.properties(component, skeleton.properties); + this.on(component, skeleton.on); + this.text(container, skeleton.text); + + if (component.hasOwnProperty('storage') === false && skeleton.storage !== false) { + component.storage = skeleton.storage || property || false; + } + + if (component.hasOwnProperty('storageValue') === false) { + if (component.storage !== false) { + component.storageValue = satus.storage.get(component.storage); + } + + if (skeleton.hasOwnProperty('value') && component.storageValue === undefined) { + component.storageValue = skeleton.value; + } + } + + component.storageChange = function () { + if (this.storage) { + var key = this.storage; + + if (typeof key === 'function') { + key = key(); + } + + satus.storage.set(key, this.storageValue); + } + + this.dispatchEvent(new CustomEvent('change')); + }; + + component.dispatchEvent(new CustomEvent('render')); + + if (skeleton.autofocus === true) { + component.focus(); + } + + if (this.events.hasOwnProperty('render')) { + for (var i = 0, l = this.events['render'].length; i < l; i++) { + this.events['render'][i](component, skeleton); + } + } + } + + if (!component || component.render_children !== false) { + for (var key in skeleton) { + if (key !== 'parent' && skeleton[key] && skeleton[key].hasOwnProperty('component')) { + skeleton[key].parent = skeleton; + + this.render(skeleton[key], container, false, key); + } + } + } + + return component; +}; +/*-------------------------------------------------------------- +>>> COLOR PICKER +--------------------------------------------------------------*/ + +satus.components.colorPicker = function (skeleton) { + var component = document.createElement('button'), + component_label = document.createElement('span'), + component_value = document.createElement('span'); + + component.inner = component_label; + component.valueElement = component_value; + + component.className = 'satus-button'; + component_value.className = 'satus-color-picker__value'; + + component.appendChild(component_label); + component.appendChild(component_value); + + component.addEventListener('click', function () { + var rgb = this.rgb, + hsl = satus.color.rgbToHsl(rgb), + s = hsl[1] / 100, + l = hsl[2] / 100; + + s *= l < .5 ? l : 1 - l; + + var v = l + s; + + s = 2 * s / (l + s); + + satus.render({ + component: 'modal', + variant: 'color-picker', + value: hsl, + parent: this, + + palette: { + component: 'div', + class: 'satus-color-picker__palette', + style: { + 'backgroundColor': 'hsl(' + hsl[0] + 'deg, 100%, 50%)' + }, + on: { + mousedown: function () { + var palette = this, + rect = this.getBoundingClientRect(), + cursor = this.children[0]; + + function mousemove(event) { + var hsl = palette.skeleton.parent.value, + x = event.clientX - rect.left, + y = event.clientY - rect.top, + s; + + x = Math.min(Math.max(x, 0), rect.width) / (rect.width / 100); + y = Math.min(Math.max(y, 0), rect.height) / (rect.height / 100); + + var v = 100 - y, + l = (2 - x / 100) * v / 2; + + hsl[1] = x * v / (l < 50 ? l * 2 : 200 - l * 2); + hsl[2] = l; + + cursor.style.left = x + '%'; + cursor.style.top = y + '%'; + + palette.nextSibling.children[0].style.backgroundColor = 'hsl(' + hsl[0] + 'deg,' + hsl[1] + '%, ' + hsl[2] + '%)'; + + event.preventDefault(); + } + + function mouseup() { + window.removeEventListener('mousemove', mousemove); + window.removeEventListener('mouseup', mouseup); + } + + window.addEventListener('mousemove', mousemove); + window.addEventListener('mouseup', mouseup); + } + }, + + cursor: { + component: 'div', + class: 'satus-color-picker__cursor', + style: { + 'left': s * 100 + '%', + 'top': 100 - v * 100 + '%' + } + } + }, + section: { + component: 'section', + variant: 'color', + + color: { + component: 'div', + class: 'satus-color-picker__color', + style: { + 'backgroundColor': 'rgb(' + this.rgb.join(',') + ')' + } + }, + hue: { + component: 'slider', + class: 'satus-color-picker__hue', + storage: false, + value: hsl[0], + max: 360, + on: { + change: function () { + var modal = this.skeleton.parent.parent, + hsl = modal.value; + + hsl[0] = this.values[0]; + + this.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg,' + hsl[1] + '%, ' + hsl[2] + '%)'; + this.parentNode.previousSibling.style.backgroundColor = 'hsl(' + hsl[0] + 'deg, 100%, 50%)'; + } + } + } + }, + actions: { + component: 'section', + variant: 'actions', + + reset: { + component: 'button', + text: 'reset', + on: { + click: function () { + var modal = this.skeleton.parent.parent, + component = modal.parent; + + component.rgb = component.skeleton.value; + + component.storageValue = component.rgb; + component.storageChange(); + + component.valueElement.style.backgroundColor = 'rgb(' + component.rgb.join(',') + ')'; + + modal.rendered.close(); + } + } + }, + cancel: { + component: 'button', + text: 'cancel', + on: { + click: function () { + this.skeleton.parent.parent.rendered.close(); + } + } + }, + ok: { + component: 'button', + text: 'OK', + on: { + click: function () { + var modal = this.skeleton.parent.parent, + component = modal.parent; + + component.rgb = satus.color.hslToRgb(modal.value); + + component.storageValue = component.rgb; + component.storageChange(); + + component.valueElement.style.backgroundColor = 'rgb(' + component.rgb.join(',') + ')'; + + modal.rendered.close(); + } + } + } + } + }); + }); + + component.addEventListener('render', function () { + component.rgb = this.storageValue || [0, 100, 50]; + + component_value.style.backgroundColor = 'rgb(' + component.rgb.join(',') + ')'; + }); + + return component; +}; +/*-------------------------------------------------------------- +>>> TABS +--------------------------------------------------------------*/ + +satus.components.tabs = function (skeleton) { + var component = document.createElement('div'), + content = document.createElement('div'), + selection = document.createElement('div'); + + content.className = 'satus-tabs__content'; + selection.className = 'satus-tabs__selection'; + selection.style.width = 100 / skeleton.items.length + '%'; + + content.appendChild(selection); + + component.selection = selection; + + for (var i = 0, l = skeleton.items.length; i < l; i++) { + var item = skeleton.items[i], + button = document.createElement('button'); + + button.className = 'satus-tabs__button'; + button.value = item; + button.style.width = 100 / l + '%'; + + satus.text(button, item); + + button.addEventListener('click', function () { + var component = this.parentNode.parentNode; + + component.selection.style.left = 100 / (this.parentNode.children.length - 1) * (satus.indexOf(this) - 1) + '%'; + + component.storageValue = this.value; + component.storageChange(); + }); + + if (skeleton.value === item) { + selection.style.left = i * 50 + '%'; + } + + content.appendChild(button); + } + + component.appendChild(content); + + component.addEventListener('render', function () { + var index = satus.indexOf(this.storageValue, this.skeleton.items); + + if (index === -1) { + index = 0; + } + + this.selection.style.left = 100 / this.skeleton.items.length * index + '%'; + }); + + return component; +}; +/*-------------------------------------------------------------- +>>> MENUBAR +--------------------------------------------------------------*/ + +satus.components.menubar = function (skeleton) { + var component = document.createElement('ul'); + + if (skeleton.items) { + for (var i = 0, l = skeleton.items.length; i < l; i++) { + var item = skeleton.items[i], + li = document.createElement('li'); + + if (Array.isArray(item) === true) { + var ul = document.createElement('ul'); + + satus.render(item[0], li); + + for (var j = 1, k = item.length; j < k; j++) { + var li2 = document.createElement('li'); + + satus.render(item[j], li2); + + ul.appendChild(li2); + } + + li.appendChild(ul); + } else { + satus.render(item, li); + } + + component.appendChild(li); + } + } + + return component; +}; +/*-------------------------------------------------------------- +>>> RADIO +--------------------------------------------------------------*/ + +satus.components.radio = function (skeleton) { + var component = document.createElement('label'), + content = document.createElement('span'), + radio = document.createElement('input'); + + component.inner = content; + + radio.type = 'radio'; + + if (skeleton.group) { + component.storage = skeleton.group; + radio.name = skeleton.group; + } + + if (skeleton.value) { + radio.value = skeleton.value; + } + + component.addEventListener('render', function () { + this.storageValue = satus.storage.get(this.storage); + + if (satus.isset(this.storageValue)) { + radio.checked = this.storageValue === skeleton.value; + } else if (skeleton.checked) { + radio.checked = true; + } + }); + + radio.addEventListener('change', function () { + component.storageValue = this.value; + component.storageChange(); + }); + + component.appendChild(content); + component.appendChild(radio); + + return component; +}; +/*-------------------------------------------------------------- +>>> LIST +--------------------------------------------------------------*/ + +satus.components.list = function (skeleton) { + var ul = document.createElement('ul'); + + for (var i = 0, l = skeleton.items.length; i < l; i++) { + var li = document.createElement('li'), + item = skeleton.items[i]; + + li.className = 'satus-list__item'; + + for (var j = 0, k = item.length; j < k; j++) { + var child = item[j]; + + if (typeof child === 'string') { + var span = document.createElement('span'); + + span.textContent = satus.locale.get(child); + + li.appendChild(span); + } else { + satus.render(child, li); + } + } + + ul.appendChild(li); + } + + return ul; +}; +/*-------------------------------------------------------------- +>>> MODAL +--------------------------------------------------------------*/ + +satus.components.modal = function (skeleton) { + var component = document.createElement('div'), + scrim = document.createElement('div'), + surface = document.createElement('div'); + + component.inner = surface; + + scrim.className = 'satus-modal__scrim'; + surface.className = 'satus-modal__surface'; + + component.close = function () { + var component = this, + component_surface = this.children[1]; + + this.classList.add('satus-modal--closing'); + + setTimeout(function () { + component.remove(); + + component.dispatchEvent(new CustomEvent('close')); + }, satus.getAnimationDuration(component_surface)); + }; + + scrim.addEventListener('click', function () { + this.parentNode.close(); + }); + + component.appendChild(scrim); + component.appendChild(surface); + + return component; +}; +/*-------------------------------------------------------------- +>>> CHECKBOX +--------------------------------------------------------------*/ + +satus.components.checkbox = function (skeleton) { + var component = document.createElement('button'), + content = document.createElement('span'); + + component.inner = content; + + content.className = 'satus-checkbox__content'; + + component.appendChild(content); + + component.addEventListener('click', function () { + if (this.dataset.value === 'true') { + this.storageValue = false; + this.dataset.value = 'false'; + } else { + this.storageValue = true; + this.dataset.value = 'true'; + } + + this.storageChange(); + }); + + component.addEventListener('render', function () { + this.dataset.value = this.storageValue; + }); + + return component; +}; +/*-------------------------------------------------------------- +>>> SWITCH +--------------------------------------------------------------*/ + +satus.components.switch = function (skeleton) { + var component = document.createElement('button'), + component_content = document.createElement('span'), + component_thumb = document.createElement('i'); + + component.inner = component_content; + + component_content.className = 'satus-switch__content'; + + component.addEventListener('click', function () { + if (this.dataset.value === 'true') { + this.storageValue = false; + this.dataset.value = 'false'; + } else { + this.storageValue = true; + this.dataset.value = 'true'; + } + + this.storageChange(); + }); + + component.addEventListener('render', function () { + this.dataset.value = this.storageValue; + }); + + component.appendChild(component_content); + component.appendChild(component_thumb); + + return component; +}; +/*-------------------------------------------------------------- +>>> SLIDER +--------------------------------------------------------------*/ + +satus.components.slider = function (skeleton) { + var component = document.createElement('div'), + content = document.createElement('div'), + container = document.createElement('div'), + track = document.createElement('div'), + track_fill = document.createElement('div'); + + container.className = 'satus-slider__container'; + track.className = 'satus-slider__track'; + track_fill.className = 'satus-slider__track-fill'; + + component.min = skeleton.min || 0; + component.max = skeleton.max || 1; + component.step = (skeleton.step || 1); + component.percent = 100 / ((component.max - component.min) / component.step); + component.precision = String(component.step).replace(/[0-9]./, '').length; + + component.container = container; + component.track = track_fill; + component.handles = []; + component.inner = content; + + component.toPercent = function (number) { + return number / this.step * this.percent + '%'; + }; + + component.createHandle = function (index) { + var handle = document.createElement('div'); + + handle.className = 'satus-slider__handle'; + handle.handleIndex = index; + handle.tabIndex = 0; + + this.handles.push(handle); + + this.container.appendChild(handle); + }; + + component.update = function () { + if (this.values.length > 1) { + var min = Math.min.apply(null, this.values) - this.min, + max = Math.max.apply(null, this.values) - this.min; + + this.track.style.left = this.toPercent(min); + this.track.style.width = this.toPercent(max - min); + + for (var i = 0, l = this.handles.length; i < l; i++) { + var handle = this.handles[i], + value = this.values[i]; + + handle.style.left = this.toPercent(value - this.min); + handle.dataset.value = value; + } + } else { + var value = this.values[0]; + + this.track.style.width = this.toPercent(value - this.min); + this.handles[0].style.left = this.toPercent(value - this.min); + this.handles[0].dataset.value = value; + } + }; + + component.appendChild(content); + track.appendChild(track_fill); + container.appendChild(track); + component.appendChild(container); + + component.addEventListener('keydown', function (event) { + var code = event.keyCode; + + console.log(code); + }); + + component.addEventListener('render', function () { + var value = this.storageValue; + + if (satus.isArray(value)) { + this.values = value; + } else if (satus.isNumber(value)) { + this.values = [value]; + } else { + this.values = this.skeleton.values || [satus.isset(this.skeleton.value) ? this.skeleton.value : 1]; + } + + for (var i = 0, l = this.values.length; i < l; i++) { + this.createHandle(i); + } + + this.update(); + }); + + container.addEventListener('mousedown', function (event) { + if (event.button === 0) { + var component = this.parentNode, + rect = this.getBoundingClientRect(), + cursor_x = event.clientX - rect.left, + percent = cursor_x / rect.width * 100, + steps = percent / component.percent * component.step + component.min, + closest_value = component.values.indexOf(component.values.reduce(function(previous, current, index) { + return Math.abs(current - steps) < Math.abs(previous - steps) ? current : previous; + })), + handle_index = component.handles[closest_value].handleIndex; + + setTimeout(function () { + component.handles[closest_value].focus(); + }); + + function update(event) { + var cursor_x = Math.min(Math.max(event.clientX - rect.left, 0), rect.width), + percent = cursor_x / rect.width * 100, + value = percent / component.percent * component.step + component.min; + + value = (Math.round(value / component.step) * component.step); + + value = Number(value.toFixed(component.precision)); + + if (component.values[handle_index] !== value) { + component.values[handle_index] = value; + + component.storageValue = component.values.length === 1 ? component.values[0] : component.values; + component.value = component.storageValue; + + component.storageChange(); + } + + component.update(); + } + + function mousemove(event) { + update(event); + } + + function mouseup(event) { + window.removeEventListener('mousemove', mousemove); + window.removeEventListener('mouseup', mouseup); + }; + + window.addEventListener('mousemove', mousemove); + window.addEventListener('mouseup', mouseup); + + update(event); + + return true; + } + }); + + return component; +}; +/*-------------------------------------------------------------- +>>> SHORTCUT +--------------------------------------------------------------*/ + +satus.components.shortcut = function (skeleton) { + var component = document.createElement('button'), + content = document.createElement('span'), + value = document.createElement('div'); + + component.inner = content; + + component.className = 'satus-button'; + value.className = 'satus-shortcut__value'; + + component.update = function () { + var object = satus.storage.get(this.skeleton.storage) || this.skeleton.value || {}, + array = []; + + if (object.shift) { + array.push('Shift'); + } + + if (object.ctrl) { + array.push('Ctrl'); + } + + if (object.alt) { + array.push('Alt'); + } + + if (typeof object.keys === 'object') { + for (var key in object.keys) { + var char = object.keys[key].key || object.keys[key].code; + + if (key === 32) { + char = 'space'; + } + + array.push(char); + } + } + + this.valueElement.textContent = array.join(' + '); + }; + + component.render = function () { + var self = this, + children = this.primary.children; + + satus.empty(this.primary); + + function createElement(name) { + var element = document.createElement('div'); + + element.className = 'satus-shortcut__' + name; + + self.primary.appendChild(element); + + return element; + } + + if (this.data.alt) { + createElement('key').textContent = 'Alt'; + } + + if (this.data.ctrl) { + if (children.length && children[children.length - 1].className.indexOf('key') !== -1) { + createElement('plus'); + } + + createElement('key').textContent = 'Ctrl'; + } + + if (this.data.shift) { + if (children.length && children[children.length - 1].className.indexOf('key') !== -1) { + createElement('plus'); + } + + createElement('key').textContent = 'Shift'; + } + + for (var code in this.data.keys) { + if (children.length && children[children.length - 1].className.indexOf('key') !== -1) { + createElement('plus'); + } + + createElement('key').textContent = this.data.keys[code].key.toUpperCase(); + } + + if (this.data.wheel) { + if (children.length && children[children.length - 1].className.indexOf('key') !== -1) { + createElement('plus'); + } + + var mouse = createElement('mouse'), + div = document.createElement('div'); + + mouse.appendChild(div); + + mouse.className += ' ' + (this.data.wheel > 0); + } + }; + + component.valueElement = value; + + component.appendChild(content); + component.appendChild(value); + + component.keydown = function (event) { + event.preventDefault(); + event.stopPropagation(); + + component.data = { + alt: event.altKey, + ctrl: event.ctrlKey, + shift: event.shiftKey, + keys: {} + }; + + if (['control', 'alt', 'altgraph', 'shift'].indexOf(event.key.toLowerCase()) === -1) { + component.data.keys[event.keyCode] = { + code: event.code, + key: event.key + }; + } + + component.data.wheel = 0; + + component.render(); + + return false; + }; + + component.mousewheel = function (event) { + event.stopPropagation(); + + if ( + ( + component.data.wheel === 0 && + ( + Object.keys(component.data.keys).length === 0 && + component.data.alt === false && + component.data.ctrl === false && + component.data.shift === false + ) + ) || + component.data.wheel < 0 && event.deltaY > 0 || + component.data.wheel > 0 && event.deltaY < 0) { + component.data = { + alt: false, + ctrl: false, + shift: false, + keys: {} + }; + } + + component.data.wheel = event.deltaY < 0 ? -1 : 1; + + component.render(); + + return false; + }; + + component.addEventListener('render', function () { + this.data = this.storageValue || { + alt: false, + ctrl: false, + shift: false, + keys: {}, + wheel: 0 + }; + + this.update(); + }); + + component.addEventListener('click', function () { + satus.render({ + component: 'modal', + on: { + close: function () { + window.removeEventListener('keydown', component.keydown); + window.removeEventListener('mousewheel', component.mousewheel); + } + }, + + primary: { + component: 'div', + class: 'satus-shortcut__primary', + on: { + render: function () { + component.primary = this; + + component.render(); + } + } + }, + actions: { + component: 'section', + variant: 'actions', + + reset: { + component: 'button', + text: 'reset', + on: { + click: function () { + component.data = component.skeleton.value; + + component.update(); + + satus.storage.set(skeleton.storage, false); + + this.parentNode.parentNode.parentNode.close(); + + window.removeEventListener('keydown', component.keydown); + window.removeEventListener('mousewheel', component.mousewheel); + } + } + }, + cancel: { + component: 'button', + text: 'cancel', + on: { + click: function () { + component.data = satus.storage.get(component.storage) || component.skeleton.value; + + component.update(); + + this.parentNode.parentNode.parentNode.close(); + + window.removeEventListener('keydown', component.keydown); + window.removeEventListener('mousewheel', component.mousewheel); + } + } + }, + save: { + component: 'button', + text: 'save', + on: { + click: function () { + component.storageValue = component.data; + + component.storageChange(); + + component.update(); + + this.parentNode.parentNode.parentNode.close(); + + window.removeEventListener('keydown', component.keydown); + window.removeEventListener('mousewheel', component.mousewheel); + } + } + } + } + }); + + window.addEventListener('keydown', this.keydown); + window.addEventListener('mousewheel', this.mousewheel); + }); + + return component; +}; +/*-------------------------------------------------------------- +>>> BASE +--------------------------------------------------------------*/ + +satus.components.base = function (skeleton) { + var component = document.createElement('div'); + + component.base = component; + + return component; +}; +/*-------------------------------------------------------------- +>>> TEXT FIELD +--------------------------------------------------------------*/ + +satus.components.textField = function (skeleton) { + var component = document.createElement('div'), + pre = document.createElement('pre'), + input = document.createElement('textarea'), + hidden_text = document.createElement('span'), + text = document.createElement('span'), + selection = document.createElement('div'), + cursor = document.createElement('div'); + + input.className = 'satus-text-field__input'; + pre.className = 'satus-text-field__pre'; + hidden_text.className = 'satus-text-field__hidden-text'; + text.className = 'satus-text-field__text'; + selection.className = 'satus-text-field__selection'; + cursor.className = 'satus-text-field__cursor'; + + component.inputElement = input; + component.textElement = text; + component.languages = { + regex: function (component) { + 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, + 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, + char_class_parts = /^(\[\^?)(]?(?:[^\\\]]+|\\[\S\s]?)*)(]?)$/, + quantifier = /^(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??$/, + matches = component.inputElement.value.match(regex_token); + + function create(type, string) { + var span = document.createElement('span'); + + span.className = type; + span.textContent = string; + + component.textElement.appendChild(span); + } + + for (var i = 0, l = matches.length; i < l; i++) { + var match = matches[i]; + + if (match[0] === '[') { + create('character-class', match); + } else if (match[0] === '(') { + create('group', match); + } else if (match[0] === ')') { + create('group', match); + } else if (match[0] === '\\' || match === '^') { + create('anchor', match); + } else if (quantifier.test(match)) { + create('quantifier', match); + } else if (match === '|' || match === '.') { + create('metasequence', match); + } else { + create('text', match); + } + } + } + }; + component._syntax = skeleton.syntax; + + Object.defineProperty(component, 'value', { + get: function () { + return this.inputElement.value; + }, + set: function (value) { + var input = this.inputElement; + + input.value = value; + + input.updateValue(); + input.updateCursor(); + } + }); + + Object.defineProperty(component, 'syntax', { + get: function () { + return this._syntax; + }, + set: function (value) { + var input = this.inputElement; + + this._syntax = value; + + input.updateValue(); + input.updateCursor(); + } + }); + + input.rows = skeleton.rows || 1; + input.autocapitalize = 'none'; + input.autocomplete = 'off'; + input.autocorrect = 'off'; + input.spellcheck = false; + input.autofocus = true; + input.textElement = text; + input.hiddenTextElement = hidden_text; + input.selectionElement = selection; + input.cursorElement = cursor; + + input.updateValue = function () { + var component = this.parentNode.parentNode; + + for (var i = this.textElement.childNodes.length - 1; i > -1; i--) { + this.textElement.childNodes[i].remove(); + } + + if (this.value.length > 0) { + if (component.languages[component._syntax]) { + component.languages[component._syntax](component); + } else { + this.textElement.textContent = this.value; + } + } + }; + + input.updateCursor = function () { + var cursor = this.cursorElement, + selection = this.selectionElement, + hidden_text = this.hiddenTextElement, + start = this.selectionStart, + end = this.selectionEnd; + + cursor.style.animation = 'none'; + + if (start === end) { + selection.setAttribute('disabled', ''); + } else { + selection.removeAttribute('disabled'); + + hidden_text.textContent = this.value.substring(0, start); + + selection.style.left = hidden_text.offsetWidth - this.scrollLeft + 'px'; + + hidden_text.textContent = this.value.substring(start, end); + + selection.style.width = hidden_text.offsetWidth + 'px'; + } + + if (this.selectionDirection === 'forward') { + hidden_text.textContent = this.value.substring(0, end); + } else { + hidden_text.textContent = this.value.substring(0, start); + } + + cursor.style.left = hidden_text.offsetWidth - this.scrollLeft + 'px'; + + cursor.style.animation = ''; + + hidden_text.textContent = ''; + }; + + input.addEventListener('keydown', function () { + var self = this; + + setTimeout(function () { + var component = self.parentNode.parentNode; + + component.storageValue = self.value; + component.storageChange(); + + self.updateValue(); + self.updateCursor(); + }); + }); + + input.addEventListener('scroll', function (event) { + this.textElement.style.left = -this.scrollLeft + 'px'; + }); + + document.addEventListener('selectionchange', function () { + input.updateCursor(); + }); + + selection.setAttribute('disabled', ''); + + pre.appendChild(input); + pre.appendChild(hidden_text); + pre.appendChild(text); + pre.appendChild(selection); + pre.appendChild(cursor); + component.appendChild(pre); + + component.addEventListener('render', function () { + input.value = this.storageValue; + + this.inputElement.updateValue(); + this.inputElement.updateCursor(); + }); + + return component; +}; +/*-------------------------------------------------------------- +>>> ALERT +--------------------------------------------------------------*/ + +satus.components.alert = function (skeleton) { + var component = document.createElement('div'); + + return component; +}; +/*-------------------------------------------------------------- +>>> LAYERS +--------------------------------------------------------------*/ + +satus.components.layers = function (skeleton) { + var component = document.createElement('div'); + + component.path = [skeleton]; + + component.back = function () { + if (this.path.length > 1) { + this.path.pop(); + + this.open(); + } + }; + + component.open = function (skeleton) { + var layer = document.createElement('div'); + + if (skeleton) { + this.path.push(skeleton); + } else { + skeleton = this.path[this.path.length - 1]; + } + + layer.className = 'satus-layer'; + + layer.skeleton = skeleton; + layer.base = this.base; + + satus.render(skeleton, layer, skeleton.component === 'layers'); + + satus.empty(this); + + this.appendChild(layer); + + this.dispatchEvent(new Event('open')); + }; + + component.update = function () { + var layer = this.querySelector('.satus-layer'); + + satus.empty(layer); + + satus.render(layer.skeleton, layer); + }; + + component.render_children = false; + + component.addEventListener('render', function () { + this.open(); + }); + + return component; +}; +/*-------------------------------------------------------------- +>>> DIVIDER +--------------------------------------------------------------*/ + +satus.components.divider = function () { + var component = document.createElement('div'); + + return component; +}; +/*-------------------------------------------------------------- +>>> INPUT +--------------------------------------------------------------*/ + +satus.components.input = function (skeleton) { + var component = document.createElement('input'); + + if (skeleton.attr) { + var key = skeleton.attr.name || skeleton.storage, + value; + + if (satus.isset(satus.storage.get(key))) { + value = satus.storage.get(key); + } else { + value = skeleton.value; + } + + if (skeleton.attr.type === 'radio') { + component.checked = value === skeleton.attr.value || skeleton.value; + } else if (satus.isset(value)) { + component.value = value; + } + + component.addEventListener('change', function () { + var key = this.skeleton.attr.name || this.skeleton.storage; + + satus.storage.set(key, this.value); + }); + } else { + var key = skeleton.name || skeleton.storage, + value; + + component.type = skeleton.type; + + if (satus.isset(satus.storage.get(key))) { + value = satus.storage.get(key); + } else { + value = skeleton.value; + } + + if (skeleton.type === 'radio') { + component.checked = value === skeleton.value || skeleton.value; + } else if (satus.isset(value)) { + component.value = value; + } + + component.addEventListener('change', function () { + var key = this.skeleton.name || this.skeleton.storage; + + satus.storage.set(key, this.value); + }); + } + + return component; +}; +/*-------------------------------------------------------------- +>>> SELECT +--------------------------------------------------------------*/ + +satus.components.select = function (skeleton) { + var component = document.createElement('div'), + component_content = document.createElement('span'), + component_value = document.createElement('span'), + select = document.createElement('select'); + + component_content.className = 'satus-select__content'; + component_value.className = 'satus-select__value'; + + for (var i = 0, l = skeleton.options.length; i < l; i++) { + var option = document.createElement('option'); + + option.value = skeleton.options[i].value; + + satus.text(option, skeleton.options[i].text); + + select.appendChild(option); + } + + component.selectElement = select; + select.valueElement = component_value; + + select.addEventListener('change', function () { + satus.empty(this.valueElement); + + satus.text(this.valueElement, this.options[this.selectedIndex].text); + + this.parentNode.storageValue = this.value; + + this.parentNode.storageChange(); + }); + + component.appendChild(component_content); + component.appendChild(component_value); + component.appendChild(select); + + component.addEventListener('render', function () { + select.value = this.storageValue || this.skeleton.options[0].value; + + satus.text(select.valueElement, select.options[select.selectedIndex].text); + }); + + component.inner = component_content; + + return component; +}; +/*-------------------------------------------------------------- +>>> COLOR: +---------------------------------------------------------------- +# RGB to HSL +# HUE to RGB +# HSL to RGB +--------------------------------------------------------------*/ + +satus.color = {}; + + +/*-------------------------------------------------------------- +# RGB TO HSL +--------------------------------------------------------------*/ + +satus.color.rgbToHsl = function (array) { + var r = array[0] / 255, + g = array[1] / 255, + b = array[2] / 255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + h = 0, + s = 0, + l = (min + max) / 2; + + if (min === max) { + h = 0; + s = 0; + } else { + var delta = max - min; + + s = l <= 0.5 ? delta / (max + min) : delta / (2 - max - min); + + if (max === r) { + h = (g - b) / delta + (g < b ? 6 : 0); + } else if (max === g) { + h = (b - r) / delta + 2; + } else if (max === b) { + h = (r - g) / delta + 4; + } + + h /= 6; + } + + h *= 360; + s *= 100; + l *= 100; + + if (array.length === 3) { + return [h, s, l]; + } else { + return [h, s, l, array[3]]; + } +}; + + +/*-------------------------------------------------------------- +# HUE TO RGB +--------------------------------------------------------------*/ + +satus.color.hueToRgb = function (array) { + var t1 = array[0], + t2 = array[1], + hue = array[2]; + + if (hue < 0) { + hue += 6; + } + + if (hue >= 6) { + hue -= 6; + } + + if (hue < 1) { + return (t2 - t1) * hue + t1; + } else if (hue < 3) { + return t2; + } else if (hue < 4) { + return (t2 - t1) * (4 - hue) + t1; + } else { + return t1; + } +}; + + +/*-------------------------------------------------------------- +# HSL TO RGB +--------------------------------------------------------------*/ + +satus.color.hslToRgb = function (array) { + var h = array[0] / 360, + s = array[1] / 100, + l = array[2] / 100, + r, g, b; + + if (s == 0) { + r = g = b = l; + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + } + + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + + return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; +}; +/*-------------------------------------------------------------- +>>> USER +--------------------------------------------------------------*/ + +satus.user = function () { + /*-------------------------------------------------------------- + 1.0 VARIABLES + --------------------------------------------------------------*/ + + var user_agent = navigator.userAgent, + random_cookie = 'ta{t`nX6cMXK,Wsc', + video = document.createElement('video'), + video_formats = { + ogg: 'video/ogg; codecs="theora"', + h264: 'video/mp4; codecs="avc1.42E01E"', + webm: 'video/webm; codecs="vp8, vorbis"', + vp9: 'video/webm; codecs="vp9"', + hls: 'application/x-mpegURL; codecs="avc1.42E01E"' + }, + audio = document.createElement('audio'), + audio_formats = { + mp3: 'audio/mpeg', + mp4: 'audio/mp4', + aif: 'audio/x-aiff' + }, + cvs = document.createElement('canvas'), + ctx = cvs.getContext('webgl'), + data = { + browser: { + audio: null, + cookies: null, + flash: null, + java: null, + languages: null, + name: null, + platform: null, + version: null, + video: null, + webgl: null + }, + os: { + name: null, + type: null + }, + device: { + connection: { + type: null, + speed: null + }, + cores: null, + gpu: null, + max_touch_points: null, + ram: null, + screen: null, + touch: null + } + }; + + + /*-------------------------------------------------------------- + 2.0 SOFTWARE + --------------------------------------------------------------*/ + + /*-------------------------------------------------------------- + 2.1.0 OS + --------------------------------------------------------------*/ + + /*-------------------------------------------------------------- + 2.1.1 NAME + --------------------------------------------------------------*/ + + if (navigator.appVersion.indexOf('Win') !== -1) { + if (navigator.appVersion.match(/(Windows 10.0|Windows NT 10.0)/)) { + data.os.name = 'Windows 10'; + } else if (navigator.appVersion.match(/(Windows 8.1|Windows NT 6.3)/)) { + data.os.name = 'Windows 8.1'; + } else if (navigator.appVersion.match(/(Windows 8|Windows NT 6.2)/)) { + data.os.name = 'Windows 8'; + } else if (navigator.appVersion.match(/(Windows 7|Windows NT 6.1)/)) { + data.os.name = 'Windows 7'; + } else if (navigator.appVersion.match(/(Windows NT 6.0)/)) { + data.os.name = 'Windows Vista'; + } else if (navigator.appVersion.match(/(Windows NT 5.1|Windows XP)/)) { + data.os.name = 'Windows XP'; + } else { + data.os.name = 'Windows'; + } + } else if (navigator.appVersion.indexOf('(iPhone|iPad|iPod)') !== -1) { + data.os.name = 'iOS'; + } else if (navigator.appVersion.indexOf('Mac') !== -1) { + data.os.name = 'macOS'; + } else if (navigator.appVersion.indexOf('Android') !== -1) { + data.os.name = 'Android'; + } else if (navigator.appVersion.indexOf('OpenBSD') !== -1) { + data.os.name = 'OpenBSD'; + } else if (navigator.appVersion.indexOf('SunOS') !== -1) { + data.os.name = 'SunOS'; + } else if (navigator.appVersion.indexOf('Linux') !== -1) { + data.os.name = 'Linux'; + } else if (navigator.appVersion.indexOf('X11') !== -1) { + data.os.name = 'UNIX'; + } + + /*-------------------------------------------------------------- + 2.1.2 TYPE + --------------------------------------------------------------*/ + + if (navigator.appVersion.match(/(Win64|x64|x86_64|WOW64)/)) { + data.os.type = '64-bit'; + } else { + data.os.type = '32-bit'; + } + + + /*-------------------------------------------------------------- + 2.2.0 BROWSER + --------------------------------------------------------------*/ + + /*-------------------------------------------------------------- + 2.2.1 NAME + --------------------------------------------------------------*/ + + if (user_agent.indexOf('Opera') !== -1) { + data.browser.name = 'Opera'; + } else if (user_agent.indexOf('Vivaldi') !== -1) { + data.browser.name = 'Vivaldi'; + } else if (user_agent.indexOf('Edge') !== -1) { + data.browser.name = 'Edge'; + } else if (user_agent.indexOf('Chrome') !== -1) { + data.browser.name = 'Chrome'; + } else if (user_agent.indexOf('Safari') !== -1) { + data.browser.name = 'Safari'; + } else if (user_agent.indexOf('Firefox') !== -1) { + data.browser.name = 'Firefox'; + } else if (user_agent.indexOf('MSIE') !== -1) { + data.browser.name = 'IE'; + } + + + /*-------------------------------------------------------------- + 2.2.2 VERSION + --------------------------------------------------------------*/ + + var browser_version = user_agent.match(new RegExp(data.browser.name + '/([0-9.]+)')); + + if (browser_version[1]) { + data.browser.version = browser_version[1]; + } + + + /*-------------------------------------------------------------- + 2.2.3 PLATFORM + --------------------------------------------------------------*/ + + data.browser.platform = navigator.platform || null; + + + /*-------------------------------------------------------------- + 2.2.4 LANGUAGES + --------------------------------------------------------------*/ + + data.browser.languages = navigator.languages || null; + + + /*-------------------------------------------------------------- + 2.2.5 COOKIES + --------------------------------------------------------------*/ + + if (document.cookie) { + document.cookie = random_cookie; + + if (document.cookie.indexOf(random_cookie) !== -1) { + data.browser.cookies = true; + } + } + + + /*-------------------------------------------------------------- + 2.2.6 FLASH + --------------------------------------------------------------*/ + + try { + if (new ActiveXObject('ShockwaveFlash.ShockwaveFlash')) { + data.browser.flash = true; + } + } catch (e) { + if (navigator.mimeTypes['application/x-shockwave-flash']) { + data.browser.flash = true; + } + } + + + /*-------------------------------------------------------------- + 2.2.7 JAVA + --------------------------------------------------------------*/ + + if (typeof navigator.javaEnabled === 'function' && navigator.javaEnabled()) { + data.browser.java = true; + } + + + /*-------------------------------------------------------------- + 2.2.8 VIDEO FORMATS + --------------------------------------------------------------*/ + + if (typeof video.canPlayType === 'function') { + data.browser.video = {}; + + for (var i in video_formats) { + var can_play_type = video.canPlayType(video_formats[i]); + + if (can_play_type === '') { + data.browser.video[i] = false; + } else { + data.browser.video[i] = can_play_type; + } + } + } + + + /*-------------------------------------------------------------- + 2.2.9 AUDIO FORMATS + --------------------------------------------------------------*/ + + if (typeof audio.canPlayType === 'function') { + data.browser.audio = {}; + + for (var i in audio_formats) { + var can_play_type = audio.canPlayType(audio_formats[i]); + + if (can_play_type == '') { + data.browser.audio[i] = false; + } else { + data.browser.audio[i] = can_play_type; + } + } + } + + + /*-------------------------------------------------------------- + 2.2.10 WEBGL + --------------------------------------------------------------*/ + + if (ctx && ctx instanceof WebGLRenderingContext) { + data.browser.webgl = true; + } + + + /*-------------------------------------------------------------- + 3.0 HARDWARE + --------------------------------------------------------------*/ + + /*-------------------------------------------------------------- + 3.1 SCREEN + --------------------------------------------------------------*/ + + if (screen) { + data.device.screen = screen.width + 'x' + screen.height; + } + + + /*-------------------------------------------------------------- + 3.2 RAM + --------------------------------------------------------------*/ + + if ('deviceMemory' in navigator) { + data.device.ram = navigator.deviceMemory + ' GB'; + } + + + /*-------------------------------------------------------------- + 3.3 GPU + --------------------------------------------------------------*/ + + if ( + ctx && + ctx instanceof WebGLRenderingContext && + 'getParameter' in ctx && + 'getExtension' in ctx + ) { + var info = ctx.getExtension('WEBGL_debug_renderer_info'); + + if (info) { + data.device.gpu = ctx.getParameter(info.UNMASKED_RENDERER_WEBGL); + } + } + + + /*-------------------------------------------------------------- + 3.4 CORES + --------------------------------------------------------------*/ + + if (navigator.hardwareConcurrency) { + data.device.cores = navigator.hardwareConcurrency; + } + + + /*-------------------------------------------------------------- + 3.5 TOUCH + --------------------------------------------------------------*/ + + if ( + window.hasOwnProperty('ontouchstart') || + window.DocumentTouch && document instanceof window.DocumentTouch || + navigator.maxTouchPoints > 0 || + window.navigator.msMaxTouchPoints > 0 + ) { + data.device.touch = true; + data.device.max_touch_points = navigator.maxTouchPoints; + } + + + /*-------------------------------------------------------------- + 3.6 CONNECTION + --------------------------------------------------------------*/ + + if (typeof navigator.connection === 'object') { + data.device.connection.type = navigator.connection.effectiveType || null; + + if (navigator.connection.downlink) { + data.device.connection.speed = navigator.connection.downlink + ' Mbps'; + } + } + + + /*-------------------------------------------------------------- + 4.0 CLEARING + --------------------------------------------------------------*/ + + video.remove(); + audio.remove(); + cvs.remove(); + + + return data; +}; +/*-------------------------------------------------------------- +>>> CONTEXT MENU +--------------------------------------------------------------*/ + +satus.events.add('render', function (component, skeleton) { + if (skeleton.contextMenu) { + component.addEventListener('contextmenu', function (event) { + var x = event.clientX, + y = event.clientY, + modal = satus.render({ + component: 'modal', + variant: 'contextmenu', + parent: skeleton + }); + + if (window.innerWidth - x < 200) { + x = window.innerWidth - 200; + } + + modal.inner.style.left = x + 'px'; + modal.inner.style.top = y + 'px'; + + satus.render(skeleton.contextMenu, modal.inner); + + event.preventDefault(); + event.stopPropagation(); + + return false; + }); + } +}); +/*-------------------------------------------------------------- +>>> EXTENSION STORAGE +--------------------------------------------------------------*/ +/*-------------------------------------------------------------- +>>> PLUVIAM +--------------------------------------------------------------*/ + +satus.events.add('render', function (component, skeleton) { + if (skeleton.pluviam === true) { + function createPluviam(event) { + var pluviam = document.createElement('span'), + rect = this.getBoundingClientRect(), + x = event.clientX - rect.left, + y = event.clientY - rect.top, + diameter = Math.sqrt(Math.pow(rect.width * 2, 2) + Math.pow(rect.height * 2, 2)); + + pluviam.className = 'satus-pluviam'; + + pluviam.style.left = x - diameter / 2 + 'px'; + pluviam.style.top = y - diameter / 2 + 'px'; + pluviam.style.width = diameter + 'px'; + pluviam.style.height = diameter + 'px'; + + this.appendChild(pluviam); + + setTimeout(function () { + pluviam.remove(); + }, 1000); + } + + component.addEventListener('mousedown', createPluviam); + component.addEventListener('mouseover', createPluviam); + } +}); +/*-------------------------------------------------------------- +>>> SORTABLE +--------------------------------------------------------------*/ + +satus.events.add('render', function (component, skeleton) { + if (skeleton.sortable === true) { + component.addEventListener('mousedown', function (event) { + if (event.button !== 0) { + return false; + } + + var component = this, + rect = this.getBoundingClientRect(), + x = event.clientX, + y = event.clientY, + offset_x = event.clientX - rect.left, + offset_y = event.clientY - rect.top, + ghost = satus.clone(this), + children = this.parentNode.children, + appended = false; + + ghost.classList.add('satus-sortable__ghost'); + + function mousemove(event) { + if (appended === false && (Math.abs(event.clientX - x) > 4 || Math.abs(event.clientY - y) > 4)) { + appended = true; + + component.classList.add('satus-sortable__chosen'); + + component.parentNode.appendChild(ghost); + } + + ghost.style.transform = 'translate(' + (event.clientX - offset_x) + 'px, ' + (event.clientY - offset_y) + 'px)'; + } + + function mouseup(event) { + component.classList.remove('satus-sortable__chosen'); + ghost.remove(); + + window.removeEventListener('mousemove', mousemove, true); + window.removeEventListener('mouseup', mouseup, true); + + for (var i = 0, l = children.length; i < l; i++) { + var child = children[i]; + + if (child !== component) { + child.removeEventListener('mouseover', siblingMouseOver); + } + } + + component.dispatchEvent(new CustomEvent('sort')); + + event.stopPropagation(); + + return false; + } + + window.addEventListener('mousemove', mousemove, { + passive: true, + capture: true + }); + + window.addEventListener('mouseup', mouseup, { + passive: true, + capture: true + }); + + function siblingMouseOver(event) { + var target = event.target, + parent = target.parentNode, + y = event.layerY / (target.offsetHeight / 100); + + if (y < 50 && target.previousSibling !== component || y >= 50 && target.nextSibling === component) { + parent.insertBefore(component, target); + } else { + parent.insertBefore(component, target.nextSibling); + } + } + + for (var i = 0, l = children.length; i < l; i++) { + var child = children[i]; + + if (child !== component) { + child.addEventListener('mouseover', siblingMouseOver); + } + } + + event.stopPropagation(); + event.preventDefault(); + + return false; + }); + } +}); \ No newline at end of file diff --git a/ui/script.js b/ui/script.js index 32b0509..897917c 100644 --- a/ui/script.js +++ b/ui/script.js @@ -12,382 +12,395 @@ var password, lists = [], skeleton = { - component: 'base', + component: 'base', - header: { - component: 'header', + header: { + component: 'header', - section_1: { - component: 'section', - variant: 'align-start', - - back: { - component: 'button', - attr: { - 'hidden': 'true' - }, - on: { - click: 'layers.back' - }, + section_1: { + component: 'section', + variant: 'align-start', - svg: { - component: 'svg', + back: { + component: 'button', attr: { - 'viewBox': '0 0 24 24', - 'fill': 'none', - 'stroke-width': '1.5', - 'stroke': 'currentColor' + 'hidden': 'true' + }, + on: { + click: 'layers.back' }, - path: { - component: 'path', + svg: { + component: 'svg', attr: { - 'd': 'M14 18l-6-6 6-6' + 'viewBox': '0 0 24 24', + 'fill': 'none', + 'stroke-width': '1.5', + 'stroke': 'currentColor' + }, + + path: { + component: 'path', + attr: { + 'd': 'M14 18l-6-6 6-6' + } } } + }, + title: { + component: 'span', + variant: 'title' } }, - title: { - component: 'span', - variant: 'title' - } - }, - section_2: { - component: 'section', - variant: 'align-end', - - menu: { - component: 'button', - on: { - click: { - component: 'modal', - variant: 'vertical', - - label: { - component: 'span', - text: 'theme' - }, - theme: { - component: 'tabs', - items: [ - 'light', - 'dark', - 'black' - ] - }, - divider: { - component: 'divider' - }, - language: { - component: 'select', - on: { - change: function (name, value) { - var self = this; - - satus.ajax('_locales/' + this.querySelector('select').value + '/messages.json', function (response) { - response = JSON.parse(response); - - for (var key in response) { - satus.locale.strings[key] = response[key].message; - } + section_2: { + component: 'section', + variant: 'align-end', + + menu: { + component: 'button', + on: { + click: { + component: 'modal', + variant: 'vertical', + + label: { + component: 'span', + text: 'theme' + }, + theme: { + component: 'tabs', + items: [ + 'light', + 'dark', + 'black' + ] + }, + divider: { + component: 'divider' + }, + language: { + component: 'select', + on: { + change: function (name, value) { + var self = this; + + satus.ajax('_locales/' + this.querySelector('select').value + '/messages.json', function (response) { + response = JSON.parse(response); + + for (var key in response) { + satus.locale.strings[key] = response[key].message; + } - self.base.skeleton.header.section_1.title.rendered.textContent = satus.locale.get('languages'); + self.base.skeleton.header.section_1.title.rendered.textContent = satus.locale.get('languages'); - self.base.skeleton.layers.rendered.update(); - }); - } - }, - options: [{ - value: 'en', - text: 'English' - }, { - value: 'ru', - text: 'Русский' - }, { - value: 'de', - text: 'Deutsch' - }], - - svg: { - component: 'svg', - attr: { - 'viewBox': '0 0 24 24', - 'fill': 'currentColor' + self.base.skeleton.layers.rendered.update(); + }); + } }, - - path: { - component: 'path', + options: [{ + value: 'en', + text: 'English' + }, { + value: 'ru', + text: 'Русский' + }, { + value: 'de', + text: 'Deutsch' + }], + + svg: { + component: 'svg', attr: { - 'd': 'M12.9 15l-2.6-2.4c1.8-2 3-4.2 3.8-6.6H17V4h-7V2H8v2H1v2h11.2c-.7 2-1.8 3.8-3.2 5.3-1-1-1.7-2.1-2.3-3.3h-2c.7 1.6 1.7 3.2 3 4.6l-5.1 5L4 19l5-5 3.1 3.1.8-2zm5.6-5h-2L12 22h2l1.1-3H20l1.1 3h2l-4.5-12zm-2.6 7l1.6-4.3 1.6 4.3H16z' + 'viewBox': '0 0 24 24', + 'fill': 'currentColor' + }, + + path: { + component: 'path', + attr: { + 'd': 'M12.9 15l-2.6-2.4c1.8-2 3-4.2 3.8-6.6H17V4h-7V2H8v2H1v2h11.2c-.7 2-1.8 3.8-3.2 5.3-1-1-1.7-2.1-2.3-3.3h-2c.7 1.6 1.7 3.2 3 4.6l-5.1 5L4 19l5-5 3.1 3.1.8-2zm5.6-5h-2L12 22h2l1.1-3H20l1.1 3h2l-4.5-12zm-2.6 7l1.6-4.3 1.6 4.3H16z' + } } + }, + label: { + component: 'span', + text: 'language' } }, - label: { - component: 'span', - text: 'language' - } - }, - encrypted: { - component: 'switch', - storage: false, - on: { - change: function () { - crypt(this.dataset.value === 'true', JSON.stringify(lists), function (mode, data) { - if (mode) { - satus.storage.set('encrypted', true); - } else { - satus.storage.set('encrypted', false); - } - - satus.storage.set('lists', data); - }); - } - }, + encrypted: { + component: 'switch', + storage: false, + on: { + render: function () { + if (satus.storage.get('encrypted') === true) { + this.dataset.value = 'true'; + } else { + this.dataset.value = 'false'; + } + }, + change: function () { + var component = this; + + crypt(this.dataset.value === 'true', JSON.stringify(lists), function (mode, data) { + if (mode) { + satus.storage.set('encrypted', true); - svg: { - component: 'svg', - attr: { - 'viewBox': '0 0 24 24', - 'fill': 'none', - 'stroke': 'currentColor', - 'stroke-linecap': 'round', - 'stroke-linejoin': 'round', - 'stroke-width': '2' + component.dataset.value = 'true'; + } else { + satus.storage.remove('encrypted'); + + component.dataset.value = 'false'; + } + + satus.storage.set('lists', data); + }, component); + } }, - path: { - component: 'path', + svg: { + component: 'svg', attr: { - 'd': 'M21 2l-2 2m-7.61 7.61a5.5 5.5 0 11-7.778 7.778 5.5 5.5 0 017.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4' + 'viewBox': '0 0 24 24', + 'fill': 'none', + 'stroke': 'currentColor', + 'stroke-linecap': 'round', + 'stroke-linejoin': 'round', + 'stroke-width': '2' + }, + + path: { + component: 'path', + attr: { + 'd': 'M21 2l-2 2m-7.61 7.61a5.5 5.5 0 11-7.778 7.778 5.5 5.5 0 017.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4' + } } + }, + label: { + component: 'span', + text: 'encryption' } }, - label: { - component: 'span', - text: 'encryption' - } - }, - export: { - component: 'button', - on: { - click: function () { - if (location.href.indexOf('/options.html?action=export') !== -1) { - exportData(); - } else { - chrome.tabs.create({ - url: 'ui/options.html?action=export' - }); - } - } - }, - - svg: { - component: 'svg', - attr: { - 'viewBox': '0 0 24 24', - 'fill': 'none', - 'stroke': 'currentColor', - 'stroke-linecap': 'round', - 'stroke-linejoin': 'round', - 'stroke-width': '2' - }, - - path: { - component: 'path', + export: { + component: 'button', + on: { + click: function () { + if (location.href.indexOf('/options.html?action=export') !== -1) { + exportData(); + } else { + chrome.tabs.create({ + url: 'ui/options.html?action=export' + }); + } + } + }, + + svg: { + component: 'svg', attr: { - 'd': 'M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12' + 'viewBox': '0 0 24 24', + 'fill': 'none', + 'stroke': 'currentColor', + 'stroke-linecap': 'round', + 'stroke-linejoin': 'round', + 'stroke-width': '2' + }, + + path: { + component: 'path', + attr: { + 'd': 'M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12' + } } + }, + label: { + component: 'span', + text: 'export' } }, - label: { - component: 'span', - text: 'export' - } - }, - import: { - component: 'button', - on: { - click: function () { - if (location.href.indexOf('/options.html?action=import') !== -1) { - importData(); - } else { - chrome.tabs.create({ - url: 'ui/options.html?action=import' - }); - } - } - }, - - svg: { - component: 'svg', - attr: { - 'viewBox': '0 0 24 24', - 'fill': 'none', - 'stroke': 'currentColor', - 'stroke-linecap': 'round', - 'stroke-linejoin': 'round', - 'stroke-width': '2' - }, - - path: { - component: 'path', + import: { + component: 'button', + on: { + click: function () { + if (location.href.indexOf('/options.html?action=import') !== -1) { + importData(); + } else { + chrome.tabs.create({ + url: 'ui/options.html?action=import' + }); + } + } + }, + + svg: { + component: 'svg', attr: { - 'd': 'M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3' + 'viewBox': '0 0 24 24', + 'fill': 'none', + 'stroke': 'currentColor', + 'stroke-linecap': 'round', + 'stroke-linejoin': 'round', + 'stroke-width': '2' + }, + + path: { + component: 'path', + attr: { + 'd': 'M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3' + } } + }, + label: { + component: 'span', + text: 'import' } - }, - label: { - component: 'span', - text: 'import' } + } + }, + + svg: { + component: 'svg', + attr: { + 'viewBox': '0 0 24 24', + 'fill': 'currentColor' + }, + + circle_1: { + component: 'circle', + attr: { + 'cx': '12', + 'cy': '5.25', + 'r': '1' + } + }, + circle_2: { + component: 'circle', + attr: { + 'cx': '12', + 'cy': '12', + 'r': '1' + } + }, + circle_3: { + component: 'circle', + attr: { + 'cx': '12', + 'cy': '18.75', + 'r': '1' + } + } + } + } + } + }, + layers: { + component: 'layers', + on: { + open: function () { + var skeleton = this.path[this.path.length - 1], + parent = skeleton.parent, + section = this.base.skeleton.header.section_1, + is_home = this.path.length <= 1, + title = 'ToDo'; + + if (parent) { + if (parent.label) { + title = parent.label.text; + } else if (parent.text) { + title = parent.text; } - } - }, - - svg: { - component: 'svg', - attr: { - 'viewBox': '0 0 24 24', - 'fill': 'currentColor' - }, - - circle_1: { - component: 'circle', - attr: { - 'cx': '12', - 'cy': '5.25', - 'r': '1' - } - }, - circle_2: { - component: 'circle', - attr: { - 'cx': '12', - 'cy': '12', - 'r': '1' - } - }, - circle_3: { - component: 'circle', - attr: { - 'cx': '12', - 'cy': '18.75', - 'r': '1' - } - } - } - } - } - }, - layers: { - component: 'layers', - on: { - open: function () { - var skeleton = this.path[this.path.length - 1], - parent = skeleton.parent, - section = this.base.skeleton.header.section_1, - is_home = this.path.length <= 1, - title = 'ToDo'; - - if (parent) { - if (parent.label) { - title = parent.label.text; - } else if (parent.text) { - title = parent.text; } - } - section.back.rendered.hidden = is_home; - section.title.rendered.innerText = satus.locale.get(title); + section.back.rendered.hidden = is_home; + section.title.rendered.innerText = satus.locale.get(title); - if (this.path.length === 1) { - updateLists(); + if (this.path.length === 1) { + updateLists(); + } } } - } - }, - create: { - component: 'button', - variant: 'create', - attr: { - 'title': 'create' }, - on: { - click: { - component: 'modal', - - title: { - component: 'span', - text: function () { - if (skeleton.layers.rendered.path.length > 1) { - return 'newTask'; - } else { - return 'newList'; + create: { + component: 'button', + variant: 'create', + attr: { + 'title': 'create' + }, + on: { + click: { + component: 'modal', + + title: { + component: 'span', + text: function () { + if (skeleton.layers.rendered.path.length > 1) { + return 'newTask'; + } else { + return 'newList'; + } } - } - }, - input: { - component: 'input', - type: 'text', - autofocus: true, - storage: false, - on: { - render: function () { - this.focus(); - }, - keydown: function (event) { - if (event.key === 'Enter') { - this.parentNode.parentNode.skeleton.actions.create.rendered.click(); + }, + input: { + component: 'input', + type: 'text', + autofocus: true, + storage: false, + on: { + render: function () { + this.focus(); + }, + keydown: function (event) { + if (event.key === 'Enter') { + this.parentNode.parentNode.skeleton.actions.create.rendered.click(); + } } } - } - }, - actions: { - component: 'section', - variant: 'actions', + }, + actions: { + component: 'section', + variant: 'actions', - create: { - component: 'button', - text: 'create', - on: { - click: function () { - var path = skeleton.layers.rendered.path, - modal = this.skeleton.parent.parent, - name = modal.input.rendered.value; - - if (path.length > 1) { - var layer = path[path.length - 1], - task = { + create: { + component: 'button', + text: 'create', + on: { + click: function () { + var path = skeleton.layers.rendered.path, + modal = this.skeleton.parent.parent, + name = modal.input.rendered.value; + + if (path.length > 1) { + var layer = path[path.length - 1], + task = { + name: name, + value: false, + time: new Date().getTime() + }; + + layer.tasks.push(task); + + satus.empty(layer.rendered); + + updateTasks(layer.tasks); + } else { + lists.push({ name: name, - value: false, - time: new Date().getTime() - }; + tasks: [] + }); - layer.tasks.push(task); - - satus.empty(layer.rendered); + updateLists(); + } - updateTasks(layer.tasks); - } else { - lists.push({ - name: name, - tasks: [] - }); + updateData(); - updateLists(); + modal.rendered.close(); } - - satus.storage.set('lists', lists); - - modal.rendered.close(); } } } } } } - } -}; + }; function updateLists() { var layer = skeleton.layers.rendered.children[0], @@ -479,7 +492,7 @@ function updateLists() { item.list.name = modal.skeleton.input.rendered.value; - satus.storage.set('lists', lists); + updateData(); updateLists(); @@ -504,7 +517,7 @@ function updateLists() { satus.remove(parent.list, lists); - satus.storage.set('lists', lists); + updateData(); updateLists(); @@ -529,7 +542,7 @@ function updateLists() { satus.toIndex(index, this.skeleton.list, lists); - satus.storage.set('lists', lists); + updateData(); } }, @@ -647,7 +660,7 @@ function updateTasks(tasks) { item.task.name = modal.skeleton.input.rendered.value; - satus.storage.set('lists', lists); + updateData(); updateTasks(item.tasks); @@ -674,7 +687,7 @@ function updateTasks(tasks) { satus.remove(parent.task, parent.tasks); - satus.storage.set('lists', lists); + updateData(); updateTasks(parent.tasks); @@ -688,24 +701,45 @@ function updateTasks(tasks) { change: function () { this.skeleton.task.value = this.storageValue; - satus.storage.set('lists', lists); + updateData(); }, sort: function () { var index = satus.indexOf(this); satus.toIndex(index, this.skeleton.task, this.skeleton.tasks); - satus.storage.set('lists', lists); + updateData(); } } }, section); } } -function crypt(mode, data, callback) { +function updateData() { + if (satus.storage.get('encrypted') === true && password) { + encr(); + } else { + satus.storage.set('lists', lists); + } +} + +function crypt(mode, data, callback, component) { satus.render({ component: 'modal', - parent: this.skeleton, + parent: component ? component.skeleton : undefined, + on: { + close: function () { + if (this.skeleton.parent) { + var component = this.skeleton.parent.parent.encrypted.rendered; + + if (satus.storage.get('encrypted') === true) { + component.dataset.value = 'true'; + } else { + component.dataset.value = 'false'; + } + } + } + }, title: { component: 'span', @@ -886,19 +920,17 @@ function importData() { } async function encr(callback) { - data = await satus.encrypt(JSON.stringify(lists), password); - - satus.storage.set('lists', data); + satus.storage.set('lists', await satus.encrypt(JSON.stringify(lists), password)); satus.storage.set('encrypted', true); - satus.storage.set('data', null); + satus.storage.remove('data'); - callback(); + if (callback) { + callback(); + } } function migrateData(callback) { - var data = satus.storage.get('data'); - - function change(data, callback) { + function change(data) { lists = []; try { @@ -924,22 +956,26 @@ function migrateData(callback) { }); } } - - encr(callback); } catch (error) { console.log(error); - - callback(); } } - if (data) { + if (satus.storage.get('data')) { if (satus.storage.get('encrypted')) { - crypt(false, data, function (mode, data) { - change(data, callback); + crypt(false, satus.storage.get('data'), function (mode, data) { + change(data); + + encr(callback); }); } else { - change(data, callback); + change(data); + + satus.storage.set('lists', lists); + satus.storage.remove('encrypted'); + satus.storage.remove('data'); + + callback(); } } else { callback(); @@ -964,7 +1000,7 @@ satus.storage.import(function (items) { lists = satus.storage.get('lists'); satus.render(skeleton); - } else if (satus.storage.get('encrypted') === true) { + } else if (satus.storage.get('encrypted') === true && typeof satus.storage.get('lists') === 'string') { crypt(false, satus.storage.get('lists'), function (mode, data) { lists = data;