diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..ac9f2a0b --- /dev/null +++ b/.babelrc @@ -0,0 +1,9 @@ +{ + "presets": ["@babel/preset-react", "@babel/preset-env"], + "comments": false, + "plugins": [ + ["react-intl", { + "messagesDir": "./src/i18n/" + }] + ] +} diff --git a/README.md b/README.md index 15ab5196..67579e26 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The app is about 200KB minified and gzipped. That includes all dependencies (Rea Although the app is generally usable, keep in mind that this is work in progress. Some bugs probably exist. The app was tested in the latest Chrome & Firefox only. NPM package is available at https://www.npmjs.com/package/tinode-webapp. -Try possibly newer or older version live at https://api.tinode.co/. For login and other instructions [see here](https://github.com/tinode/chat/#demo). +Try possibly newer or older version live at https://api.tinode.co/. For passwords for demo accounts and other instructions [see here](https://github.com/tinode/chat/#demo). Demo avatar photos are from https://www.pexels.com/ under [CC0 license](https://www.pexels.com/photo-license/). @@ -21,11 +21,17 @@ Background pattern from http://subtlepatterns.com/, commercial and non-commercia * For support, general questions, discussions post to [https://groups.google.com/d/forum/tinode](https://groups.google.com/d/forum/tinode). * For bugs and feature requests [open an issue](https://github.com/tinode/webchat/issues/new). +## Internationalization + +The app is fully internationalized using [React-Intl](https://github.com/yahoo/react-intl). The UI language is selected automatically from the language specified by the browser. A specific language can be forced by adding `hl=XX` parameter to the URL, i.e. https://api.tinode.co/#?hl=ru. + +As of the time of this writing there are only two translations. More translations are welcome. Send a pull request with a json file with translated strings. Take a look at [English](/src/i18n/en.json) and [Russian](/src/i18n/ru.json) translations for guidance. + + ## Missing functionality * Previews not generated for videos, audio, links or docs. * No end-to-end encryption. -* Editing chat permissions is incomplete. * Emoji support is weak. ## Responsive design diff --git a/css/base.css b/css/base.css index a3239c4e..03e31055 100644 --- a/css/base.css +++ b/css/base.css @@ -19,7 +19,7 @@ html, body { } tt { - font-family: Roboto Mono, monospace; + font-family: 'Roboto Mono', Courier, monospace; } a { @@ -340,6 +340,32 @@ li.right:hover .menuTrigger { } /* Top-level application layout style: sidepanel + main view */ +#noscript-alertbox { + position: relative; + display: flex; + flex-direction: column; + max-width: 30rem; + overflow: hidden; + margin-top: 5rem; + margin-left: auto; + margin-right: auto; + border-radius: 0.25rem; + box-shadow: 1px 2px 0.3rem gray; + background-color: white; +} + +#noscript-title { + padding: 1rem; + font-size: 120%; + font-weight: bolder; + background-color: #2196f3; /* blue 500 */ + color: #bbdefb; /* light-blue 100 */ +} + +#noscript-content { + padding: 1rem; +} + #app-container { position: relative; display: flex; @@ -1422,7 +1448,7 @@ ul.tabbar li.active > a:after { /* Badge with a label, like [you], [muted], etc */ .badge { - font-family: monospace; + font-family: 'Roboto Mono', Courier, monospace, sans-serif; font-size: 0.75rem; border: 1px solid #666; background-color: #eee; @@ -1448,12 +1474,24 @@ ul.tabbar li.active > a:after { color: #660; } +.badge.blue { + border-color: #339; + background-color: #eef; + color: #339; +} + .badge.red { border-color: #933; background-color: #fee; color: #933; } +.badge.magenta { + border-color: #939; + background-color: #fef; + color: #939; +} + /* Letter tile base style, responsive square box */ .lettertile { width: 100%; diff --git a/css/base.min.css b/css/base.min.css index 0d34a9c9..a706725c 100644 --- a/css/base.min.css +++ b/css/base.min.css @@ -1 +1 @@ -*{box-sizing:border-box;font-family:Roboto,Arial,sans-serif;margin:0;padding:0;text-rendering:optimizeLegibility}body,html{background-color:silver;box-shadow:inset 0 10rem #3949ab;color:#666;font-size:10pt;height:100%;overflow:hidden;width:100%}tt{font-family:Roboto Mono,monospace}a{color:#2196f3;text-decoration:none}a:focus,a:hover{color:#0a6ebd;text-decoration:underline}a.red:focus,a.red:hover{color:#f44336}a.flat-button{align-items:center;display:flex;font-weight:400;height:2em;line-height:2em;margin:.15rem .35rem;text-align:center;text-transform:uppercase;touch-action:manipulation;vertical-align:middle;white-space:nowrap}a.flat-button:focus,a.flat-button:hover{text-decoration:none}form{line-height:1.5;padding:.5rem .75rem}form,label{font-size:1.1rem}label{margin-right:auto}label.small{color:#1e88e5;font-size:.95rem}img.avatar{border-radius:50%;height:100%;width:100%}button{border:1px solid transparent;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.25);cursor:pointer;display:inline-block;font-family:inherit;font-size:1.1rem;font-weight:400;height:2.2em;line-height:2.2em;padding-left:2rem;padding-right:2rem;text-align:center;text-transform:uppercase;touch-action:manipulation;vertical-align:middle;white-space:nowrap}button[disabled],html input[disabled]{box-shadow:none;cursor:default;opacity:.65}button.round{border-radius:50%;box-shadow:0 2px .5rem 0 rgba(0,0,0,.25);height:3rem;line-height:3.5rem;margin:0;outline:none;padding:0;width:3rem;z-index:2}button.round.small{box-shadow:0 2px .35rem 0 rgba(0,0,0,.25);height:2rem;line-height:2rem;width:2rem}button:active,button:focus,button:hover{color:#fff}button:active{box-shadow:inset 0 3px .5rem rgba(0,0,0,.125)}button:active.round.small{box-shadow:inset 0 2px .35rem rgba(0,0,0,.125)}button.blue{background-color:#1e88e5;color:#fff}button.blue:active,button.blue:focus,button.blue:hover{background-color:#1976d2}button.white{background-color:#fff;color:#2196f3}button.blue:active,button.white:focus,button.white:hover{background-color:#e3f2fd}input,textarea{background:transparent;border:none;color:#666;font-family:inherit;font-size:1.1rem;margin:.25rem 0;outline:none;padding:0 .125rem}textarea{resize:none}input[type=email],input[type=password],input[type=search],input[type=text],textarea{border-bottom:1px solid #ccc;box-shadow:0 1px 0 rgba(0,0,0,.1);height:2.5rem;transition:all .1s ease-in-out}input[type=email],input[type=password],input[type=text],textarea{width:100%}input[type=email]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{border-bottom:1px solid #2196f3;box-shadow:0 1px 0 rgba(33,150,243,.25)}input[type=email]::placeholder,input[type=password]::placeholder,input[type=search]::placeholder,input[type=text]::placeholder,textarea::placeholder{color:#bbb}input[type=password].with-visibility,input[type=text].with-visibility{margin-right:-2rem;padding-right:2rem}::-webkit-scrollbar{width:.6rem}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,.2);background-color:hsla(0,0%,100%,.1)}::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.3)}ul.menu{background-color:#fff;border-radius:.2rem;box-shadow:0 1px 6px rgba(0,0,0,.45);list-style:none;padding-bottom:.35rem;padding-top:.35rem;position:absolute;width:12rem;z-index:4}ul.menu>li{padding:.75rem .75rem .75rem 1.25rem}ul.menu>li:hover{background-color:#f5f5f5}ul.menu>li.separator{height:1px;margin:2px;padding:0}ul.menu>li.separator,ul.menu>li.separator:hover{background-color:#ccc}li .menuTrigger{border-radius:.6rem;height:1.2rem;line-height:1.2rem;opacity:0;position:absolute;right:.25rem;text-align:center;top:.25rem;width:1.5rem}li:hover .menuTrigger{opacity:1}li:hover .menuTrigger a{color:#757575}li.left:hover .menuTrigger{-text-shadow:#c5e1a5 0 0 15px,#c5e1a5 0 0 5px;background-image:radial-gradient(#c5e1a5 40%,transparent 75%)}li.right:hover .menuTrigger{-text-shadow:#fafafa 0 0 15px,#fafafa 0 0 5px;background-image:radial-gradient(#fafafa 40%,transparent 75%)}.blue{color:#2196f3}.red{color:#d32f2f}.gray{color:#757575}.light-gray{color:#bbb}.small{font-size:90%}.hr{background-color:#f0f0f0;box-shadow:inset 0 .25rem .25rem -.25rem rgba(0,0,0,.15),inset 0 -.25rem .25rem -.25rem rgba(0,0,0,.15);display:block;height:.6rem;min-height:.3rem;width:100%}.group-focus{outline:none}#mountPoint{height:100%;padding-bottom:3rem;padding-top:1rem}#app-container{background-color:#fff;border-radius:.25rem;box-shadow:1px 2px .3rem grey;margin-left:auto;margin-right:auto;max-width:80rem;overflow:hidden}#app-container,#sidepanel{display:flex;height:100%;position:relative}#sidepanel{flex-direction:column;flex-grow:0;flex-shrink:0.3;min-width:18rem;width:24rem}#topic-view{width:100%}#info-view,#topic-view{display:flex;flex-direction:column;height:100%}#info-view{flex-grow:0;flex-shrink:0.3;min-width:18rem;width:24rem}@media (max-width:960px){#mountPoint{padding:0}}@media (max-width:640px){#app-container{border-radius:0;box-shadow:none;width:100%}#info-view,#sidepanel{width:100%}}.center-medium-text{align-items:center;display:flex;font-size:1.4rem;height:100%;justify-content:center;text-align:center;width:100%}.nodisplay{display:none!important}.quoted{margin-left:1.5rem}.in-place-edit{border-bottom:1px dashed #ddd;cursor:pointer;display:inline-block;font-family:inherit;font-size:1.1rem;height:2.5rem;line-height:2.25;margin:.25rem .125rem;vertical-align:baseline;width:100%}.in-place-edit.disabled{border-bottom:none;cursor:default}.in-place-edit.placeholder{color:#bbb}.in-place-edit .content{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}table.permission-editor{width:100%}table.permission-editor td{font-size:1.1rem;margin:.35rem 0;vertical-align:middle}table.permission-editor td.checkbox{color:#999;text-align:center}.permission-editor .material-icons{font-size:1.1rem;line-height:1.1rem}.material-icons.clickable{border-bottom:none;cursor:pointer}.clickable{border-bottom:1px dashed #ddd;cursor:pointer}.panel-title{font-size:1.4rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.caption-panel{align-items:center;box-shadow:0 1px 2px grey;display:flex;flex:none;height:4rem;padding:.5rem;white-space:nowrap;z-index:1}.dialog-buttons{display:flex;justify-content:flex-end;padding:.5rem}.dialog-buttons button{margin:0 .5rem}.avatar-box{flex-grow:0;flex-shrink:0;position:relative}#side-caption-panel{background-color:#2196f3;color:#bbdefb}#side-caption-panel a{color:#bbdefb;line-height:1;padding-left:.5rem}#side-caption-panel :hover{color:#fff}#sidepanel-title{margin-left:.5rem;margin-right:auto}#self-avatar{border-radius:50%;flex:none;height:3rem;min-width:3rem;width:3rem}#self-avatar.avatar-box{font-size:1.8rem}#settings-form ul{list-style:none}#settings-form li{vertical-align:middle}#settings-form label{display:inline-block;padding-left:.5rem}input.search{text-indent:1.4rem}i.search{left:.6rem;position:absolute}.alert-box{display:none;overflow:hidden;padding:1rem 1.5rem 1rem .5rem;position:relative}.alert-box .cancel{position:absolute;right:.25rem;top:.5rem}.alert-box.error{background-color:#ffcdd2;border-bottom:1px solid #ffbfc8;color:#b71c1c;display:flex}.alert-box.warning{background-color:#fff9c4;border-bottom:1px solid #fff59d;color:#607d8b;display:flex}.alert-box .cancel .material-icons{font-size:1rem}.alert-box.error .cancel .material-icons{color:#b71c1c}.alert-box.warning .cancel .material-icons{color:#757575}.alert-box .icon{display:block;margin-right:.5rem}.alert-box .icon .material-icons{font-size:1.5rem}.alert-box.warning .icon .material-icons{color:#fbc02d}.alert-box.error .icon .material-icons{color:#ef5350}.flex-column{display:flex;flex-direction:column;height:100%;overflow:hidden}.scrollable-panel{height:100%;margin-bottom:auto;margin-top:.25rem;overflow-y:auto}.contact-box{list-style:none;overflow-x:hidden}.contact-box>li{border-bottom:1px solid #eee;border-collapse:collapse;display:flex;flex-grow:0;flex-shrink:0;min-height:3.5rem;overflow-x:hidden;padding:.35rem .25rem .5rem .35rem;position:relative}.contact-box>li:hover{background-color:#eceff1}.contact-box>li.selected{background-color:#cfd8dc}.contact-box .text-box{overflow-x:hidden}.contact-box .contact-title{font-size:1.05rem;overflow:hidden;text-overflow:ellipsis}.contact-box .contact-comment{font-weight:lighter;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.contact-box .avatar-box{font-size:1.8rem;height:3rem;margin-right:.35rem;min-height:3rem;min-width:3rem;overflow:hidden;padding:0;position:relative;width:3rem}.contact-box .avatar{border-radius:50%;height:3rem;width:3rem}.contact-box .avatar-box>.material-icons{height:3rem;width:3rem}.contact-box .avatar-box>.material-icons,.contact-box .text-box{display:flex;flex-direction:column;justify-content:space-around}.avatar-box .online,.offline{border-radius:50%;bottom:.1rem;display:inline-block;flex-shrink:0;height:.75rem;min-height:.75rem;min-width:.75rem;position:absolute;right:0;width:.75rem}.avatar-box .online{background-color:#4caf50}.avatar-box .offline{background-color:silver}.online.typing{animation:typing .5s infinite}@keyframes typing{0%,to{transform:translateX(0)}50%{transform:translateX(-.3rem)}}.contact-box .checkmark.material-icons{background-color:#fff;border:0 solid transparent;bottom:.05rem;color:#0097a7;display:inline-block;font-size:1.5rem;height:1.5rem;line-height:1.5rem;overflow:visible;position:absolute;right:.05rem;width:1.5rem}#add-topic{bottom:1rem;position:absolute;right:1rem}.panel-form{display:flex;flex-direction:column;padding:0}.panel-form-row{align-items:center;justify-content:flex-start}.panel-form-column,.panel-form-row{display:flex;flex-grow:0;flex-shrink:0;line-height:1.5;padding:.5rem .75rem}.panel-form-column{flex-direction:column}.panel-form-column>.panel-form-column,.panel-form-row>.panel-form-column{flex-grow:1;padding:0!important}.panel-form-column>.panel-form-row,.panel-form-row>.panel-form-row{padding:0!important}.avatar-upload{display:flex;flex-direction:column;flex-shrink:0;font-size:4.5rem;height:9rem;margin-left:.5rem;padding:0;width:7rem}.avatar-upload .avatar-box{height:7rem;position:relative;width:7rem}.avatar-upload .avatar-box>.material-icons{font-size:4rem;height:7rem;line-height:7rem;position:relative;vertical-align:middle;width:7rem}.avatar-upload:after{clear:both;content:".";display:block;height:0;visibility:hidden}.avatar-upload .blank{border:1px dashed #999;border-radius:50%;color:#999;flex-shrink:0;font-size:1rem;height:7rem;line-height:7rem;overflow:hidden;text-align:center;vertical-align:middle;width:7rem}.avatar-upload .preview{border-radius:50%;height:7rem;overflow:hidden;width:7rem}.avatar-upload .inputfile.hidden{height:.1px;opacity:0;overflow:hidden;position:absolute;width:.1px}.avatar-upload .inputfile+label.round{align-items:center;background-color:#fafafa;border-radius:50%;bottom:0;box-shadow:0 1px 4px rgba(0,0,0,.4);cursor:pointer;display:flex;height:3rem;justify-content:center;line-height:3rem;margin-left:auto;margin-right:auto;margin-top:-1rem;width:3rem}.avatar-upload .inputfile:focus+label.round{outline:none}.avatar-upload .inputfile+label:hover,.avatar-upload .inputfile:focus+label{background-color:#e3f2fd;color:#42a5f5}#topic-view{position:relative}#topic-caption-panel{background-color:#b0bec5}#topic-caption-panel a{color:#757575;text-decoration:none}#topic-caption-panel :hover{color:#000}#topic-caption-panel .avatar-box{font-size:1.8rem;height:3rem;margin-right:.35rem;min-width:3rem;position:relative;width:3rem}#hide-message-view{display:block;margin-right:.35rem}#topic-users{margin-left:auto;margin-right:1rem}#topic-users .avatar-box{display:inline-block;font-size:1rem;height:1.8rem;margin-right:.2rem;min-height:1.8rem;min-width:1.8rem;width:1.8rem}#messages-panel{background-color:#eceff1;background-image:url(../img/ignasi_pattern_s.png);height:100%;overflow-y:scroll;padding:.75rem;position:relative}#messages-container{height:100%;overflow-y:hidden;position:relative}#write-only-background{background-image:repeating-linear-gradient(45deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.5) .5rem,hsla(0,0%,100%,.8) 0,hsla(0,0%,100%,.8) 1rem);height:100%;left:0;position:absolute;top:0;width:100%}#write-only-note{background-color:#fff;box-shadow:.1rem .1rem .1rem rgba(0,0,0,.2);color:#666;height:auto;left:50%;line-height:2rem;padding:.25rem 1.5rem;position:absolute;text-align:center;top:50%;transform:translate(-50%,-50%);width:auto}#send-message-panel{align-items:center;background-color:#eceff1;box-shadow:0 -1px rgba(0,0,0,.1);display:flex;flex:none;height:4rem;padding:.4rem .4rem .4rem .6rem;z-index:2}#sendMessage{overflow:hidden;overflow-wrap:break-word}#send-message-panel .material-icons{min-width:1.75rem}#send-message-panel .material-icons.secondary,.material-icons.disabled{color:#999!important}#send-message-panel .material-icons.secondary:hover{color:#2196f3!important}#dummy-view{align-items:center;background-color:#e0f7fa;display:flex;height:100%;justify-content:center;width:100%}#dummy-view a{display:block;text-align:center;text-decoration:none;vertical-align:middle}#image-preview{background-color:hsla(0,0%,100%,.9);display:flex;flex-direction:column;height:100%;left:0;position:absolute;top:0;width:100%;z-index:2}#image-preview-caption-panel{background-color:transparent;display:flex;height:3.5rem;justify-content:space-between;line-height:2.5rem;padding:1rem .75rem 0}#image-preview-caption-panel a{color:#757575;text-decoration:none}#image-preview-caption-panel a:hover{color:#000}#image-preview-container{margin:auto;overflow:hidden}#image-preview-footer{background-color:transparent;display:flex;height:3.2rem;justify-content:space-around;line-height:1.5rem}.chat-box{list-style:none;margin:0 .5rem;padding:0 0 3.5rem}.chat-box.group{margin-left:-.4rem}.chat-box li{align-items:flex-end;display:flex;margin:0;overflow:hidden;padding:0}.chat-box .right{justify-content:flex-end}.chat-box .bubble{border-radius:.5rem;display:flex;height:auto;margin:0;max-width:40rem;min-width:12rem;padding:.75rem .5rem .75rem 1.25rem;position:relative;text-align:left}.chat-box li.single{margin-bottom:.75rem;margin-top:.25rem}.chat-box li.first{margin-bottom:.15rem;margin-top:.25rem}.chat-box li.middle{margin-bottom:.15rem;margin-top:.15rem}.chat-box li.last{margin-bottom:.75rem;margin-top:.15rem}.chat-box .left .bubble{background-color:#c5e1a5}.chat-box .right .bubble{background-color:#fafafa}.chat-box .left .tip:before{color:#c5e1a5;left:-10px;right:auto}.chat-box .left .tip:before,.chat-box .right .tip:before{border:10px solid transparent;border-bottom-color:currentColor;bottom:0;content:"";height:0;position:absolute;top:auto;width:0}.chat-box .right .tip:before{color:#fafafa;display:flex;justify-content:space-between;left:auto;right:-10px}.chat-box .avatar-box{font-size:.8rem;height:1.4rem;margin-bottom:.8rem;margin-right:.8rem;min-height:1.4rem;min-width:1.4rem;position:relative;width:1.4rem}.chat-box .bubble div.message-content{color:#212121;margin:0;max-width:36rem;overflow-wrap:break-word}.chat-box .bubble div.message-content:after{clear:both;content:".";display:block;height:0;visibility:hidden}.chat-box .timestamp{color:#777;display:inline-block;float:right;font-size:80%;padding-left:1rem;position:relative;top:.5rem;white-space:nowrap}.chat-box .typing{border-radius:.5rem;color:#aaa;display:inline-block;margin-top:.75rem;padding:.25rem .35rem;text-align:center;vertical-align:middle;width:2rem}.chat-box .author{color:#777;font-size:80%}.inline-image{border-radius:.1rem;cursor:pointer;display:block;max-height:24rem;max-width:36rem;overflow:hidden}.attachment{display:flex}.attachment:last-of-type{margin-bottom:-.45rem}#info-caption-panel{background-color:#b0bec5}#info-caption-panel a{color:#757575;line-height:1;text-decoration:none}#info-caption-panel :hover{color:#000}#info-title{margin-right:auto}#group-manager{display:flex;flex-direction:column;height:100%;position:relative}#group-manager-buttons{background-color:#eceff1;border-left:1px solid #ccc;box-shadow:0 -1px rgba(0,0,0,.1);display:flex;height:4rem;justify-content:space-around;padding:.4rem;z-index:2}.dialog-overlay{background-color:hsla(0,0%,100%,.9);height:100%;left:0;position:absolute;top:0;width:100%;z-index:10}.dialog-overlay,.modal-dialog{display:flex;flex-direction:column}.modal-dialog{background-color:#fff;border-radius:.3rem;box-shadow:0 .5rem 1rem 0 rgba(0,0,0,.25);justify-content:space-between;margin:auto;min-height:16rem;min-width:24rem}.modal-dialog .title-panel{display:flex;font-size:1.4rem;justify-content:space-between;padding:1rem}.modal-dialog .body-panel{margin:auto .5rem}.modal-dialog .dialog-buttons{background-color:#fafafa;border-top:1px solid #f5f5f5}.modal-dialog .dialog-buttons a{display:inline-block;font-size:1.1rem;padding:.35rem 1.5rem;text-decoration:none;text-transform:uppercase}ul.tabbar{background:#eee;flex-grow:0;flex-shrink:0;list-style:none;margin:0;overflow:hidden;padding:0 0 0 1rem;width:100%}ul.tabbar>li{float:left}ul.tabbar>li>a{box-shadow:inset 0 -2px 2.5px -1px rgba(0,0,0,.25);color:rgba(33,150,243,.6);display:block;font-size:1.3rem;font-variant:small-caps;font-weight:400;line-height:2rem;overflow:hidden;padding-left:1rem;padding-right:1rem;padding-top:.75rem;position:relative;text-align:center;text-decoration:none}ul.tabbar li.active>a{color:#2196f3}ul.tabbar li.active>a:after{animation:tabbar-border-expand .2s cubic-bezier(.4,0,.4,1) 0s alternate forwards;background:#2196f3;bottom:0;content:"";display:block;height:.15rem;left:0;position:absolute}@keyframes tabbar-border-expand{0%{opacity:0;width:0}to{opacity:1;width:100%}}.unread{background-color:#0097a7;border-radius:50%;color:#fff;font-weight:bolder;height:1.35rem;line-height:1.35rem;margin-left:.5rem;text-align:center;vertical-align:middle;width:1.35rem}.badge,.unread{display:inline-block;font-size:.75rem}.badge{align-self:flex-start;background-color:#eee;border:1px solid #666;border-radius:.15rem;color:#666;font-family:monospace;margin-bottom:.1rem;margin-left:.1rem;margin-right:.1rem;padding:0 .15rem}.badge.green{background-color:#efe;border-color:#383;color:#383}.badge.yellow{background-color:#ffe;border-color:#660;color:#660}.badge.red{background-color:#fee;border-color:#933;color:#933}.lettertile{border-radius:50%;padding-bottom:100%;width:100%}.lettertile>div{align-items:center;border-radius:50%;bottom:0;color:hsla(0,0%,98%,.8);display:flex;flex-grow:0;flex-shrink:0;justify-content:center;left:0;line-height:1;position:absolute;right:0;text-align:center;text-transform:uppercase;top:0}.lettertile:hover{color:#fff}.lettertile.color0{background-color:#ef9a9a}.lettertile.color1{background-color:#90caf9}.lettertile.color2{background-color:#b0bec5}.lettertile.color3{background-color:#b39ddb}.lettertile.color4{background-color:#ffab91}.lettertile.color5{background-color:#a5d6a7}.lettertile.color6{background-color:#eee}.lettertile.color7{background-color:#e6ee9c}.lettertile.color8{background-color:#c5e1a5}.lettertile.color9{background-color:#f0e57d}.lettertile.color10{background-color:#f48fb1}.lettertile.color11{background-color:#9fa8da}.lettertile.color12{background-color:#ffe082}.lettertile.color13{background-color:#bcaaa4}.lettertile.color14{background-color:#80deea}.lettertile.color15{background-color:#ce93d8}.lettertile.dark-color0{background-color:#c62828}.lettertile.dark-color1{background-color:#ad1457}.lettertile.dark-color2{background-color:#6a1b9a}.lettertile.dark-color3{background-color:#4527a0}.lettertile.dark-color4{background-color:#283593}.lettertile.dark-color5{background-color:#1565c0}.lettertile.dark-color6{background-color:#0277bd}.lettertile.dark-color7{background-color:#00838f}.lettertile.dark-color8{background-color:#00695c}.lettertile.dark-color9{background-color:#2e7d32}.lettertile.dark-color10{background-color:#558b2f}.lettertile.dark-color11{background-color:#9e9d24}.lettertile.dark-color12{background-color:#f9a825}.lettertile.dark-color13{background-color:#ff8f00}.lettertile.dark-color14{background-color:#ef6c00}.lettertile.dark-color15{background-color:#d84315}.material-icons{font-size:1.4rem;line-height:1;text-align:center;vertical-align:middle}.material-icons.small{font-size:1rem}.material-icons.big{font-size:2.4rem}#self-avatar.material-icons,.avatar-box .material-icons{font-size:2.4rem;height:3rem;line-height:3rem;width:3rem}.avatar-box .material-icons{background-color:silver;border-radius:50%;color:hsla(0,0%,98%,.8)}.chip-input{align-items:center;background:transparent;border-bottom:1px solid #ccc;box-shadow:0 1px 0 rgba(0,0,0,.1);color:#666;cursor:text;display:flex;flex-grow:1;flex-wrap:wrap;font-size:1.1rem;font-weight:400;justify-content:flex-start;margin:.25rem 0;max-height:10rem;min-height:4rem;outline:none;overflow-y:scroll;text-align:center;transition:all .1s ease-in-out}.chip-input.focused{border-bottom:1px solid #2196f3;box-shadow:0 1px 0 rgba(33,150,243,.25)}.chip-input>input[type=text]{border:none;box-shadow:none;display:inline-block;flex:1 0 auto;min-width:4rem;width:auto}.chip,.chip-input>input[type=text]{height:1.8rem;line-height:1.8;margin:.125rem}.chip{align-items:center;background-color:#eee;border-radius:.9rem;display:flex;flex:0 0 auto;font-size:90%;white-space:nowrap}.chip>a{background-color:#aaa;border-radius:50%;color:#ccc;display:inline-block;height:1.175rem;line-height:1.15;margin:.3rem;text-decoration:none;width:1.175rem}.chip>a:focus,.chip>a:hover{background-color:#bbb;color:#ddd;text-decoration:none}.chip .avatar-box{font-size:1rem;height:1.8rem;margin-right:.2rem;min-height:1.8rem;min-width:1.8rem;position:relative;width:1.8rem}.chip .avatar-box .material-icons{font-size:1.8rem}.chip .spacer{display:inline-block;height:1.175rem;margin:.3rem;width:.25rem}.load-spinner-box{background-color:#fff;border-radius:50%;box-shadow:.1rem .1rem .15rem 1px rgba(0,0,0,.25);height:2.5rem;left:0;margin-left:auto;margin-right:auto;padding:.25rem;position:absolute;right:0;top:5rem;width:2.5rem;z-index:2}.loader-spinner{animation:spin 1.5s linear infinite;border:.35rem solid #f3f3f3;border-radius:50%;border-top-color:#1e88e5;height:2rem;width:2rem}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.uploader{display:flex}.uploader>div{background-color:#cfd8dc;border-radius:.1rem;height:.6rem;line-height:0;margin:.35rem;padding:.15rem;position:relative;width:6rem}.uploader>div>span{background-color:#1e88e5;border-radius:.08rem;display:inline-block;height:.3rem} \ No newline at end of file +*{box-sizing:border-box;font-family:Roboto,Arial,sans-serif;margin:0;padding:0;text-rendering:optimizeLegibility}body,html{background-color:silver;box-shadow:inset 0 10rem #3949ab;color:#666;font-size:10pt;height:100%;overflow:hidden;width:100%}tt{font-family:Roboto Mono,Courier,monospace}a{color:#2196f3;text-decoration:none}a:focus,a:hover{color:#0a6ebd;text-decoration:underline}a.red:focus,a.red:hover{color:#f44336}a.flat-button{align-items:center;display:flex;font-weight:400;height:2em;line-height:2em;margin:.15rem .35rem;text-align:center;text-transform:uppercase;touch-action:manipulation;vertical-align:middle;white-space:nowrap}a.flat-button:focus,a.flat-button:hover{text-decoration:none}form{line-height:1.5;padding:.5rem .75rem}form,label{font-size:1.1rem}label{margin-right:auto}label.small{color:#1e88e5;font-size:.95rem}img.avatar{border-radius:50%;height:100%;width:100%}button{border:1px solid transparent;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.25);cursor:pointer;display:inline-block;font-family:inherit;font-size:1.1rem;font-weight:400;height:2.2em;line-height:2.2em;padding-left:2rem;padding-right:2rem;text-align:center;text-transform:uppercase;touch-action:manipulation;vertical-align:middle;white-space:nowrap}button[disabled],html input[disabled]{box-shadow:none;cursor:default;opacity:.65}button.round{border-radius:50%;box-shadow:0 2px .5rem 0 rgba(0,0,0,.25);height:3rem;line-height:3.5rem;margin:0;outline:none;padding:0;width:3rem;z-index:2}button.round.small{box-shadow:0 2px .35rem 0 rgba(0,0,0,.25);height:2rem;line-height:2rem;width:2rem}button:active,button:focus,button:hover{color:#fff}button:active{box-shadow:inset 0 3px .5rem rgba(0,0,0,.125)}button:active.round.small{box-shadow:inset 0 2px .35rem rgba(0,0,0,.125)}button.blue{background-color:#1e88e5;color:#fff}button.blue:active,button.blue:focus,button.blue:hover{background-color:#1976d2}button.white{background-color:#fff;color:#2196f3}button.blue:active,button.white:focus,button.white:hover{background-color:#e3f2fd}input,textarea{background:transparent;border:none;color:#666;font-family:inherit;font-size:1.1rem;margin:.25rem 0;outline:none;padding:0 .125rem}textarea{resize:none}input[type=email],input[type=password],input[type=search],input[type=text],textarea{border-bottom:1px solid #ccc;box-shadow:0 1px 0 rgba(0,0,0,.1);height:2.5rem;transition:all .1s ease-in-out}input[type=email],input[type=password],input[type=text],textarea{width:100%}input[type=email]:focus,input[type=password]:focus,input[type=search]:focus,input[type=text]:focus,textarea:focus{border-bottom:1px solid #2196f3;box-shadow:0 1px 0 rgba(33,150,243,.25)}input[type=email]::placeholder,input[type=password]::placeholder,input[type=search]::placeholder,input[type=text]::placeholder,textarea::placeholder{color:#bbb}input[type=password].with-visibility,input[type=text].with-visibility{margin-right:-2rem;padding-right:2rem}::-webkit-scrollbar{width:.6rem}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,.2);background-color:hsla(0,0%,100%,.1)}::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.3)}ul.menu{background-color:#fff;border-radius:.2rem;box-shadow:0 1px 6px rgba(0,0,0,.45);list-style:none;padding-bottom:.35rem;padding-top:.35rem;position:absolute;width:12rem;z-index:4}ul.menu>li{padding:.75rem .75rem .75rem 1.25rem}ul.menu>li:hover{background-color:#f5f5f5}ul.menu>li.separator{height:1px;margin:2px;padding:0}ul.menu>li.separator,ul.menu>li.separator:hover{background-color:#ccc}li .menuTrigger{border-radius:.6rem;height:1.2rem;line-height:1.2rem;opacity:0;position:absolute;right:.25rem;text-align:center;top:.25rem;width:1.5rem}li:hover .menuTrigger{opacity:1}li:hover .menuTrigger a{color:#757575}li.left:hover .menuTrigger{-text-shadow:#c5e1a5 0 0 15px,#c5e1a5 0 0 5px;background-image:radial-gradient(#c5e1a5 40%,transparent 75%)}li.right:hover .menuTrigger{-text-shadow:#fafafa 0 0 15px,#fafafa 0 0 5px;background-image:radial-gradient(#fafafa 40%,transparent 75%)}.blue{color:#2196f3}.red{color:#d32f2f}.gray{color:#757575}.light-gray{color:#bbb}.small{font-size:90%}.hr{background-color:#f0f0f0;box-shadow:inset 0 .25rem .25rem -.25rem rgba(0,0,0,.15),inset 0 -.25rem .25rem -.25rem rgba(0,0,0,.15);display:block;height:.6rem;min-height:.3rem;width:100%}.group-focus{outline:none}#mountPoint{height:100%;padding-bottom:3rem;padding-top:1rem}#noscript-alertbox{background-color:#fff;border-radius:.25rem;box-shadow:1px 2px .3rem grey;display:flex;flex-direction:column;margin-left:auto;margin-right:auto;margin-top:5rem;max-width:30rem;overflow:hidden;position:relative}#noscript-title{background-color:#2196f3;color:#bbdefb;font-size:120%;font-weight:bolder;padding:1rem}#noscript-content{padding:1rem}#app-container{background-color:#fff;border-radius:.25rem;box-shadow:1px 2px .3rem grey;margin-left:auto;margin-right:auto;max-width:80rem;overflow:hidden}#app-container,#sidepanel{display:flex;height:100%;position:relative}#sidepanel{flex-direction:column;flex-grow:0;flex-shrink:0.3;min-width:18rem;width:24rem}#topic-view{width:100%}#info-view,#topic-view{display:flex;flex-direction:column;height:100%}#info-view{flex-grow:0;flex-shrink:0.3;min-width:18rem;width:24rem}@media (max-width:960px){#mountPoint{padding:0}}@media (max-width:640px){#app-container{border-radius:0;box-shadow:none;width:100%}#info-view,#sidepanel{width:100%}}.center-medium-text{align-items:center;display:flex;font-size:1.4rem;height:100%;justify-content:center;text-align:center;width:100%}.nodisplay{display:none!important}.quoted{margin-left:1.5rem}.in-place-edit{border-bottom:1px dashed #ddd;cursor:pointer;display:inline-block;font-family:inherit;font-size:1.1rem;height:2.5rem;line-height:2.25;margin:.25rem .125rem;vertical-align:baseline;width:100%}.in-place-edit.disabled{border-bottom:none;cursor:default}.in-place-edit.placeholder{color:#bbb}.in-place-edit .content{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}table.permission-editor{width:100%}table.permission-editor td{font-size:1.1rem;margin:.35rem 0;vertical-align:middle}table.permission-editor td.checkbox{color:#999;text-align:center}.permission-editor .material-icons{font-size:1.1rem;line-height:1.1rem}.material-icons.clickable{border-bottom:none;cursor:pointer}.clickable{border-bottom:1px dashed #ddd;cursor:pointer}.panel-title{font-size:1.4rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.caption-panel{align-items:center;box-shadow:0 1px 2px grey;display:flex;flex:none;height:4rem;padding:.5rem;white-space:nowrap;z-index:1}.dialog-buttons{display:flex;justify-content:flex-end;padding:.5rem}.dialog-buttons button{margin:0 .5rem}.avatar-box{flex-grow:0;flex-shrink:0;position:relative}#side-caption-panel{background-color:#2196f3;color:#bbdefb}#side-caption-panel a{color:#bbdefb;line-height:1;padding-left:.5rem}#side-caption-panel :hover{color:#fff}#sidepanel-title{margin-left:.5rem;margin-right:auto}#self-avatar{border-radius:50%;flex:none;height:3rem;min-width:3rem;width:3rem}#self-avatar.avatar-box{font-size:1.8rem}#settings-form ul{list-style:none}#settings-form li{vertical-align:middle}#settings-form label{display:inline-block;padding-left:.5rem}input.search{text-indent:1.4rem}i.search{left:.6rem;position:absolute}.alert-box{display:none;overflow:hidden;padding:1rem 1.5rem 1rem .5rem;position:relative}.alert-box .cancel{position:absolute;right:.25rem;top:.5rem}.alert-box.error{background-color:#ffcdd2;border-bottom:1px solid #ffbfc8;color:#b71c1c;display:flex}.alert-box.warning{background-color:#fff9c4;border-bottom:1px solid #fff59d;color:#607d8b;display:flex}.alert-box .cancel .material-icons{font-size:1rem}.alert-box.error .cancel .material-icons{color:#b71c1c}.alert-box.warning .cancel .material-icons{color:#757575}.alert-box .icon{display:block;margin-right:.5rem}.alert-box .icon .material-icons{font-size:1.5rem}.alert-box.warning .icon .material-icons{color:#fbc02d}.alert-box.error .icon .material-icons{color:#ef5350}.flex-column{display:flex;flex-direction:column;height:100%;overflow:hidden}.scrollable-panel{height:100%;margin-bottom:auto;margin-top:.25rem;overflow-y:auto}.contact-box{list-style:none;overflow-x:hidden}.contact-box>li{border-bottom:1px solid #eee;border-collapse:collapse;display:flex;flex-grow:0;flex-shrink:0;min-height:3.5rem;overflow-x:hidden;padding:.35rem .25rem .5rem .35rem;position:relative}.contact-box>li:hover{background-color:#eceff1}.contact-box>li.selected{background-color:#cfd8dc}.contact-box .text-box{overflow-x:hidden}.contact-box .contact-title{font-size:1.05rem;overflow:hidden;text-overflow:ellipsis}.contact-box .contact-comment{font-weight:lighter;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.contact-box .avatar-box{font-size:1.8rem;height:3rem;margin-right:.35rem;min-height:3rem;min-width:3rem;overflow:hidden;padding:0;position:relative;width:3rem}.contact-box .avatar{border-radius:50%;height:3rem;width:3rem}.contact-box .avatar-box>.material-icons{height:3rem;width:3rem}.contact-box .avatar-box>.material-icons,.contact-box .text-box{display:flex;flex-direction:column;justify-content:space-around}.avatar-box .online,.offline{border-radius:50%;bottom:.1rem;display:inline-block;flex-shrink:0;height:.75rem;min-height:.75rem;min-width:.75rem;position:absolute;right:0;width:.75rem}.avatar-box .online{background-color:#4caf50}.avatar-box .offline{background-color:silver}.online.typing{animation:typing .5s infinite}@keyframes typing{0%,to{transform:translateX(0)}50%{transform:translateX(-.3rem)}}.contact-box .checkmark.material-icons{background-color:#fff;border:0 solid transparent;bottom:.05rem;color:#0097a7;display:inline-block;font-size:1.5rem;height:1.5rem;line-height:1.5rem;overflow:visible;position:absolute;right:.05rem;width:1.5rem}#add-topic{bottom:1rem;position:absolute;right:1rem}.panel-form{display:flex;flex-direction:column;padding:0}.panel-form-row{align-items:center;justify-content:flex-start}.panel-form-column,.panel-form-row{display:flex;flex-grow:0;flex-shrink:0;line-height:1.5;padding:.5rem .75rem}.panel-form-column{flex-direction:column}.panel-form-column>.panel-form-column,.panel-form-row>.panel-form-column{flex-grow:1;padding:0!important}.panel-form-column>.panel-form-row,.panel-form-row>.panel-form-row{padding:0!important}.avatar-upload{display:flex;flex-direction:column;flex-shrink:0;font-size:4.5rem;height:9rem;margin-left:.5rem;padding:0;width:7rem}.avatar-upload .avatar-box{height:7rem;position:relative;width:7rem}.avatar-upload .avatar-box>.material-icons{font-size:4rem;height:7rem;line-height:7rem;position:relative;vertical-align:middle;width:7rem}.avatar-upload:after{clear:both;content:".";display:block;height:0;visibility:hidden}.avatar-upload .blank{border:1px dashed #999;border-radius:50%;color:#999;flex-shrink:0;font-size:1rem;height:7rem;line-height:7rem;overflow:hidden;text-align:center;vertical-align:middle;width:7rem}.avatar-upload .preview{border-radius:50%;height:7rem;overflow:hidden;width:7rem}.avatar-upload .inputfile.hidden{height:.1px;opacity:0;overflow:hidden;position:absolute;width:.1px}.avatar-upload .inputfile+label.round{align-items:center;background-color:#fafafa;border-radius:50%;bottom:0;box-shadow:0 1px 4px rgba(0,0,0,.4);cursor:pointer;display:flex;height:3rem;justify-content:center;line-height:3rem;margin-left:auto;margin-right:auto;margin-top:-1rem;width:3rem}.avatar-upload .inputfile:focus+label.round{outline:none}.avatar-upload .inputfile+label:hover,.avatar-upload .inputfile:focus+label{background-color:#e3f2fd;color:#42a5f5}#topic-view{position:relative}#topic-caption-panel{background-color:#b0bec5}#topic-caption-panel a{color:#757575;text-decoration:none}#topic-caption-panel :hover{color:#000}#topic-caption-panel .avatar-box{font-size:1.8rem;height:3rem;margin-right:.35rem;min-width:3rem;position:relative;width:3rem}#hide-message-view{display:block;margin-right:.35rem}#topic-users{margin-left:auto;margin-right:1rem}#topic-users .avatar-box{display:inline-block;font-size:1rem;height:1.8rem;margin-right:.2rem;min-height:1.8rem;min-width:1.8rem;width:1.8rem}#messages-panel{background-color:#eceff1;background-image:url(../img/ignasi_pattern_s.png);height:100%;overflow-y:scroll;padding:.75rem;position:relative}#messages-container{height:100%;overflow-y:hidden;position:relative}#write-only-background{background-image:repeating-linear-gradient(45deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.5) .5rem,hsla(0,0%,100%,.8) 0,hsla(0,0%,100%,.8) 1rem);height:100%;left:0;position:absolute;top:0;width:100%}#write-only-note{background-color:#fff;box-shadow:.1rem .1rem .1rem rgba(0,0,0,.2);color:#666;height:auto;left:50%;line-height:2rem;padding:.25rem 1.5rem;position:absolute;text-align:center;top:50%;transform:translate(-50%,-50%);width:auto}#send-message-panel{align-items:center;background-color:#eceff1;box-shadow:0 -1px rgba(0,0,0,.1);display:flex;flex:none;height:4rem;padding:.4rem .4rem .4rem .6rem;z-index:2}#sendMessage{overflow:hidden;overflow-wrap:break-word}#send-message-panel .material-icons{min-width:1.75rem}#send-message-panel .material-icons.secondary,.material-icons.disabled{color:#999!important}#send-message-panel .material-icons.secondary:hover{color:#2196f3!important}#dummy-view{align-items:center;background-color:#e0f7fa;display:flex;height:100%;justify-content:center;width:100%}#dummy-view a{display:block;text-align:center;text-decoration:none;vertical-align:middle}#image-preview{background-color:hsla(0,0%,100%,.9);display:flex;flex-direction:column;height:100%;left:0;position:absolute;top:0;width:100%;z-index:2}#image-preview-caption-panel{background-color:transparent;display:flex;height:3.5rem;justify-content:space-between;line-height:2.5rem;padding:1rem .75rem 0}#image-preview-caption-panel a{color:#757575;text-decoration:none}#image-preview-caption-panel a:hover{color:#000}#image-preview-container{margin:auto;overflow:hidden}#image-preview-footer{background-color:transparent;display:flex;height:3.2rem;justify-content:space-around;line-height:1.5rem}.chat-box{list-style:none;margin:0 .5rem;padding:0 0 3.5rem}.chat-box.group{margin-left:-.4rem}.chat-box li{align-items:flex-end;display:flex;margin:0;overflow:hidden;padding:0}.chat-box .right{justify-content:flex-end}.chat-box .bubble{border-radius:.5rem;display:flex;height:auto;margin:0;max-width:40rem;min-width:12rem;padding:.75rem .5rem .75rem 1.25rem;position:relative;text-align:left}.chat-box li.single{margin-bottom:.75rem;margin-top:.25rem}.chat-box li.first{margin-bottom:.15rem;margin-top:.25rem}.chat-box li.middle{margin-bottom:.15rem;margin-top:.15rem}.chat-box li.last{margin-bottom:.75rem;margin-top:.15rem}.chat-box .left .bubble{background-color:#c5e1a5}.chat-box .right .bubble{background-color:#fafafa}.chat-box .left .tip:before{color:#c5e1a5;left:-10px;right:auto}.chat-box .left .tip:before,.chat-box .right .tip:before{border:10px solid transparent;border-bottom-color:currentColor;bottom:0;content:"";height:0;position:absolute;top:auto;width:0}.chat-box .right .tip:before{color:#fafafa;display:flex;justify-content:space-between;left:auto;right:-10px}.chat-box .avatar-box{font-size:.8rem;height:1.4rem;margin-bottom:.8rem;margin-right:.8rem;min-height:1.4rem;min-width:1.4rem;position:relative;width:1.4rem}.chat-box .bubble div.message-content{color:#212121;margin:0;max-width:36rem;overflow-wrap:break-word}.chat-box .bubble div.message-content:after{clear:both;content:".";display:block;height:0;visibility:hidden}.chat-box .timestamp{color:#777;display:inline-block;float:right;font-size:80%;padding-left:1rem;position:relative;top:.5rem;white-space:nowrap}.chat-box .typing{border-radius:.5rem;color:#aaa;display:inline-block;margin-top:.75rem;padding:.25rem .35rem;text-align:center;vertical-align:middle;width:2rem}.chat-box .author{color:#777;font-size:80%}.inline-image{border-radius:.1rem;cursor:pointer;display:block;max-height:24rem;max-width:36rem;overflow:hidden}.attachment{display:flex}.attachment:last-of-type{margin-bottom:-.45rem}#info-caption-panel{background-color:#b0bec5}#info-caption-panel a{color:#757575;line-height:1;text-decoration:none}#info-caption-panel :hover{color:#000}#info-title{margin-right:auto}#group-manager{display:flex;flex-direction:column;height:100%;position:relative}#group-manager-buttons{background-color:#eceff1;border-left:1px solid #ccc;box-shadow:0 -1px rgba(0,0,0,.1);display:flex;height:4rem;justify-content:space-around;padding:.4rem;z-index:2}.dialog-overlay{background-color:hsla(0,0%,100%,.9);height:100%;left:0;position:absolute;top:0;width:100%;z-index:10}.dialog-overlay,.modal-dialog{display:flex;flex-direction:column}.modal-dialog{background-color:#fff;border-radius:.3rem;box-shadow:0 .5rem 1rem 0 rgba(0,0,0,.25);justify-content:space-between;margin:auto;min-height:16rem;min-width:24rem}.modal-dialog .title-panel{display:flex;font-size:1.4rem;justify-content:space-between;padding:1rem}.modal-dialog .body-panel{margin:auto .5rem}.modal-dialog .dialog-buttons{background-color:#fafafa;border-top:1px solid #f5f5f5}.modal-dialog .dialog-buttons a{display:inline-block;font-size:1.1rem;padding:.35rem 1.5rem;text-decoration:none;text-transform:uppercase}ul.tabbar{background:#eee;flex-grow:0;flex-shrink:0;list-style:none;margin:0;overflow:hidden;padding:0 0 0 1rem;width:100%}ul.tabbar>li{float:left}ul.tabbar>li>a{box-shadow:inset 0 -2px 2.5px -1px rgba(0,0,0,.25);color:rgba(33,150,243,.6);display:block;font-size:1.3rem;font-variant:small-caps;font-weight:400;line-height:2rem;overflow:hidden;padding-left:1rem;padding-right:1rem;padding-top:.75rem;position:relative;text-align:center;text-decoration:none}ul.tabbar li.active>a{color:#2196f3}ul.tabbar li.active>a:after{animation:tabbar-border-expand .2s cubic-bezier(.4,0,.4,1) 0s alternate forwards;background:#2196f3;bottom:0;content:"";display:block;height:.15rem;left:0;position:absolute}@keyframes tabbar-border-expand{0%{opacity:0;width:0}to{opacity:1;width:100%}}.unread{background-color:#0097a7;border-radius:50%;color:#fff;font-weight:bolder;height:1.35rem;line-height:1.35rem;margin-left:.5rem;text-align:center;vertical-align:middle;width:1.35rem}.badge,.unread{display:inline-block;font-size:.75rem}.badge{align-self:flex-start;background-color:#eee;border:1px solid #666;border-radius:.15rem;color:#666;font-family:Roboto Mono,Courier,monospace,sans-serif;margin-bottom:.1rem;margin-left:.1rem;margin-right:.1rem;padding:0 .15rem}.badge.green{background-color:#efe;border-color:#383;color:#383}.badge.yellow{background-color:#ffe;border-color:#660;color:#660}.badge.blue{background-color:#eef;border-color:#339;color:#339}.badge.red{background-color:#fee;border-color:#933;color:#933}.badge.magenta{background-color:#fef;border-color:#939;color:#939}.lettertile{border-radius:50%;padding-bottom:100%;width:100%}.lettertile>div{align-items:center;border-radius:50%;bottom:0;color:hsla(0,0%,98%,.8);display:flex;flex-grow:0;flex-shrink:0;justify-content:center;left:0;line-height:1;position:absolute;right:0;text-align:center;text-transform:uppercase;top:0}.lettertile:hover{color:#fff}.lettertile.color0{background-color:#ef9a9a}.lettertile.color1{background-color:#90caf9}.lettertile.color2{background-color:#b0bec5}.lettertile.color3{background-color:#b39ddb}.lettertile.color4{background-color:#ffab91}.lettertile.color5{background-color:#a5d6a7}.lettertile.color6{background-color:#eee}.lettertile.color7{background-color:#e6ee9c}.lettertile.color8{background-color:#c5e1a5}.lettertile.color9{background-color:#f0e57d}.lettertile.color10{background-color:#f48fb1}.lettertile.color11{background-color:#9fa8da}.lettertile.color12{background-color:#ffe082}.lettertile.color13{background-color:#bcaaa4}.lettertile.color14{background-color:#80deea}.lettertile.color15{background-color:#ce93d8}.lettertile.dark-color0{background-color:#c62828}.lettertile.dark-color1{background-color:#ad1457}.lettertile.dark-color2{background-color:#6a1b9a}.lettertile.dark-color3{background-color:#4527a0}.lettertile.dark-color4{background-color:#283593}.lettertile.dark-color5{background-color:#1565c0}.lettertile.dark-color6{background-color:#0277bd}.lettertile.dark-color7{background-color:#00838f}.lettertile.dark-color8{background-color:#00695c}.lettertile.dark-color9{background-color:#2e7d32}.lettertile.dark-color10{background-color:#558b2f}.lettertile.dark-color11{background-color:#9e9d24}.lettertile.dark-color12{background-color:#f9a825}.lettertile.dark-color13{background-color:#ff8f00}.lettertile.dark-color14{background-color:#ef6c00}.lettertile.dark-color15{background-color:#d84315}.material-icons{font-size:1.4rem;line-height:1;text-align:center;vertical-align:middle}.material-icons.small{font-size:1rem}.material-icons.big{font-size:2.4rem}#self-avatar.material-icons,.avatar-box .material-icons{font-size:2.4rem;height:3rem;line-height:3rem;width:3rem}.avatar-box .material-icons{background-color:silver;border-radius:50%;color:hsla(0,0%,98%,.8)}.chip-input{align-items:center;background:transparent;border-bottom:1px solid #ccc;box-shadow:0 1px 0 rgba(0,0,0,.1);color:#666;cursor:text;display:flex;flex-grow:1;flex-wrap:wrap;font-size:1.1rem;font-weight:400;justify-content:flex-start;margin:.25rem 0;max-height:10rem;min-height:4rem;outline:none;overflow-y:scroll;text-align:center;transition:all .1s ease-in-out}.chip-input.focused{border-bottom:1px solid #2196f3;box-shadow:0 1px 0 rgba(33,150,243,.25)}.chip-input>input[type=text]{border:none;box-shadow:none;display:inline-block;flex:1 0 auto;min-width:4rem;width:auto}.chip,.chip-input>input[type=text]{height:1.8rem;line-height:1.8;margin:.125rem}.chip{align-items:center;background-color:#eee;border-radius:.9rem;display:flex;flex:0 0 auto;font-size:90%;white-space:nowrap}.chip>a{background-color:#aaa;border-radius:50%;color:#ccc;display:inline-block;height:1.175rem;line-height:1.15;margin:.3rem;text-decoration:none;width:1.175rem}.chip>a:focus,.chip>a:hover{background-color:#bbb;color:#ddd;text-decoration:none}.chip .avatar-box{font-size:1rem;height:1.8rem;margin-right:.2rem;min-height:1.8rem;min-width:1.8rem;position:relative;width:1.8rem}.chip .avatar-box .material-icons{font-size:1.8rem}.chip .spacer{display:inline-block;height:1.175rem;margin:.3rem;width:.25rem}.load-spinner-box{background-color:#fff;border-radius:50%;box-shadow:.1rem .1rem .15rem 1px rgba(0,0,0,.25);height:2.5rem;left:0;margin-left:auto;margin-right:auto;padding:.25rem;position:absolute;right:0;top:5rem;width:2.5rem;z-index:2}.loader-spinner{animation:spin 1.5s linear infinite;border:.35rem solid #f3f3f3;border-radius:50%;border-top-color:#1e88e5;height:2rem;width:2rem}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.uploader{display:flex}.uploader>div{background-color:#cfd8dc;border-radius:.1rem;height:.6rem;line-height:0;margin:.35rem;padding:.15rem;position:relative;width:6rem}.uploader>div>span{background-color:#1e88e5;border-radius:.08rem;display:inline-block;height:.3rem} \ No newline at end of file diff --git a/index-dev.html b/index-dev.html index 1d0bad3e..25e94351 100644 --- a/index-dev.html +++ b/index-dev.html @@ -1,8 +1,4 @@ - - + @@ -17,39 +13,36 @@ - - + + - + - + - - - - - - + + + + + + + + +
- - - - - - - + + diff --git a/index.html b/index.html index b122f711..90514fdb 100644 --- a/index.html +++ b/index.html @@ -1,32 +1,35 @@ - - - Tinode - - - - - - - - - - - - - - - - - - - - - - -
- - - + + +Tinode + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json index 584b0c64..d6bf6fc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,1046 @@ { "name": "tinode-webapp", - "version": "0.15.8", + "version": "0.15.9-rc1", "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.2.tgz", + "integrity": "sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.1.2", + "@babel/helpers": "^7.1.2", + "@babel/parser": "^7.1.2", + "@babel/template": "^7.1.2", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.1.2", + "convert-source-map": "^1.1.0", + "debug": "^3.1.0", + "json5": "^0.5.0", + "lodash": "^4.17.10", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.3.tgz", + "integrity": "sha512-ZoCZGcfIJFJuZBqxcY9OjC1KW2lWK64qrX1o4UYL3yshVhwKFYgzpWZ0vvtGMNJdTlvkw0W+HR1VnYN8q3QPFQ==", + "dev": true, + "requires": { + "@babel/types": "^7.1.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", + "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-builder-react-jsx": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.0.0.tgz", + "integrity": "sha512-ebJ2JM6NAKW0fQEqN8hOLxK84RbRz9OkUhGS/Xd5u56ejMfVbayJ4+LykERZCOUM6faa6Fp3SZNX3fcT16MKHw==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "esutils": "^2.0.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz", + "integrity": "sha512-YEtYZrw3GUK6emQHKthltKNZwszBcHK58Ygcis+gVUrF4/FmTVr5CCqQNSfmvg2y+YDEANyYoaLz/SHsnusCwQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.0.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-define-map": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz", + "integrity": "sha512-yPPcW8dc3gZLN+U1mhYV91QU3n5uTbx7DUdf8NnPbjS0RMwBuHi9Xt2MUgppmNz7CJxTBWsGczTiEp1CSOTPRg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/types": "^7.0.0", + "lodash": "^4.17.10" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", + "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0.tgz", + "integrity": "sha512-Ggv5sldXUeSKsuzLkddtyhyHe2YantsxWKNi7A+7LeD12ExRDWTRk29JCXpaHPAbMaIPZSil7n+lq78WY2VY7w==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", + "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz", + "integrity": "sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0", + "lodash": "^4.17.10" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.0.0.tgz", + "integrity": "sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-wrap-function": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.1.0.tgz", + "integrity": "sha512-BvcDWYZRWVuDeXTYZWxekQNO5D4kO55aArwZOTFXw6rlLQA8ZaDicJR1sO47h+HrnCiDFiww0fSPV0d713KBGQ==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", + "dev": true, + "requires": { + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-wrap-function": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz", + "integrity": "sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helpers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.2.tgz", + "integrity": "sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA==", + "dev": true, + "requires": { + "@babel/template": "^7.1.2", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.1.2" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.3.tgz", + "integrity": "sha512-gqmspPZOMW3MIRb9HlrnbZHXI1/KHTOroBwN1NcLL6pWxzqzEKGvRTq0W/PxS45OtQGbaFikSQpkS5zbnsQm2w==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz", + "integrity": "sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0", + "@babel/plugin-syntax-async-generators": "^7.0.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz", + "integrity": "sha512-kfVdUkIAGJIVmHmtS/40i/fg/AGnw/rsZBCaapY5yjeO5RA9m165Xbw9KMOu2nqXP5dTFjEjHdfNdoVcHv133Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-json-strings": "^7.0.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz", + "integrity": "sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.0.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0.tgz", + "integrity": "sha512-JPqAvLG1s13B/AuoBjdBYvn38RqW6n1TzrQO839/sIpqLpbnXKacsAgpZHzLD83Sm8SDXMkkrAvEnJ25+0yIpw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.0.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz", + "integrity": "sha512-tM3icA6GhC3ch2SkmSxv7J/hCWKISzwycub6eGsDrFDgukD4dZ/I+x81XgW0YslS6mzNuQ1Cbzh5osjIMgepPQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0", + "regexpu-core": "^4.2.0" + }, + "dependencies": { + "regexpu-core": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.2.0.tgz", + "integrity": "sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^7.0.0", + "regjsgen": "^0.4.0", + "regjsparser": "^0.3.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.0.2" + } + }, + "regjsgen": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.4.0.tgz", + "integrity": "sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA==", + "dev": true + }, + "regjsparser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.3.0.tgz", + "integrity": "sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz", + "integrity": "sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz", + "integrity": "sha512-UlSfNydC+XLj4bw7ijpldc1uZ/HB84vw+U6BTuqMdIEmz/LDe63w/GHtpQMdXWdqQZFeAI9PjnHe/vDhwirhKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.0.0.tgz", + "integrity": "sha512-PdmL2AoPsCLWxhIr3kG2+F9v4WH06Q3z+NoGVpQgnUNGcagXHq5sB3OXxkSahKq9TLdNMN/AJzFYSOo8UKDMHg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz", + "integrity": "sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0.tgz", + "integrity": "sha512-Wc+HVvwjcq5qBg1w5RG9o9RVzmCaAg/Vp0erHCKpAYV8La6I94o4GQAmFYNmkzoMO6gzoOSulpKeSSz6mPEoZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz", + "integrity": "sha512-2EZDBl1WIO/q4DIkIp4s86sdp4ZifL51MoIviLY/gG/mLSuOIEg7J8o6mhbxOTvUJkaN50n+8u41FVsr5KLy/w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz", + "integrity": "sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0.tgz", + "integrity": "sha512-AOBiyUp7vYTqz2Jibe1UaAWL0Hl9JUXEgjFvvvcSc9MVDItv46ViXFw2F7SVt1B5k+KWjl44eeXOAk3UDEaJjQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0.tgz", + "integrity": "sha512-GWEMCrmHQcYWISilUrk9GDqH4enf3UmhOEbNbNrlNAX1ssH3MsS1xLOS6rdjRVPgA7XXVPn87tRkdTEoA/dxEg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "lodash": "^4.17.10" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.1.0.tgz", + "integrity": "sha512-rNaqoD+4OCBZjM7VaskladgqnZ1LO6o2UxuWSDzljzW21pN1KXkB7BstAVweZdxQkHAujps5QMNOTWesBciKFg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-define-map": "^7.1.0", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", + "dev": true + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0.tgz", + "integrity": "sha512-ubouZdChNAv4AAWAgU7QKbB93NU5sHwInEWfp+/OzJKA02E6Woh9RVoX4sZrbRwtybky/d7baTUqwFx+HgbvMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.1.3.tgz", + "integrity": "sha512-Mb9M4DGIOspH1ExHOUnn2UUXFOyVTiX84fXCd+6B5iWrQg/QMeeRmSwpZ9lnjYLSXtZwiw80ytVMr3zue0ucYw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0.tgz", + "integrity": "sha512-00THs8eJxOJUFVx1w8i1MBF4XH4PsAjKjQ1eqN/uCH3YKwP21GCKfrn6YZFZswbOk9+0cw1zGQPHVc1KBlSxig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0", + "regexpu-core": "^4.1.3" + }, + "dependencies": { + "regexpu-core": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.2.0.tgz", + "integrity": "sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^7.0.0", + "regjsgen": "^0.4.0", + "regjsparser": "^0.3.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.0.2" + } + }, + "regjsgen": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.4.0.tgz", + "integrity": "sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA==", + "dev": true + }, + "regjsparser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.3.0.tgz", + "integrity": "sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0.tgz", + "integrity": "sha512-w2vfPkMqRkdxx+C71ATLJG30PpwtTpW7DDdLqYt2acXU7YjztzeWW2Jk1T6hKqCLYCcEA5UQM/+xTAm+QCSnuQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz", + "integrity": "sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0.tgz", + "integrity": "sha512-TlxKecN20X2tt2UEr2LNE6aqA0oPeMT1Y3cgz8k4Dn1j5ObT8M3nl9aA37LLklx0PBZKETC9ZAf9n/6SujTuXA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.1.0.tgz", + "integrity": "sha512-VxOa1TMlFMtqPW2IDYZQaHsFrq/dDoIjgN098NowhexhZcz3UGlvPgZXuE1jEvNygyWyxRacqDpCZt+par1FNg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0.tgz", + "integrity": "sha512-1NTDBWkeNXgpUcyoVFxbr9hS57EpZYXpje92zv0SUzjdu3enaRwF/l3cmyRnXLtIdyJASyiS6PtybK+CgKf7jA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.1.0.tgz", + "integrity": "sha512-wt8P+xQ85rrnGNr2x1iV3DW32W8zrB6ctuBkYBbf5/ZzJY99Ob4MFgsZDFgczNU76iy9PWsy4EuxOliDjdKw6A==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.1.0.tgz", + "integrity": "sha512-wtNwtMjn1XGwM0AXPspQgvmE6msSJP15CX2RVfpTSTNPLhKhaOjaIfBaVfj4iUZ/VrFSodcFedwtPg/NxwQlPA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.1.3.tgz", + "integrity": "sha512-PvTxgjxQAq4pvVUZF3mD5gEtVDuId8NtWkJsZLEJZMZAW3TvgQl1pmydLLN1bM8huHFVVU43lf0uvjQj9FRkKw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.1.0.tgz", + "integrity": "sha512-enrRtn5TfRhMmbRwm7F8qOj0qEYByqUvTttPEGimcBH4CJHphjyK1Vg7sdU7JjeEmgSpM890IT/efS2nMHwYig==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0.tgz", + "integrity": "sha512-yin069FYjah+LbqfGeTfzIBODex/e++Yfa0rH0fpfam9uTbuEeEOx5GLGr210ggOV77mVRNoeqSYqeuaqSzVSw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.1.0.tgz", + "integrity": "sha512-/O02Je1CRTSk2SSJaq0xjwQ8hG4zhZGNjE8psTsSNPXyLRCODv7/PBozqT5AmQMzp7MI3ndvMhGdqp9c96tTEw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.1.0" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.1.0.tgz", + "integrity": "sha512-vHV7oxkEJ8IHxTfRr3hNGzV446GAb+0hgbA7o/0Jd76s+YzccdWuTU296FOCOl/xweU4t/Ya4g41yWz80RFCRw==", + "dev": true, + "requires": { + "@babel/helper-call-delegate": "^7.1.0", + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.0.0.tgz", + "integrity": "sha512-BX8xKuQTO0HzINxT6j/GiCwoJB0AOMs0HmLbEnAvcte8U8rSkNa/eSCAY+l1OA4JnCVq2jw2p6U8QQryy2fTPg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.0.0.tgz", + "integrity": "sha512-0TMP21hXsSUjIQJmu/r7RiVxeFrXRcMUigbKu0BLegJK9PkYodHstaszcig7zxXfaBji2LYUdtqIkHs+hgYkJQ==", + "dev": true, + "requires": { + "@babel/helper-builder-react-jsx": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.0.0" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.0.0.tgz", + "integrity": "sha512-pymy+AK12WO4safW1HmBpwagUQRl9cevNX+82AIAtU1pIdugqcH+nuYP03Ja6B+N4gliAaKWAegIBL/ymALPHA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.0.0" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.0.0.tgz", + "integrity": "sha512-OSeEpFJEH5dw/TtxTg4nijl4nHBbhqbKL94Xo/Y17WKIf2qJWeIk/QeXACF19lG1vMezkxqruwnTjVizaW7u7w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.0.0" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz", + "integrity": "sha512-sj2qzsEx8KDVv1QuJc/dEfilkg3RRPvPYx/VnKLtItVQRWt1Wqf5eVCOLZm29CiGFfYYsA3VPjfizTCV0S0Dlw==", + "dev": true, + "requires": { + "regenerator-transform": "^0.13.3" + }, + "dependencies": { + "regenerator-transform": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.3.tgz", + "integrity": "sha512-5ipTrZFSq5vU2YoGoww4uaRVAK4wyYC4TSICibbfEPOruUu8FFP7ErV0BjmbIOEpn3O/k9na9UEdYR/3m7N6uA==", + "dev": true, + "requires": { + "private": "^0.1.6" + } + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz", + "integrity": "sha512-g/99LI4vm5iOf5r1Gdxq5Xmu91zvjhEG5+yZDJW268AZELAu4J1EiFLnkSG3yuUsZyOipVOVUKoGPYwfsTymhw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0.tgz", + "integrity": "sha512-L702YFy2EvirrR4shTj0g2xQp7aNwZoWNCkNu2mcoU0uyzMl0XRwDSwzB/xp6DSUFiBmEXuyAyEN16LsgVqGGQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0.tgz", + "integrity": "sha512-LFUToxiyS/WD+XEWpkx/XJBrUXKewSZpzX68s+yEOtIbdnsRjpryDw9U06gYc6klYEij/+KQVRnD3nz3AoKmjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0.tgz", + "integrity": "sha512-vA6rkTCabRZu7Nbl9DfLZE1imj4tzdWcg5vtdQGvj+OH9itNNB6hxuRMHuIY8SGnEt1T9g5foqs9LnrHzsqEFg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0.tgz", + "integrity": "sha512-1r1X5DO78WnaAIvs5uC48t41LLckxsYklJrZjNKcevyz83sF2l4RHbw29qrCPr/6ksFsdfRpT/ZgxNWHXRnffg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0.tgz", + "integrity": "sha512-uJBrJhBOEa3D033P95nPHu3nbFwFE9ZgXsfEitzoIXIwqAZWk7uXcg06yFKXz9FSxBH5ucgU/cYdX0IV8ldHKw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0", + "regexpu-core": "^4.1.3" + }, + "dependencies": { + "regexpu-core": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.2.0.tgz", + "integrity": "sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^7.0.0", + "regjsgen": "^0.4.0", + "regjsparser": "^0.3.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.0.2" + } + }, + "regjsgen": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.4.0.tgz", + "integrity": "sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA==", + "dev": true + }, + "regjsparser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.3.0.tgz", + "integrity": "sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "@babel/preset-env": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.1.0.tgz", + "integrity": "sha512-ZLVSynfAoDHB/34A17/JCZbyrzbQj59QC1Anyueb4Bwjh373nVPq5/HMph0z+tCmcDjXDe+DlKQq9ywQuvWrQg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.1.0", + "@babel/plugin-proposal-json-strings": "^7.0.0", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.0.0", + "@babel/plugin-syntax-async-generators": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.0.0", + "@babel/plugin-transform-arrow-functions": "^7.0.0", + "@babel/plugin-transform-async-to-generator": "^7.1.0", + "@babel/plugin-transform-block-scoped-functions": "^7.0.0", + "@babel/plugin-transform-block-scoping": "^7.0.0", + "@babel/plugin-transform-classes": "^7.1.0", + "@babel/plugin-transform-computed-properties": "^7.0.0", + "@babel/plugin-transform-destructuring": "^7.0.0", + "@babel/plugin-transform-dotall-regex": "^7.0.0", + "@babel/plugin-transform-duplicate-keys": "^7.0.0", + "@babel/plugin-transform-exponentiation-operator": "^7.1.0", + "@babel/plugin-transform-for-of": "^7.0.0", + "@babel/plugin-transform-function-name": "^7.1.0", + "@babel/plugin-transform-literals": "^7.0.0", + "@babel/plugin-transform-modules-amd": "^7.1.0", + "@babel/plugin-transform-modules-commonjs": "^7.1.0", + "@babel/plugin-transform-modules-systemjs": "^7.0.0", + "@babel/plugin-transform-modules-umd": "^7.1.0", + "@babel/plugin-transform-new-target": "^7.0.0", + "@babel/plugin-transform-object-super": "^7.1.0", + "@babel/plugin-transform-parameters": "^7.1.0", + "@babel/plugin-transform-regenerator": "^7.0.0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0", + "@babel/plugin-transform-spread": "^7.0.0", + "@babel/plugin-transform-sticky-regex": "^7.0.0", + "@babel/plugin-transform-template-literals": "^7.0.0", + "@babel/plugin-transform-typeof-symbol": "^7.0.0", + "@babel/plugin-transform-unicode-regex": "^7.0.0", + "browserslist": "^4.1.0", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.3.0" + }, + "dependencies": { + "browserslist": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.3.3.tgz", + "integrity": "sha512-6h84UD1mmHeuQ9IucX6yzBc+KBYcBBTLYt2CXtY7GYCra6iE5kOm7oM+zuGw/0tjGtbJxjm58OvxSBmogEMCRQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000898", + "electron-to-chromium": "^1.3.81", + "node-releases": "^1.0.0-alpha.15" + } + }, + "caniuse-lite": { + "version": "1.0.30000898", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000898.tgz", + "integrity": "sha512-ytlTZqO4hYe4rNAJhMynUAIUI33jsP2Bb1two/9OVC39wZjPZ8exIO0eCLw5mqAtegOGiGF0kkTWTn3B02L+mw==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.81", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.81.tgz", + "integrity": "sha512-+rym2xtzwPWmoi8AYRrCdW65QOT0vfUHjZb5mjgh0VLyj31pGM3CpP3znKhQNBzQaWujR/KEl/mfC2lnKYgADA==", + "dev": true + }, + "node-releases": { + "version": "1.0.0-alpha.15", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.0-alpha.15.tgz", + "integrity": "sha512-hKG6hd/g6a9OV/ARt2qrxbRhe/4WEMFohTLOB9PNyTYvvI59gICZFzt9/mMgpYUTts06qXlN8H6UjfbIRdnW8A==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + } + } + }, + "@babel/preset-react": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", + "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0" + } + }, + "@babel/runtime": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.1.2.tgz", + "integrity": "sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.12.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", + "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.1.2", + "@babel/types": "^7.1.2" + } + }, + "@babel/traverse": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.4.tgz", + "integrity": "sha512-my9mdrAIGdDiSVBuMjpn/oXYpva0/EZwWL3sm3Wcy/AVWO2eXnsoZruOT9jOGNRXU8KbCIu5zsKnXcAJ6PcV6Q==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.1.3", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.1.3", + "@babel/types": "^7.1.3", + "debug": "^3.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz", + "integrity": "sha512-RpPOVfK+yatXyn8n4PB1NW6k9qjinrXrRR8ugBN8fD6hCy5RXI6PSbVqpOJBO9oSaY7Nom4ohj35feb0UR9hSA==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "@firebase/app": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.3.4.tgz", @@ -153,6 +1190,223 @@ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.11.tgz", "integrity": "sha512-WyMXDxk/WZ+f2lOCeEvDWUce2f5Kk2sNfvArK8f+PlUnzFdy/MBzLXrmbMgyZXP7GP4ooUxYV8Sdmoh1hGk1Uw==" }, + "@webassemblyjs/ast": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.10.tgz", + "integrity": "sha512-wTUeaByYN2EA6qVqhbgavtGc7fLTOx0glG2IBsFlrFG51uXIGlYBTyIZMf4SPLo3v1bgV/7lBN3l7Z0R6Hswew==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/wast-parser": "1.7.10" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.10.tgz", + "integrity": "sha512-gMsGbI6I3p/P1xL2UxqhNh1ga2HCsx5VBB2i5VvJFAaqAjd2PBTRULc3BpTydabUQEGlaZCzEUQhLoLG7TvEYQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.10.tgz", + "integrity": "sha512-DoYRlPWtuw3yd5BOr9XhtrmB6X1enYF0/54yNvQWGXZEPDF5PJVNI7zQ7gkcKfTESzp8bIBWailaFXEK/jjCsw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.10.tgz", + "integrity": "sha512-+RMU3dt/dPh4EpVX4u5jxsOlw22tp3zjqE0m3ftU2tsYxnPULb4cyHlgaNd2KoWuwasCQqn8Mhr+TTdbtj3LlA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.10.tgz", + "integrity": "sha512-UiytbpKAULOEab2hUZK2ywXen4gWJVrgxtwY3Kn+eZaaSWaRM8z/7dAXRSoamhKFiBh1uaqxzE/XD9BLlug3gw==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.7.10" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.10.tgz", + "integrity": "sha512-w2vDtUK9xeSRtt5+RnnlRCI7wHEvLjF0XdnxJpgx+LJOvklTZPqWkuy/NhwHSLP19sm9H8dWxKeReMR7sCkGZA==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.10.tgz", + "integrity": "sha512-yE5x/LzZ3XdPdREmJijxzfrf+BDRewvO0zl8kvORgSWmxpRrkqY39KZSq6TSgIWBxkK4SrzlS3BsMCv2s1FpsQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.10.tgz", + "integrity": "sha512-u5qy4SJ/OrxKxZqJ9N3qH4ZQgHaAzsopsYwLvoWJY6Q33r8PhT3VPyNMaJ7ZFoqzBnZlCcS/0f4Sp8WBxylXfg==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.10.tgz", + "integrity": "sha512-Ecvww6sCkcjatcyctUrn22neSJHLN/TTzolMGG/N7S9rpbsTZ8c6Bl98GpSpV77EvzNijiNRHBG0+JO99qKz6g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-buffer": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/wasm-gen": "1.7.10" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.10.tgz", + "integrity": "sha512-HRcWcY+YWt4+s/CvQn+vnSPfRaD4KkuzQFt5MNaELXXHSjelHlSEA8ZcqT69q0GTIuLWZ6JaoKar4yWHVpZHsQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.10.tgz", + "integrity": "sha512-og8MciYlA8hvzCLR71hCuZKPbVBfLQeHv7ImKZ4nlyxrYbG7uJHYtHiHu6OV9SqrGuD03H/HtXC4Bgdjfm9FHw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.1" + } + }, + "@webassemblyjs/utf8": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.10.tgz", + "integrity": "sha512-Ng6Pxv6siyZp635xCSnH3mKmIFgqWPCcGdoo0GBYgyGdxu7cUj4agV7Uu1a8REP66UYUFXJLudeGgd4RvuJAnQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.10.tgz", + "integrity": "sha512-e9RZFQlb+ZuYcKRcW9yl+mqX/Ycj9+3/+ppDI8nEE/NCY6FoK8f3dKBcfubYV/HZn44b+ND4hjh+4BYBt+sDnA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-buffer": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/helper-wasm-section": "1.7.10", + "@webassemblyjs/wasm-gen": "1.7.10", + "@webassemblyjs/wasm-opt": "1.7.10", + "@webassemblyjs/wasm-parser": "1.7.10", + "@webassemblyjs/wast-printer": "1.7.10" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.10.tgz", + "integrity": "sha512-M0lb6cO2Y0PzDye/L39PqwV+jvO+2YxEG5ax+7dgq7EwXdAlpOMx1jxyXJTScQoeTpzOPIb+fLgX/IkLF8h2yw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/ieee754": "1.7.10", + "@webassemblyjs/leb128": "1.7.10", + "@webassemblyjs/utf8": "1.7.10" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.10.tgz", + "integrity": "sha512-R66IHGCdicgF5ZliN10yn5HaC7vwYAqrSVJGjtJJQp5+QNPBye6heWdVH/at40uh0uoaDN/UVUfXK0gvuUqtVg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-buffer": "1.7.10", + "@webassemblyjs/wasm-gen": "1.7.10", + "@webassemblyjs/wasm-parser": "1.7.10" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.10.tgz", + "integrity": "sha512-AEv8mkXVK63n/iDR3T693EzoGPnNAwKwT3iHmKJNBrrALAhhEjuPzo/lTE4U7LquEwyvg5nneSNdTdgrBaGJcA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-api-error": "1.7.10", + "@webassemblyjs/helper-wasm-bytecode": "1.7.10", + "@webassemblyjs/ieee754": "1.7.10", + "@webassemblyjs/leb128": "1.7.10", + "@webassemblyjs/utf8": "1.7.10" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.10.tgz", + "integrity": "sha512-YTPEtOBljkCL0VjDp4sHe22dAYSm3ZwdJ9+2NTGdtC7ayNvuip1wAhaAS8Zt9Q6SW9E5Jf5PX7YE3XWlrzR9cw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/floating-point-hex-parser": "1.7.10", + "@webassemblyjs/helper-api-error": "1.7.10", + "@webassemblyjs/helper-code-frame": "1.7.10", + "@webassemblyjs/helper-fsm": "1.7.10", + "@xtuc/long": "4.2.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.10.tgz", + "integrity": "sha512-mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/wast-parser": "1.7.10", + "@xtuc/long": "4.2.1" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", + "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", + "dev": true + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "ajv": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", + "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, "alphanum-sort": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", @@ -165,22 +1419,30 @@ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } }, "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, - "optional": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -191,14 +1453,10 @@ } }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.0.1" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", @@ -213,11 +1471,10 @@ "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true, - "optional": true + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true }, "ascli": { "version": "1.0.1", @@ -228,6 +1485,43 @@ "optjs": "~3.2.2" } }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -238,8 +1532,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true, - "optional": true + "dev": true }, "atob": { "version": "2.1.2", @@ -247,661 +1540,29 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, - "babel-cli": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz", - "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", + "babel-loader": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.4.tgz", + "integrity": "sha512-fhBhNkUToJcW9nV46v8w87AJOwAJDz84c1CL57n3Stj73FANM/b9TbCUK4YhdOwEyZ+OxhYpdeZDNzSI29Firw==", "dev": true, "requires": { - "babel-core": "^6.26.0", - "babel-polyfill": "^6.26.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "chokidar": "^1.6.1", - "commander": "^2.11.0", - "convert-source-map": "^1.5.0", - "fs-readdir-recursive": "^1.0.0", - "glob": "^7.1.2", - "lodash": "^4.17.4", - "output-file-sync": "^1.1.2", - "path-is-absolute": "^1.0.1", - "slash": "^1.0.0", - "source-map": "^0.5.6", - "v8flags": "^2.1.1" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - } - } - }, - "babel-preset-env": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", - "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^3.2.6", - "invariant": "^2.2.2", - "semver": "^5.3.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", + "find-cache-dir": "^1.0.0", + "loader-utils": "^1.0.2", "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" + "util.promisify": "^1.0.0" } }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "babel-plugin-react-intl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-3.0.1.tgz", + "integrity": "sha512-FqnEO+Tq7kJVUPKsSG3s5jaHi3pAC4RUR11IrscvjsfkOApLP2DtzNo6dtQ+tX+OzEzJx7cUms8aCw5BFyW5xg==", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "@babel/runtime": "^7.0.0", + "intl-messageformat-parser": "^1.2.0", + "mkdirp": "^0.5.1" } }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -959,27 +1620,38 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true } } }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, "binary-extensions": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", - "dev": true, - "optional": true + "dev": true + }, + "bluebird": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true }, "boolbase": { "version": "1.0.0", @@ -997,27 +1669,168 @@ } }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, - "optional": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" } }, "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.3.3.tgz", + "integrity": "sha512-6h84UD1mmHeuQ9IucX6yzBc+KBYcBBTLYt2CXtY7GYCra6iE5kOm7oM+zuGw/0tjGtbJxjm58OvxSBmogEMCRQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" + "caniuse-lite": "^1.0.30000898", + "electron-to-chromium": "^1.3.81", + "node-releases": "^1.0.0-alpha.15" + }, + "dependencies": { + "caniuse-lite": { + "version": "1.0.30000899", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000899.tgz", + "integrity": "sha512-enC3zKfUCJxxwvUIsBkbHd54CtJw1KtIWvrK0JZxWD/fEN2knHaai45lndJ4xXAkyRAPyk60J3yagkKDWhfeMA==", + "dev": true + }, + "node-releases": { + "version": "1.0.0-alpha.15", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.0-alpha.15.tgz", + "integrity": "sha512-hKG6hd/g6a9OV/ARt2qrxbRhe/4WEMFohTLOB9PNyTYvvI59gICZFzt9/mMgpYUTts06qXlN8H6UjfbIRdnW8A==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + } } }, + "buffer": { + "version": "4.9.1", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, "bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", @@ -1026,6 +1839,35 @@ "long": "~3" } }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + }, + "dependencies": { + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + } + } + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -1041,14 +1883,6 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "camelcase": { @@ -1102,34 +1936,60 @@ "dev": true }, "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "dev": true, - "optional": true, "requires": { - "anymatch": "^1.3.0", + "anymatch": "^2.0.0", "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", "inherits": "^2.0.1", "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "class-utils": { @@ -1152,12 +2012,6 @@ "requires": { "is-descriptor": "^0.1.0" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true } } }, @@ -1242,9 +2096,15 @@ "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=" }, "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, "component-emitter": { @@ -1258,13 +2118,51 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "safe-buffer": "~5.1.1" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" } }, "copy-descriptor": { @@ -1273,18 +2171,80 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", "dev": true, - "optional": true + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } }, "css-color-names": { "version": "0.0.4", @@ -1649,6 +2609,18 @@ } } }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1717,28 +2689,28 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true } } }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { - "repeating": "^2.0.0" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" } }, "dom-serializer": { @@ -1764,6 +2736,12 @@ "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==" }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, "domelementtype": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", @@ -1789,12 +2767,45 @@ "is-obj": "^1.0.0" } }, + "duplexify": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, "electron-to-chromium": { "version": "1.3.82", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.82.tgz", "integrity": "sha512-NI4nB2IWGcU4JVT1AE8kBb/dFor4zjLHMLsOROPahppeHrR0FG5uslxMmkp/thO1MvPjM2xhlKoY29/I60s0ew==", "dev": true }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, "encoding": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", @@ -1803,12 +2814,41 @@ "iconv-lite": "~0.4.13" } }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, "entities": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "dev": true }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1848,36 +2888,107 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "events": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, - "optional": true, "requires": { - "is-posix-bracket": "^0.1.0" + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" } }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", "dev": true, - "optional": true, "requires": { - "fill-range": "^2.1.0" + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "extend-shallow": { @@ -1902,15 +3013,82 @@ } }, "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, - "optional": true, "requires": { - "is-extglob": "^1.0.0" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } } }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, "faye-websocket": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", @@ -1919,25 +3097,47 @@ "websocket-driver": ">=0.5.1" } }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true, - "optional": true - }, "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, - "optional": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" } }, "firebase": { @@ -1961,22 +3161,22 @@ "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", "dev": true }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "optional": true, - "requires": { - "for-in": "^1.0.1" - } - }, "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", @@ -1992,11 +3192,27 @@ "map-cache": "^0.2.2" } }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } }, "fs.realpath": { "version": "1.0.0", @@ -2538,6 +3754,18 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -2557,30 +3785,31 @@ "path-is-absolute": "^1.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "optional": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "global-modules-path": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.0.tgz", + "integrity": "sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag==", "dev": true }, "graceful-fs": { @@ -2602,23 +3831,19 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "bundled": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "bundled": true }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + "bundled": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "bundled": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -2626,13 +3851,11 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "bundled": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "bundled": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2640,69 +3863,57 @@ }, "chownr": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + "bundled": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "bundled": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "bundled": true }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "bundled": true }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "bundled": true, "requires": { "ms": "2.0.0" } }, "deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + "bundled": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "bundled": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + "bundled": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", - "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "bundled": true, "requires": { "minipass": "^2.2.1" } }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "bundled": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "bundled": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -2716,8 +3927,7 @@ }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "bundled": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2729,29 +3939,25 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + "bundled": true }, "iconv-lite": { "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "bundled": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "bundled": true, "requires": { "minimatch": "^3.0.4" } }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "bundled": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2759,44 +3965,37 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "bundled": true }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "bundled": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "bundled": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "bundled": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "bundled": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "bundled": true }, "minipass": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.3.tgz", - "integrity": "sha512-/jAn9/tEX4gnpyRATxgHEOV6xbcyxgT7iUnxo9Y3+OB0zX00TgKIv/2FZCf5brBbICcwbLqVv2ImjvWWrQMSYw==", + "bundled": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2804,36 +4003,31 @@ }, "minizlib": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", - "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "bundled": true, "requires": { "minipass": "^2.2.1" } }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "bundled": true, "requires": { "minimist": "0.0.8" }, "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "bundled": true } } }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "bundled": true }, "needle": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.1.tgz", - "integrity": "sha512-t/ZswCM9JTWjAdXS9VpvqhI2Ct2sL2MdY4fUXqGJaGBk13ge99ObqRksRTbBE56K+wxUXwwfZYOuZHifFW9q+Q==", + "bundled": true, "requires": { "debug": "^2.1.2", "iconv-lite": "^0.4.4", @@ -2842,8 +4036,7 @@ }, "node-pre-gyp": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", - "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", + "bundled": true, "requires": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", @@ -2859,8 +4052,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "bundled": true, "requires": { "abbrev": "1", "osenv": "^0.1.4" @@ -2868,13 +4060,11 @@ }, "npm-bundled": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", - "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==" + "bundled": true }, "npm-packlist": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.11.tgz", - "integrity": "sha512-CxKlZ24urLkJk+9kCm48RTQ7L4hsmgSVzEk0TLGPzzyuFxD7VNgy5Sl24tOLMzQv773a/NeJ1ce1DKeacqffEA==", + "bundled": true, "requires": { "ignore-walk": "^3.0.1", "npm-bundled": "^1.0.1" @@ -2882,8 +4072,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "bundled": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -2893,36 +4082,30 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "bundled": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "bundled": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "bundled": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + "bundled": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "bundled": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "bundled": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -2930,18 +4113,15 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "bundled": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "bundled": true }, "rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "bundled": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -2951,8 +4131,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2965,46 +4144,38 @@ }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "bundled": true, "requires": { "glob": "^7.0.5" } }, "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "bundled": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "bundled": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "bundled": true }, "semver": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + "bundled": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "bundled": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "bundled": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3013,29 +4184,25 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "requires": { "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "bundled": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "bundled": true }, "tar": { "version": "4.4.4", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.4.tgz", - "integrity": "sha512-mq9ixIYfNF9SK0IS/h2HKMu8Q2iaCuhDDsZhdEag/FHv8fOaYld4vN7ouMgcSSt5WKZzPs8atclTcJm36OTh4w==", + "bundled": true, "requires": { "chownr": "^1.0.1", "fs-minipass": "^1.2.5", @@ -3048,26 +4215,22 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "bundled": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "bundled": true, "requires": { "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "bundled": true }, "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + "bundled": true } } }, @@ -3080,15 +4243,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -3104,14 +4258,6 @@ "get-value": "^2.0.6", "has-values": "^1.0.0", "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "has-values": { @@ -3124,26 +4270,6 @@ "kind-of": "^4.0.0" }, "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -3155,22 +4281,48 @@ } } }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", + "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, "hsl-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", @@ -3194,6 +4346,12 @@ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=" }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3202,12 +4360,100 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", "dev": true }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3222,11 +4468,42 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "intl-format-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.1.0.tgz", + "integrity": "sha1-BKNp/sv61tpgBbrh8UMzMy3PkxY=" + }, + "intl-messageformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", + "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", + "requires": { + "intl-messageformat-parser": "1.4.0" + } + }, + "intl-messageformat-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", + "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=" + }, + "intl-relativeformat": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz", + "integrity": "sha1-AQ8RBYAiUfQKxH0OPhogE0iiVd8=", + "requires": { + "intl-messageformat": "^2.0.0" + } + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -3249,6 +4526,17 @@ "dev": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-arrayish": { @@ -3262,7 +4550,6 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, - "optional": true, "requires": { "binary-extensions": "^1.0.0" } @@ -3300,6 +4587,17 @@ "dev": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-date-object": { @@ -3333,23 +4631,6 @@ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", "dev": true }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true, - "optional": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "optional": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -3357,20 +4638,11 @@ "dev": true }, "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -3380,22 +4652,32 @@ } }, "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "^2.1.1" } }, "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-obj": { @@ -3411,30 +4693,8 @@ "dev": true, "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true, - "optional": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true, - "optional": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -3474,8 +4734,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "optional": true + "dev": true }, "isarray": { "version": "1.0.0", @@ -3483,15 +4742,17 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "optional": true, - "requires": { - "isarray": "1.0.0" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "isomorphic-fetch": { "version": "2.2.1", @@ -3502,6 +4763,12 @@ "whatwg-fetch": ">=0.10.0" } }, + "js-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.4.tgz", + "integrity": "sha512-PxfGzSs0ztShKrUYPIn5r0MtyAhYcCwmndozzpz8YObbPnD1jFxzlBGbRnX2mIu6Z13xN6+PTu05TQFnZFlzow==", + "dev": true + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -3518,9 +4785,9 @@ } }, "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true }, "json-parse-better-errors": { @@ -3529,6 +4796,12 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "json5": { "version": "0.5.1", "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -3536,13 +4809,10 @@ "dev": true }, "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true }, "lcid": { "version": "1.0.0", @@ -3552,11 +4822,44 @@ "invert-kv": "^1.0.0" } }, + "loader-runner": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz", + "integrity": "sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw==", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -3582,6 +4885,34 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", + "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -3597,12 +4928,16 @@ "object-visit": "^1.0.0" } }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, - "optional": true + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } }, "mdn-data": { "version": "1.1.4", @@ -3610,28 +4945,76 @@ "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", "dev": true }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "mem": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", + "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", "dev": true, - "optional": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^1.1.0" } }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3646,6 +5029,24 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", @@ -3676,6 +5077,20 @@ "minimist": "0.0.8" } }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3692,7 +5107,6 @@ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, - "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -3705,31 +5119,20 @@ "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - } } }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -3739,6 +5142,45 @@ "is-stream": "^1.0.1" } }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, "node-releases": { "version": "1.0.0-alpha.11", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.0-alpha.11.tgz", @@ -3763,6 +5205,15 @@ "integrity": "sha512-WvF3Myk0NhXkG8S9bygFM4IC1KOvnVJGq0QoGeoqOYOBeinBZp5ybW3QuYbTc89lkWBMM9ZBO4QGRoc0353kKA==", "dev": true }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, "nth-check": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", @@ -3801,6 +5252,15 @@ "requires": { "is-descriptor": "^0.1.0" } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } } } }, @@ -3817,14 +5277,6 @@ "dev": true, "requires": { "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "object.getownpropertydescriptors": { @@ -3837,17 +5289,6 @@ "es-abstract": "^1.5.1" } }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "optional": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -3855,14 +5296,6 @@ "dev": true, "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "object.values": { @@ -3890,10 +5323,10 @@ "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, "os-locale": { @@ -3904,34 +5337,76 @@ "lcid": "^1.0.0" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", "dev": true }, - "output-file-sync": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", - "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.4", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.0" + "p-try": "^1.0.0" } }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, - "optional": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" } }, "parse-json": { @@ -3950,17 +5425,74 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "optional": true + "dev": true }, "postcss-calc": { "version": "6.0.1", @@ -5666,25 +7198,29 @@ "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true, - "optional": true - }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true, - "optional": true + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true }, "promise-polyfill": { "version": "7.1.2", @@ -5711,38 +7247,94 @@ "yargs": "^3.10.0" } }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", "dev": true, - "optional": true, "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - } + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, "react": { @@ -5767,12 +7359,23 @@ "schedule": "^0.5.0" } }, + "react-intl": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.7.1.tgz", + "integrity": "sha512-Ndk0i04dSplBivA0/fELd8lMvIPuRizKC+F6s1EZ6jELvRKNcx9shWa8A1UIlTQfYSbys5huWCRgmylbW0e57A==", + "requires": { + "hoist-non-react-statics": "^2.5.5", + "intl-format-cache": "^2.0.5", + "intl-messageformat": "^2.1.0", + "intl-relativeformat": "^2.1.0", + "invariant": "^2.1.1" + } + }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -5788,311 +7391,10 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } } }, "reduce-css-calc": { @@ -6111,31 +7413,13 @@ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "dev": true }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "regenerate-unicode-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz", + "integrity": "sha512-s5NGghCE4itSlUS+0WUj88G6cfMVMmH8boTPNvABf8od+2dhT9WDlWu8n01raQAJZMOK8Ch6jSexaRO7swd6aw==", "dev": true, "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "optional": true, - "requires": { - "is-equal-shallow": "^0.1.3" + "regenerate": "^1.4.0" } }, "regex-not": { @@ -6148,40 +7432,6 @@ "safe-regex": "^1.1.0" } }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -6200,15 +7450,42 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { - "is-finite": "^1.0.0" + "path-parse": "^1.0.5" } }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -6233,6 +7510,34 @@ "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6267,12 +7572,34 @@ "object-assign": "^4.1.1" } }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -6296,6 +7623,43 @@ } } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -6313,12 +7677,6 @@ } } }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -6360,7 +7718,6 @@ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, - "optional": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -6372,7 +7729,6 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, - "optional": true, "requires": { "is-descriptor": "^1.0.0" } @@ -6382,7 +7738,6 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, - "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -6392,7 +7747,6 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, - "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -6402,25 +7756,11 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, - "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true } } }, @@ -6429,11 +7769,27 @@ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, - "optional": true, "requires": { "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -6453,15 +7809,6 @@ "urix": "^0.1.0" } }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -6483,6 +7830,15 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -6510,6 +7866,45 @@ } } }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -6525,7 +7920,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6538,6 +7932,12 @@ "ansi-regex": "^2.0.0" } }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, "stylehacks": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.0.tgz", @@ -6632,10 +8032,13 @@ } }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } }, "svgo": { "version": "1.0.5", @@ -6671,6 +8074,31 @@ } } }, + "tapable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz", + "integrity": "sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, "timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", @@ -6678,14 +8106,14 @@ "dev": true }, "tinode-sdk": { - "version": "0.15.8", - "resolved": "https://registry.npmjs.org/tinode-sdk/-/tinode-sdk-0.15.8.tgz", - "integrity": "sha512-itF8ej4Ffe8y3vnd1Kj13ULq9dxJdYmnqw6B7TGJgQANFmqB3fl3qUymteECegUA8uBAb/lmI5NaawAFuTL+Sg==" + "version": "0.15.8-rc1", + "resolved": "https://registry.npmjs.org/tinode-sdk/-/tinode-sdk-0.15.8-rc1.tgz", + "integrity": "sha512-139xdnrkBb6mPU4NiLuvMCZ2Ji/1WmP5WhHI7utdMirewq3l5jnQ0cF86iYBbOMaYhhnLLLsbbm4KjleP+1zHA==" }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, "to-object-path": { @@ -6695,6 +8123,17 @@ "dev": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "to-regex": { @@ -6714,22 +8153,9 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, - "optional": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - } - } } }, "trim-right": { @@ -6743,6 +8169,18 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==" }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", @@ -6753,12 +8191,6 @@ "source-map": "~0.6.1" }, "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6767,6 +8199,58 @@ } } }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz", + "integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz", + "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==", + "dev": true + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -6814,6 +8298,24 @@ "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", "dev": true }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, "unquote": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", @@ -6857,39 +8359,68 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true } } }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "user-home": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", - "dev": true + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true + "dev": true }, "util.promisify": { "version": "1.0.0", @@ -6901,14 +8432,11 @@ "object.getownpropertydescriptors": "^2.0.3" } }, - "v8flags": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", - "dev": true, - "requires": { - "user-home": "^1.1.1" - } + "v8-compile-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", + "dev": true }, "vendors": { "version": "1.0.2", @@ -6916,6 +8444,236 @@ "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", "dev": true }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "webpack": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.23.1.tgz", + "integrity": "sha512-iE5Cu4rGEDk7ONRjisTOjVHv3dDtcFfwitSxT7evtYj/rANJpt1OuC/Kozh1pBa99AUBr1L/LsaNB+D9Xz3CEg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.10", + "@webassemblyjs/helper-module-context": "1.7.10", + "@webassemblyjs/wasm-edit": "1.7.10", + "@webassemblyjs/wasm-parser": "1.7.10", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.1.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.3.0" + } + }, + "webpack-cli": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.2.tgz", + "integrity": "sha512-Cnqo7CeqeSvC6PTdts+dywNi5CRlIPbLx1AoUPK2T6vC1YAugMG3IOoO9DmEscd+Dghw7uRlnzV1KwOe5IrtgQ==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.0", + "global-modules-path": "^2.3.0", + "import-local": "^2.0.0", + "interpret": "^1.1.0", + "loader-utils": "^1.1.0", + "supports-color": "^5.5.0", + "v8-compile-cache": "^2.0.2", + "yargs": "^12.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "os-locale": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", + "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", + "dev": true, + "requires": { + "execa": "^0.10.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "yargs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + } + } + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "websocket-driver": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", @@ -6935,11 +8693,35 @@ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, "window-size": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -6959,11 +8741,29 @@ "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, "yargs": { "version": "3.32.0", "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", @@ -6977,6 +8777,23 @@ "window-size": "^0.1.4", "y18n": "^3.2.0" } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } } } } diff --git a/package.json b/package.json index 13ee88bd..2aeada26 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,37 @@ { "name": "tinode-webapp", "description": "Tinode messenger for the web", - "version": "0.15.8", + "version": "0.15.9-rc1", "devDependencies": { - "babel-cli": "^6.26.0", - "babel-preset-env": "^1.7.0", - "babel-preset-react": "^6.24.1", + "@babel/core": "^7.1.2", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.0.0", + "babel-loader": "^8.0.4", + "babel-plugin-react-intl": "^3.0.1", + "browserslist": "^4.3.3", "cssnano": "^4.0.5", - "uglify-es": "^3.3.9" + "webpack": "^4.23.1", + "webpack-cli": "^3.1.2" }, "repository": { "type": "git", "url": "https://github.com/tinode/example-react-js.git" }, "scripts": { - "build": "npm run vers && npm run build:babel && npm run build:prod && npm run build:dev && npm run build:css", - "build:prod": "browserify ./src/index.js --standalone Webapp | uglifyjs --compress --mangle > ./umd/index.prod.js", - "build:dev": "browserify ./src/index.js --standalone Webapp > ./umd/index.dev.js", - "build:babel": "babel ./src/webapp.jsx --presets react,env --out-file ./src/webapp.es5.js", + "build": "npm run vers && npm run build:prod && npm run build:dev && npm run build:css && npm run build:i18n", "build:css": "postcss css/base.css > css/base.min.css", - "vers": "echo \"{\\\"version\\\": \\\"`node -p -e \"require('./package.json').version\"`\\\"}\" > version.json" + "build:dev": "webpack --mode development --output ./umd/index.dev.js", + "build:prod": "webpack --mode production --output ./umd/index.prod.js", + "build:i18n": "node scripts/flatten-messages.js", + "vers": "node scripts/gen-version.js" }, "files": [ "src/index.js", - "src/webapp.jsx", + "src/config.js", + "src/views/*.jsx", + "src/widgets/*.jsx", + "src/lib/*.js", + "src/i18n/*.json", "umd/index.prod.js", "umd/index.dev.js", "audio/msg.mp3", @@ -38,8 +46,6 @@ "img/badge96.png", "index-dev.html", "index.html", - "index.js", - "index.min.js", "manifest.json", "service-worker.js", "firebase-init.js", @@ -69,6 +75,7 @@ "firebase": "^5.5.5", "react": "^16.5.2", "react-dom": "^16.5.2", - "tinode-sdk": "^0.15.8" + "react-intl": "^2.7.1", + "tinode-sdk": "^0.15.8-rc1" } } diff --git a/scripts/flatten-messages.js b/scripts/flatten-messages.js new file mode 100644 index 00000000..8c3933f5 --- /dev/null +++ b/scripts/flatten-messages.js @@ -0,0 +1,97 @@ +// 1.Flattens messages extracted via the React Intl Babel plugin. An error will be +// thrown if there are messages with the same `id`. The result is a file containing +// `id: message` pairs in "en" locale. +// +// 2. Takes all translations and groups them into a single file. +var fs = require('fs'); +var path = require('path'); +var globSync = require('glob').sync; +var mkdirpSync = require('mkdirp').sync; + +const DEFAULT_TRANSLATION = 'en.json'; +const MESSAGES_PATTERN = './src/i18n/src/**/*.json'; +const LANG_DIR = './src/i18n/'; +const LANG_PATTERN = './src/i18n/*.json'; +const FINAL_FILE = './src/messages.json'; + +const MISSING_MARKER = ':!![MISSING]!!:'; +const OBSOLETE_MARKER = ':!![OBSOLETE]!!:'; + +// The react-intl-babel-plugin saves extracted messages +// into files, one fine per component. Rewrite them into +// a single file en.json. +let messages = globSync(MESSAGES_PATTERN) + .map((filename) => fs.readFileSync(filename, 'utf8')) + .map((file) => JSON.parse(file)) + .reduce((collection, descriptors) => { + descriptors.forEach(({id, defaultMessage}) => { + if (collection.hasOwnProperty(id)) { + throw new Error(`Duplicate message id: ${id}`); + } + collection[id] = defaultMessage; + }); + + return collection; + }, {}); + +let filename = path.join(LANG_DIR, DEFAULT_TRANSLATION); +mkdirpSync(path.dirname(filename)); +fs.writeFileSync(filename, JSON.stringify(messages, null, 2)+"\n"); + +// Find messages present in en.json but missing in translations and vice versa. +// Mark missing or obsolete messages as such in translation files. +globSync(LANG_PATTERN) + .map((filename) => { + const parts = filename.split('/'); + if (parts[parts.length-1] != DEFAULT_TRANSLATION) { + const translated = JSON.parse(fs.readFileSync(filename, 'utf8')); + // Find missing messages. + Object.entries(messages).forEach((ent) => { + if (!translated.hasOwnProperty(ent[0])) { + translated[ent[0]] = MISSING_MARKER + ent[1]; + } + }); + // Find no longer needed messages. + Object.entries(translated).forEach((ent) => { + if (!messages.hasOwnProperty(ent[0])) { + let val = translated[ent[0]]; + if (val.indexOf(MISSING_MARKER) == 0) { + val = val.substring(0, MISSING_MARKER.length); + } + if (val.indexOf(OBSOLETE_MARKER) != 0) { + val = OBSOLETE_MARKER + val; + } + translated[ent[0]] = val; + } + }); + fs.writeFileSync(filename, JSON.stringify(translated, null, 2)+"\n"); + } + }); + +// Combine all translations into a single json {"lang_1": {...}, "lang_2: {...}"} +// while removing messages marked as missing or obsolete. + +// Read all translations. +messages = globSync(LANG_PATTERN) + .map((filename) => { + const parts = filename.split('/'); + const locale = parts[parts.length - 1].split('.json')[0]; + const translated = JSON.parse(fs.readFileSync(filename, 'utf8')); + + // Strip messages prefixed with MISSING or OBSOLETE markers. + for (let id in translated) { + if (translated[id].indexOf(OBSOLETE_MARKER) == 0 || + translated[id].indexOf(MISSING_MARKER) == 0) { + delete translated[id]; + } + } + + return { [locale]: translated }; + }) + .reduce((collection, descriptors) => { + return { ...collection, ...descriptors }; + }, {}); + +// Write file with all translations combined. +mkdirpSync(path.dirname(FINAL_FILE)); +fs.writeFileSync(FINAL_FILE, JSON.stringify(messages, null, 2)+"\n"); diff --git a/scripts/gen-version.js b/scripts/gen-version.js new file mode 100644 index 00000000..b5fdcf91 --- /dev/null +++ b/scripts/gen-version.js @@ -0,0 +1,13 @@ +var fs = require('fs'); +var version = require('../package.json').version; + +fs.writeFile('./src/version.js', + '// This is a generated file. Don\'t edit.\n\n'+ + 'export const PACKAGE_VERSION = "'+version+'";\n', + { flag: 'w' }, + function(err) { + if (err) { + return console.log(err); + } + } +); diff --git a/service-worker.js b/service-worker.js index 561c84e4..7a736a02 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1,7 +1,7 @@ // Must be located at the root. -importScripts("https://unpkg.com/firebase@5.5.5/firebase-app.js"); -importScripts("https://unpkg.com/firebase@5.5.5/firebase-messaging.js"); -importScripts("/firebase-init.js") +importScripts('https://unpkg.com/firebase@5.5.6/firebase-app.js'); +importScripts('https://unpkg.com/firebase@5.5.6/firebase-messaging.js'); +importScripts('firebase-init.js'); firebase.initializeApp(FIREBASE_INIT); @@ -67,3 +67,19 @@ self.addEventListener('notificationclick', function(event) { event.waitUntil(promises); }); + +// This is needed for 'Add to Home Screen'. +self.addEventListener('fetch', function(event) { + event.respondWith( + // Try to find response in cache. + caches.match(event.request) + .then((resp) => { + // If response is found in cache, return it. Otherwise fetch. + return resp || fetch(event.request); + }) + .catch((err) => { + // Something went wrong. + console.log("Service worker Fetch: ", err); + }) + ); +}); diff --git a/src/config.js b/src/config.js new file mode 100644 index 00000000..be09bc00 --- /dev/null +++ b/src/config.js @@ -0,0 +1,52 @@ +import { PACKAGE_VERSION } from './version.js'; + +// Name of this application, used in the User-Agent. +export const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.15'); + +// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own +export const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K'; + +// The array of possible hosts to connect to. +export const KNOWN_HOSTS = {hosted: 'api.tinode.co', local: 'localhost:6060'}; + +// Default host name and port to connect to. +export const DEFAULT_HOST = KNOWN_HOSTS.hosted; + +// Minimum time between two keypress notifications, milliseconds. +export const KEYPRESS_DELAY = 3*1000; +// Delay before sending a {note} for reciving a message, milliseconds. +export const RECEIVED_DELAY = 500; +// Delay before sending a read notification, milliseconds. +export const READ_DELAY = 1000; + +// The shortest allowed tag length. Matches the value on the server. +export const MIN_TAG_LENGTH = 4; + +// Mediaquery breakpoint between desktop and mobile, in px. Should match the value +// in @media (max-size: 640px) in base.css +export const MEDIA_BREAKPOINT = 640; +// Size of css 'rem' unit in pixels. Default 1rem = 10pt = 13px. +export const REM_SIZE = 13; + +// Size of the avatar image: When an avatar image is uploaded, it's resized to +// a square of this size. +export const AVATAR_SIZE = 128; + +// Number of chat messages to fetch in one call. +export const MESSAGES_PAGE = 24; + +// Maximum in-band (included directly into the message) attachment size which fits into +// a message of 256K in size, assuming base64 encoding and 1024 bytes of overhead. +// This is size of an object *before* base64 encoding is applied. +// Increase this limit to a greater value in production, if desired. Also increase +// max_message_size in server config. +// MAX_INBAND_ATTACHMENT_SIZE = base64DecodedLen(max_message_size - overhead); +export const MAX_INBAND_ATTACHMENT_SIZE = 195840; + +// Absolute maximum attachment size to be used with the server = 8MB. Increase to +// something like 100MB in production. +export const MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23; + +// Maximum allowed linear dimension of an inline image in pixels. You may want +// to adjust it to 1600 or 2400 for production. +export const MAX_IMAGE_DIM = 768; diff --git a/src/i18n/en.json b/src/i18n/en.json new file mode 100644 index 00000000..20478240 --- /dev/null +++ b/src/i18n/en.json @@ -0,0 +1,121 @@ +{ + "contacts_not_found": "You have no chats
¯\\\\_(ツ)_/¯", + "full_name_prompt": "Full name, e.g. John Doe", + "email_prompt": "Email, e.g. jdoe@example.com", + "button_sign_up": "Sign up", + "label_your_name": "Your name", + "label_password": "Password", + "password_unchanged_prompt": "Unchanged", + "label_user_id": "Address:", + "label_default_access_mode": "Default access mode:", + "label_message_sound": "Message sound:", + "label_push_notifications": "Notification alerts:", + "label_push_notifications_disabled": "Notification alerts (requires HTTPS):", + "title_tag_manager": "Tags (user discovery)", + "button_logout": "Logout", + "requested_permissions": "Requested", + "granted_permissions": "Granted", + "menu_item_edit_permissions": "Edit permissions", + "label_other_user": "Other", + "title_info": "Info", + "label_topic_name": "Name", + "label_private": "Private comment", + "private_editing_placeholder": "Visible to you only", + "label_muting_topic": "Muted:", + "action_more": "More", + "label_your_permissions": "Your permissions:", + "label_permissions": "Permissions:", + "label_you": "You:", + "label_default_access": "Default access mode:", + "label_group_members": "Group members:", + "button_add_members": "Add members", + "button_leave": "Leave", + "group_has_no_members": "No members", + "action_leave_chat": "Leave", + "login_prompt": "Login", + "password_prompt": "Password", + "stay_logged_in": "Stay logged in", + "forgot_password_link": "Forgot password?", + "button_sign_in": "Sign in", + "label_client": "Client:", + "label_server": "Server:", + "online_now": "online now", + "last_seen_timestamp": "Last seen", + "title_not_found": "Not found", + "unnamed_topic": "Unnamed", + "messages_not_readable": "no access to messages", + "tabtitle_find_user": "find", + "tabtitle_new_group": "new group", + "tabtitle_group_by_id": "by id", + "search_for_contacts": "Use search to find contacts", + "new_password_placeholder": "Enter new password", + "label_reset_password": "Send a password reset email:", + "credential_email_prompt": "Your registration email", + "button_reset": "Reset", + "button_send_request": "Send request", + "label_server_to_use": "Server to use:", + "label_wire_transport": "Wire transport:", + "button_update": "Update", + "sidepanel_title_login": "Sign In", + "sidepanel_title_register": "Create Account", + "sidepanel_title_settings": "Settings", + "sidepanel_title_edit_account": "Edit Account", + "sidepanel_title_newtpk": "Start New Chat", + "sidepanel_title_cred": "Confirm Credentials", + "sidepanel_title_reset": "Reset Password", + "phone_dative": "phone", + "email_dative": "email", + "enter_confirmation_code_prompt": "Enter confirmation code sent to you by {method}:", + "numeric_confirmation_code_prompt": "Numbers only", + "button_confirm": "Confirm", + "invalid_content": "invalid content", + "badge_you": "you", + "badge_owner": "owner", + "menu_item_info": "Info", + "menu_item_clear_messages": "Clear messages", + "menu_item_clear_messages_for_all": "Clear for All", + "menu_item_delete": "Delete", + "menu_item_delete_for_all": "Delete for All", + "menu_item_mute": "Mute", + "menu_item_unmute": "Unmute", + "menu_item_delete_topic": "Delete", + "menu_item_unblock": "Unblock", + "menu_item_block": "Block", + "menu_item_member_delete": "Remove", + "action_cancel": "cancel", + "upload_finishing": "finishing...", + "no_contacts": "You have no contacts :-(", + "contacts_not_found_short": "No contacts match '{query}'", + "title_group_members": "Group Members", + "title_all_contacts": "All Contacts", + "button_ok": "OK", + "button_cancel": "Cancel", + "download_action": "download", + "label_file_name": "File name:", + "label_content_type": "Content type:", + "label_size": "Size:", + "error_invalid_id": "Invalid ID", + "group_user_id_prompt": "Group or User ID", + "button_subscribe": "Subscribe", + "topic_name_editing_placeholder": "Freeform name of the group", + "button_create": "Create", + "permission_join": "Join ({val})", + "permission_read": "Read ({val})", + "permission_write": "Write ({val})", + "permission_pres": "Get notified ({val})", + "permission_admin": "Approve ({val})", + "permission_share": "Share ({val})", + "permission_delete": "Delete ({val})", + "permission_owner": "Owner ({val})", + "title_permissions": "Permissions", + "message_sending": "sending...", + "message_sending_failed": "failed", + "search_placeholder": "List like email:alice@example.com, tel:17025550003...", + "messaging_disabled_prompt": "Messaging disabled", + "new_message_prompt": "New message", + "file_attachment_too_large": "The file size {size} exceeds the {limit} limit.", + "cannot_initiate_file_upload": "Cannot initiate file upload.", + "tags_not_found": "No tags defined. Add some.", + "tags_editor_no_tags": "Add some tags", + "title_manage_tags": "Manage tags" +} diff --git a/src/i18n/ru.json b/src/i18n/ru.json new file mode 100644 index 00000000..4dc60fc2 --- /dev/null +++ b/src/i18n/ru.json @@ -0,0 +1,121 @@ +{ + "contacts_not_found": "Чатов нет
¯\\\\_(ツ)_/¯", + "full_name_prompt": "Полное имя, напр. Иван Петров", + "email_prompt": "Email, напр. ivan@example.com", + "button_sign_up": "Создать счет", + "label_your_name": "Ваше имя", + "label_password": "Пароль", + "password_unchanged_prompt": "Не изменен", + "label_user_id": "Адрес:", + "label_default_access_mode": "Доступ по умолчанию:", + "label_message_sound": "Звук нового сообщения:", + "label_push_notifications": "Уведомления:", + "label_push_notifications_disabled": "Уведомления (требуют HTTPS):", + "title_tag_manager": "Таги для поиска", + "button_logout": "Выйти", + "login_prompt": "Логин", + "password_prompt": "Пароль", + "stay_logged_in": "Запомнить", + "forgot_password_link": "Напомнить пароль", + "button_sign_in": "Войти", + "label_client": "Клиент:", + "label_server": "Сервер:", + "online_now": "онлайн", + "last_seen_timestamp": "Был активен", + "title_not_found": "Не найден", + "unnamed_topic": "Без названия", + "messages_not_readable": "нет доступа к сообщениям", + "tabtitle_find_user": "найти", + "tabtitle_new_group": "создать", + "tabtitle_group_by_id": "по id", + "label_server_to_use": "Использовать сервер:", + "label_wire_transport": "Соединение:", + "button_update": "Применить", + "sidepanel_title_login": "Авторизация", + "sidepanel_title_register": "Зарегистрироваться", + "sidepanel_title_settings": "Настройки", + "sidepanel_title_edit_account": "Редактировать счет", + "sidepanel_title_newtpk": "Новый чат", + "sidepanel_title_cred": "Подтвердить", + "sidepanel_title_reset": "Сменить пароль", + "tags_not_found": "Тагов нет. Добавьте", + "tags_editor_no_tags": "Добавьте таги", + "title_manage_tags": "Редактор тагов", + "message_sending": "в пути...", + "message_sending_failed": "ошибка", + "search_placeholder": "Список, напр. email:alice@example.com, tel:17025550003...", + "messaging_disabled_prompt": "Отправка недоступна", + "new_message_prompt": "Новое сообщение", + "file_attachment_too_large": "Размер файла {size} превышает {limit} лимит.", + "cannot_initiate_file_upload": "Ошибка загрузки файла.", + "search_for_contacts": "Поиск контактов", + "enter_confirmation_code_prompt": "Код подтверждения, полученный по {method}:", + "numeric_confirmation_code_prompt": "Только цифры", + "button_confirm": "Подтвердить", + "button_ok": "OK", + "button_cancel": "Отменить", + "invalid_content": "сообщение не читается", + "label_file_name": "Имя файла:", + "label_content_type": "Тип:", + "label_size": "Размер:", + "phone_dative": "телефону", + "email_dative": "емейлу", + "title_group_members": "Участники", + "download_action": "скачать", + "permission_join": "Подписываться ({val})", + "permission_read": "Читать ({val})", + "permission_write": "Писать ({val})", + "permission_pres": "Уведомлять ({val})", + "permission_admin": "Подтверждать ({val})", + "permission_share": "Приглашать ({val})", + "permission_delete": "Удалять ({val})", + "permission_owner": "Владелец ({val})", + "title_permissions": "Права доступа", + "requested_permissions": "Требуются", + "granted_permissions": "Получены", + "menu_item_edit_permissions": "Права доступа", + "label_other_user": "Второй", + "label_topic_name": "Название", + "label_private": "Комментарий", + "private_editing_placeholder": "Виден только вам", + "label_muting_topic": "Без уведомлений", + "action_more": "Ещё", + "label_your_permissions": "Ваши права доступа:", + "label_permissions": "Права доступа:", + "label_you": "Вы:", + "label_default_access": "Права по умолчанию:", + "label_group_members": "Участники чата:", + "button_add_members": "Добавить", + "button_leave": "Отписаться", + "group_has_no_members": "Нет участников", + "action_leave_chat": "Уйти из чата", + "menu_item_info": "Информация", + "menu_item_clear_messages": "Удалить сообщения", + "menu_item_clear_messages_for_all": "Удалить для всех", + "menu_item_delete": "Удалить", + "menu_item_delete_for_all": "Удалить для всех", + "menu_item_mute": "Не уведомлять", + "menu_item_unmute": "Уведомлять", + "menu_item_delete_topic": "Удалить чат", + "menu_item_unblock": "Разблокировать", + "menu_item_block": "Заблокировать", + "menu_item_member_delete": "Отписать", + "title_info": "Подробности", + "new_password_placeholder": "Введите новый пароль", + "label_reset_password": "Отправить емейл для смены пароля:", + "credential_email_prompt": "Регистрационный емейл", + "button_reset": "Изменить", + "button_send_request": "Отправить", + "action_cancel": "отменить", + "upload_finishing": "завершение...", + "no_contacts": "Ничего нет :-(", + "contacts_not_found_short": "Нет контактов для запроса '{query}'", + "title_all_contacts": "Все контакты", + "error_invalid_id": "Неверный ID", + "group_user_id_prompt": "ID чата или пользователя", + "button_subscribe": "Подписаться", + "topic_name_editing_placeholder": "Название чата", + "button_create": "Создать", + "badge_you": "вы", + "badge_owner": "влад." +} diff --git a/src/index.js b/src/index.js index db5416dd..53e3b870 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,34 @@ // Put all packages together. // Used to generate umd/index.prod.js -var React = require('react'); -var ReactDOM = require('react-dom'); -var TinodeWeb = require('./webapp.es5.js'); +import React from 'react'; +import ReactDOM from 'react-dom'; +import { IntlProvider, addLocaleData } from 'react-intl'; +import ruData from 'react-intl/locale-data/ru'; +addLocaleData(ruData); +import allMessages from './messages.json'; + +import TinodeWeb from './views/tinode-web.jsx'; + +import HashNavigation from './lib/navigation.js'; + +const {params} = HashNavigation.parseUrlHash(window.location.hash); +const language = (params && params.hl) || + (navigator.languages && navigator.languages[0]) || + navigator.language || + navigator.userLanguage; + +const baseLanguage = language.toLowerCase().split(/[-_]+/)[0]; + +// Try full locale, try locale without region code, fallback to 'en' +const messages = + allMessages[language] || + allMessages[baseLanguage] || + allMessages.en; ReactDOM.render( - React.createElement(TinodeWeb, null), + + + , document.getElementById('mountPoint') ); diff --git a/src/lib/blob-helpers.js b/src/lib/blob-helpers.js new file mode 100644 index 00000000..545f8a6b --- /dev/null +++ b/src/lib/blob-helpers.js @@ -0,0 +1,213 @@ +// File and image helper functions. +import { MAX_INBAND_ATTACHMENT_SIZE, MAX_IMAGE_DIM } from '../config.js'; + +// Supported image MIME types and corresponding file extensions. +export const SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml']; +export const MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg']; + +// Make a data URL from public.photo +export function makeImageUrl(photo) { + return (photo && photo.type && photo.data) ? + 'data:image/' + photo.type + ';base64,' + photo.data : null; +} + +// Calculate linear dimensions for scaling image down to fit under a certain size. +// Returns an object which contains destination sizes, source sizes, and offsets +// into source (when making square images). +export function fitImageSize(width, height, maxWidth, maxHeight, forceSquare) { + if (!width || !height || !maxWidth || !maxHeight) { + return null; + } + + if (forceSquare) { + maxWidth = maxHeight = Math.min(maxWidth, maxHeight); + } + + let scale = Math.min( + Math.min(width, maxWidth) / width, + Math.min(height, maxHeight) / height + ); + + let size = { + dstWidth: (width * scale) | 0, + dstHeight: (height * scale) | 0, + }; + + if (forceSquare) { + // Also calculate parameters for making the image square. + size.dstWidth = size.dstHeight = Math.min(size.dstWidth, size.dstHeight); + size.srcWidth = size.srcHeight = Math.min(width, height); + size.xoffset = ((width - size.srcWidth) / 2) | 0; + size.yoffset = ((height - size.srcWidth) / 2) | 0; + } else { + size.xoffset = size.yoffset = 0; + size.srcWidth = width; + size.srcHeight = height; + } + return size; +} + +// Ensure file's extension matches mime content type +export function fileNameForMime(fname, mime) { + var idx = SUPPORTED_IMAGE_FORMATS.indexOf(mime); + var ext = MIME_EXTENSIONS[idx]; + + var at = fname.lastIndexOf('.'); + if (at >= 0) { + fname = fname.substring(0, at); + } + return fname + '.' + ext; +} + +// Convert uploaded image into a base64-encoded string possibly scaling +// linear dimensions or constraining to a square. +export function imageFileScaledToBase64(file, width, height, forceSquare, onSuccess, onError) { + var img = new Image(); + img.crossOrigin = 'Anonymous'; + img.onerror = function(err) { + onError("Image format unrecognized"); + } + img.onload = function() { + var dim = fitImageSize(this.width, this.height, width, height, forceSquare); + if (!dim) { + onError("Invalid image"); + return; + } + var canvas = document.createElement('canvas'); + canvas.width = dim.dstWidth; + canvas.height = dim.dstHeight; + var ctx = canvas.getContext('2d'); + ctx.imageSmoothingEnabled = true; + ctx.drawImage(this, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight, + 0, 0, dim.dstWidth, dim.dstHeight); + + var mime = (this.width != dim.dstWidth || + this.height != dim.dstHeight || + SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) ? 'image/jpeg' : file.type; + var imageBits = canvas.toDataURL(mime); + var parts = imageBits.split(','); + // Get actual image type: 'data:image/png;base64,' + mime = getMimeType(parts[0]); + if (!mime) { + onError("Unsupported image format"); + return; + } + // Ensure the image is not too large + var quality = 0.78; + if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) { + mime = 'image/jpeg'; + } + if (mime == 'image/jpeg') { + // Reduce size of the jpeg by reducing image quality + while (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE && quality > 0.45) { + imageBits = canvas.toDataURL(mime, quality); + quality *= 0.84; + } + } + if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) { + onError("The image size " + bytesToHumanSize(base64DecodedLen(imageBits.length)) + + " exceeds the " + bytesToHumanSize(MAX_INBAND_ATTACHMENT_SIZE) + " limit.", "err"); + return; + } + canvas = null; + onSuccess(imageBits.split(',')[1], mime, dim.dstWidth, dim.dstHeight, fileNameForMime(file.name, mime)); + }; + img.src = URL.createObjectURL(file); +} + +// Convert uploaded image file to base64-encoded string without scaling/converting the image +export function imageFileToBase64(file, onSuccess, onError) { + var reader = new FileReader(); + reader.addEventListener('load', function() { + var parts = reader.result.split(','); + var mime = getMimeType(parts[0]); + if (!mime) { + onError("Failed to process image file"); + return; + } + + // Get image size. + var img = new Image(); + img.crossOrigin = 'Anonymous'; + img.onload = function() { + onSuccess(parts[1], mime, this.width, this.height, fileNameForMime(file.name, mime)); + } + img.onerror = function(err) { + onError("Image format not recognized"); + } + img.src = URL.createObjectURL(file); + }, false); + reader.readAsDataURL(file); +} + +export function fileToBase64(file, onSuccess, onError) { + var reader = new FileReader(); + reader.addEventListener('load', function() { + onSuccess(file.type, reader.result.split(',')[1], file.name); + }); + reader.readAsDataURL(file); +} + +// File pasted from the clipboard. It's either an inline image or a file attachment. +// FIXME: handle large files out of band. +export function filePasted(event, onImageSuccess, onAttachmentSuccess, onError) { + var items = (event.clipboardData || event.originalEvent.clipboardData || {}).items; + for (var i in items) { + var item = items[i]; + if (item.kind === 'file') { + var file = item.getAsFile(); + if (!file) { + console.log("Failed to get file object from pasted file item", item.kind, item.type); + continue; + } + if (file.type && file.type.split('/')[0] == 'image') { + // Handle inline image + if (file.size > MAX_INBAND_ATTACHMENT_SIZE || SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) { + imageFileScaledToBase64(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, false, onImageSuccess, onError); + } else { + imageFileToBase64(file, onImageSuccess, onError); + } + } else { + // Handle file attachment + fileToBase64(file, onAttachmentSuccess, onError) + } + // Indicate that the pasted data contains a file. + return true; + } + } + // No file found. + return false; +} + +// Get mime type from data URL header. +export function getMimeType(header) { + var mime = /^data:(image\/[-+a-z0-9.]+);base64/.exec(header); + return (mime && mime.length > 1) ? mime[1] : null; +} + +// Given length of a binary object in bytes, calculate the length after +// base64 encoding. +export function base64EncodedLen(n) { + return Math.floor((n + 2) / 3) * 4; +} + +// Given length of a base64-encoded object, calculate decoded size of the +// pbject in bytes. +export function base64DecodedLen(n) { + return Math.floor(n / 4) * 3; +} + +// Re-encode string to standard base64 encoding with padding. +// The string may be base64-URL encoded without the padding. +export function base64ReEncode(str) { + if (str) { + str = str.replace('-', '+').replace('_', '/'); + try { + str = btoa(atob(str)); + } catch(err) { + console.log("Failed to base64 re-encode string"); + str = null; + } + } + return str; +} diff --git a/src/lib/host-name.js b/src/lib/host-name.js new file mode 100644 index 00000000..294887fc --- /dev/null +++ b/src/lib/host-name.js @@ -0,0 +1,29 @@ +import { KNOWN_HOSTS, DEFAULT_HOST } from '../config.js'; + +// Detect server address from the URL +export function detectServerAddress() { + let host = DEFAULT_HOST; + if (typeof window.location == 'object') { + if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') { + host = KNOWN_HOSTS.local; + } else if (window.location.hostname) { + host = window.location.hostname + (window.location.port ? ':' + window.location.port : ''); + } + } + return host; +} + +// Detect if the page is served over HTTPS. +export function isSecureConnection() { + if (typeof window.location == 'object') { + return window.location.protocol == 'https:'; + } + return false; +} + +export function isLocalHost() { + if (typeof window.location == 'object') { + return window.location.hostname == 'localhost'; + } + return false; +} diff --git a/src/lib/local-storage.js b/src/lib/local-storage.js new file mode 100644 index 00000000..3c32aa94 --- /dev/null +++ b/src/lib/local-storage.js @@ -0,0 +1,26 @@ +// Helper functions for storing values in localStorage. +// By default localStorage can store only strings, not objects or other types. + +export default class LocalStorageUtil { + // Replace old object with the new one. + static setObject(key, value) { + localStorage.setItem(key, JSON.stringify(value)); + } + + // Get stored object. + static getObject(key) { + let value = localStorage.getItem(key); + return value && JSON.parse(value); + } + + // Partially or wholly update stored object. + static updateObject(key, value) { + let oldVal = this.getObject(key); + this.setObject(key, Object.assign(oldVal || {}, value)); + } + + // Just a wrapper. + static removeItem(key) { + localStorage.removeItem(key); + } +} diff --git a/src/lib/navigation.js b/src/lib/navigation.js new file mode 100644 index 00000000..4411763a --- /dev/null +++ b/src/lib/navigation.js @@ -0,0 +1,68 @@ +// Utility class for hash navigation. + +// Parse hash as in http://www.example.com/path#hash as if it were +// path and arguments. +export default class HashNavigation { + static parseUrlHash(hash) { + // Split path from args, path -> parts[0], args->path[1] + let parts = hash.split('?', 2); + let params = {}; + let path = []; + if (parts[0]) { + path = parts[0].substr(1).split('/'); + } + if (parts[1]) { + parts[1].split('&').forEach(function(part) { + let item = part.split('='); + if (item[0]) { + params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]); + } + }); + } + return {path: path, params: params}; + } + + static navigateTo(url) { + window.location.hash = url; + } + + static composeUrlHash(path, params) { + var url = path.join('/'); + var args = []; + for (var key in params) { + if (params.hasOwnProperty(key)) { + args.push(key + '=' + params[key]); + } + } + if (args.length > 0) { + url += '?' + args.join('&'); + } + return url; + } + + static addUrlParam(hash, key, value) { + var parsed = this.parseUrlHash(hash); + parsed.params[key] = value; + return this.composeUrlHash(parsed.path, parsed.params); + } + + static removeUrlParam(hash, key) { + var parsed = this.parseUrlHash(hash); + delete parsed.params[key]; + return this.composeUrlHash(parsed.path, parsed.params); + } + + static setUrlSidePanel(hash, sidepanel) { + var parsed = this.parseUrlHash(hash); + parsed.path[0] = sidepanel; + return this.composeUrlHash(parsed.path, parsed.params); + } + + static setUrlTopic(hash, topic) { + var parsed = this.parseUrlHash(hash); + parsed.path[1] = topic; + // Close InfoView on topic change. + delete parsed.params.info; + return this.composeUrlHash(parsed.path, parsed.params); + } +} diff --git a/src/lib/strformat.js b/src/lib/strformat.js new file mode 100644 index 00000000..933ff18c --- /dev/null +++ b/src/lib/strformat.js @@ -0,0 +1,40 @@ +// Short representation of time in the past. +export function shortDateFormat(then) { + var locale = window.navigator.userLanguage || window.navigator.language; + var now = new Date(); + if (then.getFullYear() == now.getFullYear()) { + if (then.getMonth() == now.getMonth() && then.getDate() == now.getDate()) { + return then.toLocaleTimeString(locale, {hour12: false, hour: '2-digit', minute: '2-digit'}); + } else { + return then.toLocaleDateString(locale, + {hour12: false, month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'}); + } + } + return then.toLocaleDateString(locale, + {hour12: false, year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'}); +} + +// Convert a number of bytes to human-readable format. +export function bytesToHumanSize(bytes) { + if (!bytes || bytes == 0) { + return '0 Bytes'; + } + + var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + var bucket = Math.min(Math.floor(Math.log2(bytes) / 10) | 0, sizes.length-1); + var count = bytes / Math.pow(1024, bucket); + var round = bucket > 0 ? (count < 3 ? 2 : (count < 30 ? 1 : 0)) : 0; + return count.toFixed(round) + ' ' + sizes[bucket]; +} + +// Get 32 bit integer hash value for a string. Ideally it should produce the same value +// as Java's String#hash(). +export function stringHash(value) { + var hash = 0; + value = '' + value; + for (var i = 0; i < value.length; i++) { + hash = ((hash << 5) - hash) + value.charCodeAt(i); + hash = hash & hash; // Convert to 32bit integer + } + return hash; +} diff --git a/src/lib/utils.js b/src/lib/utils.js new file mode 100644 index 00000000..f513ced6 --- /dev/null +++ b/src/lib/utils.js @@ -0,0 +1,54 @@ +// Odds and ends + +// Make shortcut icon appear with a green dot + show unread count in title. +export function updateFavicon(count) { + var oldIcon = document.getElementById('shortcut-icon'); + if (oldIcon) { + var head = document.head || document.getElementsByTagName('head')[0]; + var newIcon = document.createElement('link'); + newIcon.type = 'image/png'; + newIcon.id = 'shortcut-icon'; + newIcon.rel = 'shortcut icon'; + newIcon.href = 'img/logo32x32' + (count > 0 ? 'a' : '') + '.png'; + head.removeChild(oldIcon); + head.appendChild(newIcon); + } + document.title = (count > 0 ? '('+count+') ' : '') + 'Tinode'; +} + +// Create VCard which represents topic 'public' info +export function vcard(fn, imageDataUrl) { + var card = null; + + if ((fn && fn.trim()) || imageDataUrl) { + card = {}; + if (fn) { + card.fn = fn.trim(); + } + if (imageDataUrl) { + var dataStart = imageDataUrl.indexOf(','); + card.photo = { + data: imageDataUrl.substring(dataStart+1), + type: 'jpg' + }; + } + } + return card; +} + +// Deep-shallow compare two arrays: shallow compare each element. +export function arrayEqual(a, b) { + // Compare lengths first. + if (a.length != b.length) { + return false; + } + // Order of elements is ignored. + a.sort(); + b.sort(); + for (var i = 0, l = a.length; i < l; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} diff --git a/src/messages.json b/src/messages.json new file mode 100644 index 00000000..acdb87b9 --- /dev/null +++ b/src/messages.json @@ -0,0 +1,244 @@ +{ + "en": { + "contacts_not_found": "You have no chats
¯\\\\_(ツ)_/¯", + "full_name_prompt": "Full name, e.g. John Doe", + "email_prompt": "Email, e.g. jdoe@example.com", + "button_sign_up": "Sign up", + "label_your_name": "Your name", + "label_password": "Password", + "password_unchanged_prompt": "Unchanged", + "label_user_id": "Address:", + "label_default_access_mode": "Default access mode:", + "label_message_sound": "Message sound:", + "label_push_notifications": "Notification alerts:", + "label_push_notifications_disabled": "Notification alerts (requires HTTPS):", + "title_tag_manager": "Tags (user discovery)", + "button_logout": "Logout", + "requested_permissions": "Requested", + "granted_permissions": "Granted", + "menu_item_edit_permissions": "Edit permissions", + "label_other_user": "Other", + "title_info": "Info", + "label_topic_name": "Name", + "label_private": "Private comment", + "private_editing_placeholder": "Visible to you only", + "label_muting_topic": "Muted:", + "action_more": "More", + "label_your_permissions": "Your permissions:", + "label_permissions": "Permissions:", + "label_you": "You:", + "label_default_access": "Default access mode:", + "label_group_members": "Group members:", + "button_add_members": "Add members", + "button_leave": "Leave", + "group_has_no_members": "No members", + "action_leave_chat": "Leave", + "login_prompt": "Login", + "password_prompt": "Password", + "stay_logged_in": "Stay logged in", + "forgot_password_link": "Forgot password?", + "button_sign_in": "Sign in", + "label_client": "Client:", + "label_server": "Server:", + "online_now": "online now", + "last_seen_timestamp": "Last seen", + "title_not_found": "Not found", + "unnamed_topic": "Unnamed", + "messages_not_readable": "no access to messages", + "tabtitle_find_user": "find", + "tabtitle_new_group": "new group", + "tabtitle_group_by_id": "by id", + "search_for_contacts": "Use search to find contacts", + "new_password_placeholder": "Enter new password", + "label_reset_password": "Send a password reset email:", + "credential_email_prompt": "Your registration email", + "button_reset": "Reset", + "button_send_request": "Send request", + "label_server_to_use": "Server to use:", + "label_wire_transport": "Wire transport:", + "button_update": "Update", + "sidepanel_title_login": "Sign In", + "sidepanel_title_register": "Create Account", + "sidepanel_title_settings": "Settings", + "sidepanel_title_edit_account": "Edit Account", + "sidepanel_title_newtpk": "Start New Chat", + "sidepanel_title_cred": "Confirm Credentials", + "sidepanel_title_reset": "Reset Password", + "phone_dative": "phone", + "email_dative": "email", + "enter_confirmation_code_prompt": "Enter confirmation code sent to you by {method}:", + "numeric_confirmation_code_prompt": "Numbers only", + "button_confirm": "Confirm", + "invalid_content": "invalid content", + "badge_you": "you", + "badge_owner": "owner", + "menu_item_info": "Info", + "menu_item_clear_messages": "Clear messages", + "menu_item_clear_messages_for_all": "Clear for All", + "menu_item_delete": "Delete", + "menu_item_delete_for_all": "Delete for All", + "menu_item_mute": "Mute", + "menu_item_unmute": "Unmute", + "menu_item_delete_topic": "Delete", + "menu_item_unblock": "Unblock", + "menu_item_block": "Block", + "menu_item_member_delete": "Remove", + "action_cancel": "cancel", + "upload_finishing": "finishing...", + "no_contacts": "You have no contacts :-(", + "contacts_not_found_short": "No contacts match '{query}'", + "title_group_members": "Group Members", + "title_all_contacts": "All Contacts", + "button_ok": "OK", + "button_cancel": "Cancel", + "download_action": "download", + "label_file_name": "File name:", + "label_content_type": "Content type:", + "label_size": "Size:", + "error_invalid_id": "Invalid ID", + "group_user_id_prompt": "Group or User ID", + "button_subscribe": "Subscribe", + "topic_name_editing_placeholder": "Freeform name of the group", + "button_create": "Create", + "permission_join": "Join ({val})", + "permission_read": "Read ({val})", + "permission_write": "Write ({val})", + "permission_pres": "Get notified ({val})", + "permission_admin": "Approve ({val})", + "permission_share": "Share ({val})", + "permission_delete": "Delete ({val})", + "permission_owner": "Owner ({val})", + "title_permissions": "Permissions", + "message_sending": "sending...", + "message_sending_failed": "failed", + "search_placeholder": "List like email:alice@example.com, tel:17025550003...", + "messaging_disabled_prompt": "Messaging disabled", + "new_message_prompt": "New message", + "file_attachment_too_large": "The file size {size} exceeds the {limit} limit.", + "cannot_initiate_file_upload": "Cannot initiate file upload.", + "tags_not_found": "No tags defined. Add some.", + "tags_editor_no_tags": "Add some tags", + "title_manage_tags": "Manage tags" + }, + "ru": { + "contacts_not_found": "Чатов нет
¯\\\\_(ツ)_/¯", + "full_name_prompt": "Полное имя, напр. Иван Петров", + "email_prompt": "Email, напр. ivan@example.com", + "button_sign_up": "Создать счет", + "label_your_name": "Ваше имя", + "label_password": "Пароль", + "password_unchanged_prompt": "Не изменен", + "label_user_id": "Адрес:", + "label_default_access_mode": "Доступ по умолчанию:", + "label_message_sound": "Звук нового сообщения:", + "label_push_notifications": "Уведомления:", + "label_push_notifications_disabled": "Уведомления (требуют HTTPS):", + "title_tag_manager": "Таги для поиска", + "button_logout": "Выйти", + "login_prompt": "Логин", + "password_prompt": "Пароль", + "stay_logged_in": "Запомнить", + "forgot_password_link": "Напомнить пароль", + "button_sign_in": "Войти", + "label_client": "Клиент:", + "label_server": "Сервер:", + "online_now": "онлайн", + "last_seen_timestamp": "Был активен", + "title_not_found": "Не найден", + "unnamed_topic": "Без названия", + "messages_not_readable": "нет доступа к сообщениям", + "tabtitle_find_user": "найти", + "tabtitle_new_group": "создать", + "tabtitle_group_by_id": "по id", + "label_server_to_use": "Использовать сервер:", + "label_wire_transport": "Соединение:", + "button_update": "Применить", + "sidepanel_title_login": "Авторизация", + "sidepanel_title_register": "Зарегистрироваться", + "sidepanel_title_settings": "Настройки", + "sidepanel_title_edit_account": "Редактировать счет", + "sidepanel_title_newtpk": "Новый чат", + "sidepanel_title_cred": "Подтвердить", + "sidepanel_title_reset": "Сменить пароль", + "tags_not_found": "Тагов нет. Добавьте", + "tags_editor_no_tags": "Добавьте таги", + "title_manage_tags": "Редактор тагов", + "message_sending": "в пути...", + "message_sending_failed": "ошибка", + "search_placeholder": "Список, напр. email:alice@example.com, tel:17025550003...", + "messaging_disabled_prompt": "Отправка недоступна", + "new_message_prompt": "Новое сообщение", + "file_attachment_too_large": "Размер файла {size} превышает {limit} лимит.", + "cannot_initiate_file_upload": "Ошибка загрузки файла.", + "search_for_contacts": "Поиск контактов", + "enter_confirmation_code_prompt": "Код подтверждения, полученный по {method}:", + "numeric_confirmation_code_prompt": "Только цифры", + "button_confirm": "Подтвердить", + "button_ok": "OK", + "button_cancel": "Отменить", + "invalid_content": "сообщение не читается", + "label_file_name": "Имя файла:", + "label_content_type": "Тип:", + "label_size": "Размер:", + "phone_dative": "телефону", + "email_dative": "емейлу", + "title_group_members": "Участники", + "download_action": "скачать", + "permission_join": "Подписываться ({val})", + "permission_read": "Читать ({val})", + "permission_write": "Писать ({val})", + "permission_pres": "Уведомлять ({val})", + "permission_admin": "Подтверждать ({val})", + "permission_share": "Приглашать ({val})", + "permission_delete": "Удалять ({val})", + "permission_owner": "Владелец ({val})", + "title_permissions": "Права доступа", + "requested_permissions": "Требуются", + "granted_permissions": "Получены", + "menu_item_edit_permissions": "Права доступа", + "label_other_user": "Второй", + "label_topic_name": "Название", + "label_private": "Комментарий", + "private_editing_placeholder": "Виден только вам", + "label_muting_topic": "Без уведомлений", + "action_more": "Ещё", + "label_your_permissions": "Ваши права доступа:", + "label_permissions": "Права доступа:", + "label_you": "Вы:", + "label_default_access": "Права по умолчанию:", + "label_group_members": "Участники чата:", + "button_add_members": "Добавить", + "button_leave": "Отписаться", + "group_has_no_members": "Нет участников", + "action_leave_chat": "Уйти из чата", + "menu_item_info": "Информация", + "menu_item_clear_messages": "Удалить сообщения", + "menu_item_clear_messages_for_all": "Удалить для всех", + "menu_item_delete": "Удалить", + "menu_item_delete_for_all": "Удалить для всех", + "menu_item_mute": "Не уведомлять", + "menu_item_unmute": "Уведомлять", + "menu_item_delete_topic": "Удалить чат", + "menu_item_unblock": "Разблокировать", + "menu_item_block": "Заблокировать", + "menu_item_member_delete": "Отписать", + "title_info": "Подробности", + "new_password_placeholder": "Введите новый пароль", + "label_reset_password": "Отправить емейл для смены пароля:", + "credential_email_prompt": "Регистрационный емейл", + "button_reset": "Изменить", + "button_send_request": "Отправить", + "action_cancel": "отменить", + "upload_finishing": "завершение...", + "no_contacts": "Ничего нет :-(", + "contacts_not_found_short": "Нет контактов для запроса '{query}'", + "title_all_contacts": "Все контакты", + "error_invalid_id": "Неверный ID", + "group_user_id_prompt": "ID чата или пользователя", + "button_subscribe": "Подписаться", + "topic_name_editing_placeholder": "Название чата", + "button_create": "Создать", + "badge_you": "вы", + "badge_owner": "влад." + } +} diff --git a/src/version.js b/src/version.js new file mode 100644 index 00000000..4d97f3da --- /dev/null +++ b/src/version.js @@ -0,0 +1,3 @@ +// This is a generated file. Don't edit. + +export const PACKAGE_VERSION = "0.15.9-rc1"; diff --git a/src/views/contacts-view.jsx b/src/views/contacts-view.jsx new file mode 100644 index 00000000..92e92191 --- /dev/null +++ b/src/views/contacts-view.jsx @@ -0,0 +1,58 @@ +/* ContactsView holds all contacts-related stuff */ +import React from 'react'; +import { defineMessages, injectIntl } from 'react-intl'; + +import ContactList from '../widgets/contact-list.jsx'; + +import { updateFavicon } from '../lib/utils.js'; + +// Panel titles for translation. +const messages = defineMessages({ + 'contacts_not_found': { + id: 'contacts_not_found', + description: 'HTML message shown in ContactList when no contacts are found', + defaultMessage: 'You have no chats
¯\\\\_(ツ)_/¯' + } +}); + +class ContactsView extends React.Component { + constructor(props) { + super(props); + + this.state = ContactsView.getDerivedStateFromProps(props, {}); + } + + static getDerivedStateFromProps(nextProps, prevState) { + let contacts = []; + let unreadThreads = 0; + nextProps.chatList.map((c) => { + unreadThreads += c.unread > 0 ? 1 : 0; + contacts.push(c); + }); + contacts.sort(function(a, b){ + return (b.touched || 0) - (a.touched || 0); + }); + + updateFavicon(unreadThreads); + + return {contactList: contacts}; + } + + render() { + const {formatHTMLMessage} = this.props.intl; + return ( + {formatHTMLMessage(messages.contacts_not_found)}} + topicSelected={this.props.topicSelected} + myUserId={this.props.myUserId} + showOnline={true} + showUnread={true} + onTopicSelected={this.props.onTopicSelected} + showContextMenu={this.props.showContextMenu} /> + ); + } +}; + +export default injectIntl(ContactsView); diff --git a/src/views/create-account-view.jsx b/src/views/create-account-view.jsx new file mode 100644 index 00000000..c226db4b --- /dev/null +++ b/src/views/create-account-view.jsx @@ -0,0 +1,125 @@ +// Account registration. +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +import AvatarUpload from '../widgets/avatar-upload.jsx'; +import CheckBox from '../widgets/checkbox.jsx'; +import VisiblePassword from '../widgets/visible-password.jsx'; + +import LocalStorageUtil from '../lib/local-storage.js'; +import { vcard } from '../lib/utils.js'; + +export default class CreateAccountView extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + login: '', + password: '', + email: '', + fn: '', // full/formatted name + imageDataUrl: null, + errorCleared: false, + saveToken: LocalStorageUtil.getObject('keep-logged-in') + }; + + this.handleLoginChange = this.handleLoginChange.bind(this); + this.handlePasswordChange = this.handlePasswordChange.bind(this); + this.handleEmailChange = this.handleEmailChange.bind(this); + this.handleFnChange = this.handleFnChange.bind(this); + this.handleImageChanged = this.handleImageChanged.bind(this); + this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleLoginChange(e) { + this.setState({login: e.target.value}); + } + + handlePasswordChange(password) { + this.setState({password: password}); + } + + handleEmailChange(e) { + this.setState({email: e.target.value}) + } + + handleFnChange(e) { + this.setState({fn: e.target.value}); + } + + handleImageChanged(img) { + this.setState({imageDataUrl: img}); + } + + handleToggleSaveToken() { + LocalStorageUtil.setObject('keep-logged-in', !this.state.saveToken); + this.setState({saveToken: !this.state.saveToken}); + } + + handleSubmit(e) { + e.preventDefault(); + // TODO: check email for validity + this.setState({errorCleared: false}); + this.props.onCreateAccount( + this.state.login.trim(), + this.state.password.trim(), + vcard(this.state.fn, this.state.imageDataUrl), + {'meth': 'email', 'val': this.state.email}); + } + + render() { + let submitClasses = 'blue'; + if (this.props.disabled) { + submitClasses += ' disabled'; + } + + return ( +
+
+
+ { + (login_prompt) => + } + { + (password_prompt) => + } +
+ +
+
+ { + (full_name_prompt) => + } +
+
+ { + (email_prompt) => + } +
+
+ + { + (stay_logged_in) => + } +
+
+ +
+
+ ); + } +}; diff --git a/src/views/edit-account-view.jsx b/src/views/edit-account-view.jsx new file mode 100644 index 00000000..94156e2d --- /dev/null +++ b/src/views/edit-account-view.jsx @@ -0,0 +1,227 @@ +// Edit account parameters. +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +import AvatarUpload from '../widgets/avatar-upload.jsx'; +import CheckBox from '../widgets/checkbox.jsx'; +import InPlaceEdit from '../widgets/in-place-edit.jsx'; +import PermissionsEditor from '../widgets/permissions-editor.jsx'; +import TagManager from '../widgets/tag-manager.jsx'; + +import { makeImageUrl } from '../lib/blob-helpers.js'; + +export default class EditAccountView extends React.Component { + constructor(props) { + super(props); + + let me = this.props.tinode.getMeTopic(); + let defacs = me.getDefaultAccess(); + let fnd = this.props.tinode.getFndTopic(); + this.state = { + fullName: me.public ? me.public.fn : undefined, + avatar: makeImageUrl(me.public ? me.public.photo : null), + auth: defacs ? defacs.auth : null, + anon: defacs ? defacs.anon : null, + tags: fnd.tags(), + showPermissionEditorFor: undefined, + previousOnTags: fnd.onTagsUpdated + }; + + this.tnNewTags = this.tnNewTags.bind(this); + this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this); + this.handlePasswordUpdate = this.handlePasswordUpdate.bind(this); + this.handleImageChanged = this.handleImageChanged.bind(this); + this.handleCheckboxClick = this.handleCheckboxClick.bind(this); + this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this); + this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this); + this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this); + this.handleTagsUpdated = this.handleTagsUpdated.bind(this); + } + + componentDidMount() { + let fnd = this.props.tinode.getFndTopic(); + fnd.onTagsUpdated = this.tnNewTags; + if (!fnd.isSubscribed()) { + fnd.subscribe(fnd.startMetaQuery().withLaterDesc().withTags().build()).catch((err) => { + this.props.onError(err.message, 'err'); + }); + } + } + + componentWillUnmount() { + var fnd = this.props.tinode.getFndTopic(); + fnd.onTagsUpdated = this.state.previousOnTags; + } + + tnNewTags(tags) { + this.setState({tags: tags}); + } + + handleFullNameUpdate(fn) { + this.setState({fullName: fn}); + this.props.onUpdateAccount(undefined, vcard(fn, this.state.avatar)); + } + + handlePasswordUpdate(pwd) { + this.setState({password: pwd}); + this.props.onUpdateAccount(pwd); + } + + handleImageChanged(img) { + this.setState({avatar: img}); + this.props.onUpdateAccount(undefined, vcard(this.state.fullName, img)); + } + + handleCheckboxClick(what, checked) { + if (what == 'sound') { + this.props.onToggleMessageSounds(checked); + } else if (what == 'alert') { + this.props.onTogglePushNotifications(checked); + } + } + + handleLaunchPermissionsEditor(which) { + this.setState({ + showPermissionEditorFor: which, + editedPermissions: this.state[which] + }); + } + + handleHidePermissionsEditor() { + this.setState({showPermissionEditorFor: undefined}); + } + + handlePermissionsChanged(perm) { + let defacs = {}; + defacs[this.state.showPermissionEditorFor] = perm; + this.props.onUpdateAccount(undefined, undefined, defacs); + + let newState = {showPermissionEditorFor: undefined}; + newState[this.state.showPermissionEditorFor] = perm; + this.setState(newState); + } + + handleTagsUpdated(tags) { + // Check if tags have actually changed. + if (arrayEqual(this.state.tags.slice(0), tags.slice(0))) { + return; + } + this.props.onUpdateTags(tags); + } + + render() { + var tags = []; + this.state.tags.map(function(tag) { + tags.push({tag}); + }); + if (tags.length == 0) { + tags = ; + } + return ( + {this.state.showPermissionEditorFor ? + + : +
+
+
+
+
{ + (full_name_placeholder) => + }
+
+
+ { + (password_unchanged) => + } +
+
+ +
+
+
+
+ + {this.props.myUserId} +
+
+ +
+
+
Auth: {this.state.auth}
+
Anon: {this.state.anon}
+
+
+
+
+ + +
+
+ + +
+
+ { + (title_tag_manager) => + } + + } + ); + } +}; diff --git a/src/views/info-view.jsx b/src/views/info-view.jsx new file mode 100644 index 00000000..74854d03 --- /dev/null +++ b/src/views/info-view.jsx @@ -0,0 +1,636 @@ +// InfoView: panel with topic/user info. +import React from 'react'; +import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; + +import Tinode from 'tinode-sdk'; + +import AvatarUpload from '../widgets/avatar-upload.jsx'; +import CheckBox from '../widgets/checkbox.jsx'; +import ContactList from '../widgets/contact-list.jsx'; +import GroupManager from '../widgets/group-manager.jsx'; +import InPlaceEdit from '../widgets/in-place-edit.jsx'; +import MenuCancel from '../widgets/menu-cancel.jsx'; +import MoreButton from '../widgets/more-button.jsx'; +import PermissionsEditor from '../widgets/permissions-editor.jsx'; +import TagManager from '../widgets/tag-manager.jsx'; + +import { makeImageUrl } from '../lib/blob-helpers.js'; + +const messages = defineMessages({ + requested: { + id: 'requested_permissions', + defaultMessage: 'Requested', + description: 'Title for permissions' + }, + granted: { + id: 'granted_permissions', + defaultMessage: 'Granted', + description: 'Title for permissions' + }, + edit_permissions: { + id: 'menu_item_edit_permissions', + defaultMessage: 'Edit permissions', + description: 'Menu item [Edit permissions]' + }, + other_user: { + id: 'label_other_user', + defaultMessage: 'Other', + description: 'Label for the other user when the user is unnamed' + } +}); + +class InfoView extends React.Component { + constructor(props) { + super(props); + + this.state = { + topic: null, + owner: false, + admin: false, + sharer: false, + muted: false, + address: null, + groupTopic: undefined, + fullName: undefined, + avatar: null, + private: null, + selectedContact: null, + access: null, + modeGiven: null, + modeWant: null, + modeGiven2: null, // P2P topic, the other user mode given + modeWant2: null, // P2P topic, the other user mode want + auth: null, + anon: null, + contactList: [], + tags: [], + showMemberPanel: false, + showPermissionEditorFor: undefined, + moreInfoExpanded: false, + previousMetaDesc: undefined, + previousSubsUpdated: undefined, + previousTagsUpdated: undefined + }; + + this.resetSubs = this.resetSubs.bind(this); + this.resetDesc = this.resetDesc.bind(this); + this.onMetaDesc = this.onMetaDesc.bind(this); + this.onSubsUpdated = this.onSubsUpdated.bind(this); + this.onTagsUpdated = this.onTagsUpdated.bind(this); + this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this); + this.handlePrivateUpdate = this.handlePrivateUpdate.bind(this); + this.handleImageChanged = this.handleImageChanged.bind(this); + this.handleMuted = this.handleMuted.bind(this); + this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this); + this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this); + this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this); + this.handleShowAddMembers = this.handleShowAddMembers.bind(this); + this.handleHideAddMembers = this.handleHideAddMembers.bind(this); + this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this); + this.handleLeave = this.handleLeave.bind(this); + this.handleMemberSelected = this.handleMemberSelected.bind(this); + this.handleMoreInfo = this.handleMoreInfo.bind(this); + this.handleTagsUpdated = this.handleTagsUpdated.bind(this); + this.handleContextMenu = this.handleContextMenu.bind(this); + } + + // No need to separately handle component mount. + UNSAFE_componentWillReceiveProps(props) { + var topic = this.props.tinode.getTopic(props.topic); + if (!topic) { + return; + } + + if (this.onMetaDesc != topic.onMetaDesc) { + this.previousMetaDesc = topic.onMetaDesc; + topic.onMetaDesc = this.onMetaDesc; + + this.previousSubsUpdated = topic.onSubsUpdated; + topic.onSubsUpdated = this.onSubsUpdated; + + if (topic.getType() == 'grp') { + this.previousTagsUpdated = topic.onTagsUpdated; + topic.onTagsUpdated = this.onTagsUpdated; + } else { + this.previousTagsUpdated = undefined; + } + } + + if (this.state.topic != props.topic) { + this.setState({topic: props.topic}); + this.resetDesc(topic, props); + this.resetSubs(topic, props); + } + } + + componentWillUnmount() { + var topic = this.props.tinode.getTopic(this.props.topic); + if (!topic) { + return; + } + this.setState({topic: null}); + topic.onMetaDesc = this.previousMetaDesc; + topic.onSubsUpdated = this.previousSubsUpdated; + topic.onTagsUpdated = this.previousTagsUpdated; + } + + resetSubs(topic, props) { + var newState = {contactList: []}; + if (topic.getType() == 'p2p') { + // Fetch the other party in the p2p conversation. + // Topic may not be ready yet, so check if user is found. + var user2 = topic.subscriber(props.topic); + if (user2) { + newState.modeGiven2 = user2.acs.getGiven(); + newState.modeWant2 = user2.acs.getWant(); + } + } else { + topic.subscribers(function(sub) { + newState.contactList.push(sub); + }, this); + } + + this.setState(newState); + } + + resetDesc(topic, props) { + var defacs = topic.getDefaultAccess() || {}; + var acs = topic.getAccessMode(); + this.setState({ + owner: acs && acs.isOwner(), + admin: acs && acs.isAdmin(), + sharer: acs && (acs.isAdmin() || acs.isSharer()), + muted: acs && acs.isMuted(), + + fullName: topic.public ? topic.public.fn : undefined, + avatar: makeImageUrl(topic.public ? topic.public.photo : null), + private: topic.private ? topic.private.comment : null, + address: topic.name, + groupTopic: (topic.getType() == 'grp'), + showMemberPanel: false, + access: acs ? acs.getMode() : undefined, + modeGiven: acs ? acs.getGiven() : undefined, + modeWant: acs ? acs.getWant() : undefined, + auth: defacs.auth, + anon: defacs.anon + }); + + if (topic.getType() == 'grp' && acs && acs.isOwner()) { + // Requesting tags: owner is editing the topic. + topic.getMeta(topic.startMetaQuery().withTags().build()); + } + } + + onMetaDesc(desc) { + var topic = this.props.tinode.getTopic(this.props.topic); + if (!topic) { + return; + } + this.resetDesc(topic, this.props); + + if (this.previousMetaDesc && this.previousMetaDesc != this.onMetaDesc) { + this.previousMetaDesc(desc); + } + } + + onSubsUpdated() { + var topic = this.props.tinode.getTopic(this.props.topic); + if (!topic) { + return; + } + this.resetSubs(topic, this.props); + + if (this.previousSubsUpdated && this.previousSubsUpdated != this.onSubsUpdated) { + this.previousSubsUpdated(); + } + } + + onTagsUpdated(tags) { + this.setState({tags: tags}); + + if (this.previousTagsUpdated && this.previousTagsUpdated != this.onTagsUpdated) { + this.previousTagsUpdated(); + } + } + + handleFullNameUpdate(fn) { + if (this.state.fullName !== fn) { + this.setState({fullName: fn}); + this.props.onTopicDescUpdate(this.props.topic, + vcard(fn, this.state.avatar), + null); + } + } + + handlePrivateUpdate(priv) { + if (this.state.priv !== priv) { + this.setState({private: priv}); + this.props.onTopicDescUpdate(this.props.topic, + null, + priv || Tinode.DEL_CHAR); + } + } + + handleImageChanged(img) { + this.setState({avatar: img}); + this.props.onTopicDescUpdate(this.props.topic, + vcard(this.state.fullName, img), + null); + } + + handleMuted(ignored, checked) { + this.setState({muted: checked}); + this.props.onChangePermissions(this.props.topic, checked ? '-P' : '+P'); + } + + handlePermissionsChanged(perm) { + switch (this.state.showPermissionEditorFor) { + case 'auth': + this.props.onTopicDescUpdate(this.props.topic, null, null, {auth: perm}); + break; + case 'anon': + this.props.onTopicDescUpdate(this.props.topic, null, null, {anon: perm}); + break; + case 'mode': + case 'want': + this.props.onChangePermissions(this.props.topic, perm); + break; + case 'given': + this.props.onChangePermissions(this.props.topic, perm, this.props.topic); + break; + case 'user': + this.props.onChangePermissions(this.props.topic, perm, this.state.userPermissionsEdited); + break; + } + + this.setState({showPermissionEditorFor: undefined}); + } + + handleLaunchPermissionsEditor(which, uid) { + const {formatMessage} = this.props.intl; + let toEdit, toCompare, toSkip, titleEdit, titleCompare, userTitle, userAvatar; + switch (which) { + case 'mode': + toEdit = this.state.access; + break; + case 'want': + toEdit = this.state.modeWant; + toCompare = this.state.modeGiven; + toSkip = this.state.groupTopic ? 'O' : 'ASDO'; + titleEdit = formatMessage(messages.requested); + titleCompare = formatMessage(messages.granted); + break; + case 'given': + toEdit = this.state.modeGiven2; + toCompare = this.state.modeWant2; + toSkip = this.state.groupTopic ? (this.state.owner ? '' : 'O') : 'ASDO'; + titleEdit = formatMessage(messages.granted); + titleCompare = formatMessage(messages.requested); + break; + case 'auth': + toEdit = this.state.auth; + toSkip = 'O'; + break; + case 'anon': + toEdit = this.state.anon; + toSkip = 'O'; + break; + case 'user': { + let topic = this.props.tinode.getTopic(this.props.topic); + if (!topic) { + return; + } + var user = topic.subscriber(uid); + if (!user || !user.acs) { + return; + } + toEdit = user.acs.getGiven(); + toCompare = user.acs.getWant(); + toSkip = this.state.owner ? '' : 'O'; + titleEdit = formatMessage(messages.granted); + titleCompare = formatMessage(messages.requested); + if (user.public) { + userTitle = user.public.fn; + userAvatar = user.public.photo; + } + break; + } + default: + console.log("Unknown permission editing mode '" + which + "'"); + break; + } + this.setState({ + showPermissionEditorFor: which, + userPermissionsEdited: uid, + userPermissionsTitle: userTitle, + userPermissionsAvatar: userAvatar, + editedPermissions: toEdit, + immutablePermissions: toCompare, + editedPermissionsTitle: titleEdit, + immutablePermissionsTitle: titleCompare, + editedPermissionsSkipped: toSkip, + }); + } + + handleHidePermissionsEditor() { + this.setState({showPermissionEditorFor: undefined}); + } + + handleShowAddMembers() { + this.props.onInitFind(); + this.setState({showMemberPanel: true}); + } + + handleHideAddMembers() { + this.setState({showMemberPanel: false}); + } + + handleMemberUpdateRequest(members, added, removed) { + this.props.onMemberUpdateRequest(this.props.topic, added, removed); + this.setState({showMemberPanel: false}); + } + + handleLeave() { + this.props.onLeaveTopic(this.props.topic); + } + + handleMemberSelected(uid) { + this.setState({selectedContact: uid}); + } + + handleMoreInfo(open) { + this.setState({moreInfoExpanded: open}); + } + + handleTagsUpdated(tags) { + if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) { + this.props.onTopicTagsUpdate(this.props.topic, tags); + } + } + + handleContextMenu(params) { + const {formatMessage} = this.props.intl; + const instance = this; + const topic = this.props.tinode.getTopic(this.props.topic); + if (!topic) { + return; + } + const user = topic.subscriber(params.topicName); + if (!user || !user.acs) { + return; + } + + const menuItems = [ + {title: formatMessage(messages.edit_permissions), handler: function() { + instance.handleLaunchPermissionsEditor('user', params.topicName); + }}, + 'member_delete', + user.acs.isMuted() ? 'member_unmute' : 'member_mute', + user.acs.isJoiner() ? 'member_block' : 'member_unblock' + ]; + this.props.showContextMenu({ + topicName: this.props.topic, + x: params.x, + y: params.y, + user: params.topicName}, menuItems); + } + + render() { + const {formatMessage} = this.props.intl; + + return ( +
+
+
+ +
+
+ +
+
+ {this.props.displayMobile ? + : null} + {this.state.showMemberPanel ? + + : + this.state.showPermissionEditorFor ? + + : +
+
+
+
+
Unknown} + readOnly={!this.state.owner} + value={this.state.fullName} + onFinished={this.handleFullNameUpdate} />
+
+ +
+
+ { + (private_placeholder) => + } +
+
+ +
+
+
+
+ + +
+ { + (more) => + } + {this.state.moreInfoExpanded ? +
+
+ + {this.state.address} +
+ {this.state.groupTopic ? +
+ + + {this.state.access} + +
+ : +
+
+ +
+
+
+   + {this.state.access} +
+
{this.state.fullName ? this.state.fullName : formatMessage(messages.other_user)}: +   + {this.state.modeGiven2} + +
+
+
+ } + {this.state.sharer && (this.state.auth || this.state.anon) ? +
+
+ +
+
+
Auth: {this.state.admin ? + + {this.state.auth} + + : + {this.state.auth} + } +
+
Anon: {this.state.admin ? + + {this.state.anon} + + : + {this.state.anon} + } +
+
+
+ : + null + } +
+ : + null + } +
+
+ {this.state.owner ? + { + (tags) => + } + : + null + } + {this.state.owner ?
: null } + {this.state.groupTopic ? +
+
+ +
+
+ {this.state.sharer ? + + person_add + + : null} + {!this.state.owner ? + + exit_to_app + + : null} +
+ { + (no_members) => + } +
+ : + + } +
+ } +
+ ); + } +}; + +export default injectIntl(InfoView); diff --git a/src/views/login-view.jsx b/src/views/login-view.jsx new file mode 100644 index 00000000..32fb39f3 --- /dev/null +++ b/src/views/login-view.jsx @@ -0,0 +1,96 @@ +// Login form. +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +import CheckBox from '../widgets/checkbox.jsx'; +import VisiblePassword from '../widgets/visible-password.jsx'; + +import LocalStorageUtil from '../lib/local-storage.js'; + +export default class LoginView extends React.Component { + constructor(props) { + super(props); + + this.state = { + login: props.login, + password: '', + hostName: props.serverAddress, + saveToken: LocalStorageUtil.getObject('keep-logged-in') + }; + this.handleLoginChange = this.handleLoginChange.bind(this); + this.handlePasswordChange = this.handlePasswordChange.bind(this); + this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleLoginChange(e) { + this.setState({login: e.target.value}); + } + + handlePasswordChange(e) { + this.setState({password: e.target.value}); + } + + handleToggleSaveToken() { + LocalStorageUtil.setObject('keep-logged-in', !this.state.saveToken); + this.setState({saveToken: !this.state.saveToken}); + } + + handleSubmit(e) { + e.preventDefault(); + this.props.onLogin(this.state.login.trim(), this.state.password.trim()); + } + + render() { + var submitClasses = 'blue'; + if (this.props.disabled) { + submitClasses += ' disabled'; + } + + return ( +
+ + { + (login_prompt) => + } + + + { + (password_prompt) => + } + +
+ + + + + +
+
+ +
+
+ ); + } +}; +/* END Login */ diff --git a/src/views/logo-view.jsx b/src/views/logo-view.jsx new file mode 100644 index 00000000..ca8b4904 --- /dev/null +++ b/src/views/logo-view.jsx @@ -0,0 +1,26 @@ +/* This is just a static page to display when no conversation is selected. */ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +import Tinode from 'tinode-sdk'; + +import { APP_NAME } from '../config.js'; + +export default class LogoView extends React.PureComponent { + render() { + var version = APP_NAME + ' (' + Tinode.getLibrary() + ')'; + return ( +
+
+ + +

Tinode Web

+
+

{version}

+

{this.props.serverVersion} + ({this.props.serverAddress})

+
+
+ ); + } +}; diff --git a/src/views/messages-view.jsx b/src/views/messages-view.jsx new file mode 100644 index 00000000..f7260b85 --- /dev/null +++ b/src/views/messages-view.jsx @@ -0,0 +1,532 @@ +import React from 'react'; +import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; + +import Tinode from 'tinode-sdk'; + +import ChatMessage from '../widgets/chat-message.jsx'; +import ErrorPanel from '../widgets/error-panel.jsx'; +import GroupSubs from '../widgets/group-subs.jsx'; +import ImagePreview from '../widgets/image-preview.jsx'; +import LetterTile from '../widgets/letter-tile.jsx'; +import LoadSpinner from '../widgets/load-spinner.jsx'; +import LogoView from './logo-view.jsx'; +import SendMessage from '../widgets/send-message.jsx'; + +import { KEYPRESS_DELAY, MESSAGES_PAGE } from '../config.js'; +import { makeImageUrl } from '../lib/blob-helpers.js'; +import { shortDateFormat } from '../lib/strformat.js'; + +const messages = defineMessages({ + online_now: { + id: 'online_now', + defaultMessage: 'online now', + description: 'Indicator that the user or topic is currently online', + }, + last_seen: { + id: 'last_seen_timestamp', + defaultMessage: 'Last seen', + description: 'Label for the timestamp of when the user or topic was last online' + }, + 'not_found': { + id: 'title_not_found', + defaultMessage: 'Not found', + description: 'Title shown when topic is not found' + } +}); + +class MessagesView extends React.Component { + constructor(props) { + super(props); + + this.state = { + messages: [], + onlineSubs: [], + topic: '', + title: '', + avatar: null, + scrollPosition: 0, + readOnly: props.acs ? !props.acs.isWriter() : true, + writeOnly: props.acs ? !props.acs.isReader() : true, + typingIndicator: false, + imagePreview: null + }; + + this.propsChange = this.propsChange.bind(this); + this.leave = this.leave.bind(this); + this.handleScrollReference = this.handleScrollReference.bind(this); + this.fetchMoreMessages = this.fetchMoreMessages.bind(this); + this.handleDescChange = this.handleDescChange.bind(this); + this.handleSubsUpdated = this.handleSubsUpdated.bind(this); + this.handleNewMessage = this.handleNewMessage.bind(this); + this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this); + this.handleInfoReceipt = this.handleInfoReceipt.bind(this); + this.handleImagePreview = this.handleImagePreview.bind(this); + this.handleCloseImagePreview = this.handleCloseImagePreview.bind(this); + this.handleContextClick = this.handleContextClick.bind(this); + this.handleShowContextMenuMessage = this.handleShowContextMenuMessage.bind(this); + this.handleBackNavigation = this.handleBackNavigation.bind(this); + } + + // Scroll last message into view on component update e.g. on message received. + componentDidUpdate(prevProps, prevState) { + if (this.messagesScroller && + (prevState.title != this.state.title || + prevState.messages.length != this.state.messages.length)) { + this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition; + } + } + + componentDidMount() { + this.propsChange(this.props); + if (this.messagesScroller) { + this.messagesScroller.addEventListener('scroll', this.fetchMoreMessages); + } + } + + componentWillUnmount() { + this.leave(this.state.topic); + if (this.messagesScroller) { + this.messagesScroller.removeEventListener('scroll', this.fetchMoreMessages); + } + } + + UNSAFE_componentWillReceiveProps(nextProps) { + this.propsChange(nextProps); + } + + propsChange(props) { + if (!props || !props.topic) { + let oldTopic = this.state.topic; + this.setState({messages: [], onlineSubs: [], topic: null}, this.leave(oldTopic)); + return; + } + + if (!props.connected) { + // connection lost, clear online subs + this.setState({onlineSubs: []}); + return; + } + + let topic = this.props.tinode.getTopic(props.topic); + if (!topic) { + return; + } + + let newGroupTopic = Tinode.isNewGroupTopicName(props.topic); + let tryToResubscribe = !this.props.connected && props.connected; + + if (props.topic != this.state.topic) { + let msgs = []; + let subs = []; + + // Bind the new topic to component. + topic.onData = this.handleNewMessage; + topic.onAllMessagesReceived = this.handleAllMessagesReceived; + topic.onInfo = this.handleInfoReceipt; + topic.onMetaDesc = this.handleDescChange; + topic.onSubsUpdated = this.handleSubsUpdated; + topic.onPres = this.handleSubsUpdated; + // Unbind the previous topic from this component. + this.leave(this.state.topic); + + this.handleDescChange(topic); + topic.subscribers((sub) => { + if (sub.online && sub.user != this.props.myUserId) { + subs.push(sub); + } + }); + + topic.messages(function(msg) { + if (!msg.deleted) { + msgs = msgs.concat(msg); + } + }); + + this.setState({ + messages: msgs, + onlineSubs: subs, + topic: props.topic, + imagePreview: null, + scrollPosition: 0 + }); + tryToResubscribe = true; + + // The user switched to the new topic before the timer for + // the previous topic has triggered, kill it. + this.props.readTimerHandler(null); + } + + this.setState({ + readOnly: props.acs ? !props.acs.isWriter() : true, + writeOnly: props.acs ? !props.acs.isReader() : true + }); + + if (!topic.isSubscribed() && tryToResubscribe) { + // Don't request the tags. They are useless unless the user + // is the owner and is editing the topic. + let getQuery = topic.startMetaQuery() + .withLaterDesc() + .withLaterSub() + .withLaterData(MESSAGES_PAGE) + .withLaterDel(); + let setQuery = newGroupTopic ? props.newGroupTopicParams : undefined; + // Show "loading" spinner. + this.setState({ fetchingMessages: true }); + topic.subscribe(getQuery.build(), setQuery) + .then((ctrl) => { + this.setState({topic: ctrl.topic}); + this.props.onNewTopicCreated(props.topic, ctrl.topic); + // If there are unsent messages, try sending them now. + topic.queuedMessages((pub) => { + if (!pub._sending && topic.isSubscribed()) { + topic.publishMessage(pub); + } + }); + }) + .catch((err) => { + this.props.onError(err.message, 'err'); + const {formatMessage} = this.props.intl; + this.setState({ + title: formatMessage(messages.not_found), + avatar: null, + readOnly: true, + writeOnly: true, + fetchingMessages: false, + messages: [], + onlineSubs: [], + typingIndicator: false, + imagePreview: null + }); + }); + } + } + + leave(oldTopicName) { + if (!oldTopicName) { + return; + } + let oldTopic = this.props.tinode.getTopic(oldTopicName); + if (oldTopic && oldTopic.isSubscribed()) { + oldTopic.leave(false) + .catch(() => { /* do nothing here */ }) + .finally(() => { + // We don't care if the request succeeded or failed. + // The topic is dead regardless. + this.setState({fetchingMessages: false}); + oldTopic.onData = undefined; + oldTopic.onAllMessagesReceived = undefined; + oldTopic.onInfo = undefined; + oldTopic.onMetaDesc = undefined; + oldTopic.onSubsUpdated = undefined; + oldTopic.onPres = undefined; + }); + } + } + + handleScrollReference(node) { + if (node) { + node.addEventListener('scroll', this.fetchMoreMessages); + this.messagesScroller = node; + } + } + + // Get older messages + fetchMoreMessages(event) { + if (event.target.scrollTop <= 0) { + let newState = {scrollPosition: event.target.scrollHeight - event.target.scrollTop}; + this.setState((prevState, props) => { + if (!prevState.fetchingMessages) { + var topic = this.props.tinode.getTopic(this.state.topic); + if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) { + newState.fetchingMessages = true; + topic.getMessagesPage(MESSAGES_PAGE).catch((err) => { + this.setState({fetchingMessages: false}); + this.props.onError(err.message, 'err'); + }); + } + } + return newState; + }); + } + } + + handleDescChange(desc) { + if (desc.public) { + this.setState({ + title: desc.public.fn, + avatar: makeImageUrl(desc.public.photo) + }); + } else { + this.setState({ + title: '', + avatar: null + }); + } + if (desc.acs) { + this.setState({ + readOnly: !desc.acs.isWriter(), + writeOnly: !desc.acs.isReader() + }); + } + } + + handleSubsUpdated() { + if (this.state.topic) { + var subs = []; + var topic = this.props.tinode.getTopic(this.state.topic); + topic.subscribers((sub) => { + if (sub.online && sub.user != this.props.myUserId) { + subs.push(sub); + } + }); + this.setState({onlineSubs: subs}); + } + } + + handleNewMessage(msg) { + // Regenerate messages list + var topic = this.props.tinode.getTopic(this.state.topic); + var newState = {messages: []}; + topic.messages(function(m) { + if (!m.deleted) { + newState.messages = newState.messages.concat(m); + } + }); + + // msg could be null if one or more messages were deleted. + if (msg && !msg.deleted) { + // If the message is added to the end of the message list, + // scroll to the bottom. + if (topic.isNewMessage(msg.seq)) { + newState.scrollPosition = 0; + } + + // Aknowledge all messages, including own messages. + let status = topic.msgStatus(msg); + if (status >= Tinode.MESSAGE_STATUS_SENT) { + this.props.readTimerHandler(() => { + topic.noteRead(msg.seq); + }); + } + this.props.onData(msg); + } + + this.setState(newState); + } + + handleAllMessagesReceived(count) { + this.setState({fetchingMessages: false}); + } + + handleInfoReceipt(info) { + switch (info.what) { + case 'kp': { + clearTimeout(this.keyPressTimer); + var instance = this; + this.keyPressTimer = setTimeout(function() { + instance.setState({typingIndicator: false}); + }, KEYPRESS_DELAY + 1000); + if (!this.state.typingIndicator) { + this.setState({typingIndicator: true}); + } + break; + } + case 'read': + case 'recv': + // Redraw due to changed recv/read status. + this.forceUpdate(); + break; + default: + console.log("Other change in topic: ", info.what); + } + } + + handleImagePreview(content) { + this.setState({ imagePreview: content }); + } + + handleCloseImagePreview() { + this.setState({ imagePreview: null }); + } + + handleContextClick(e) { + e.preventDefault(); + e.stopPropagation(); + this.props.showContextMenu({ topicName: this.state.topic, y: e.pageY, x: e.pageX }); + } + + handleShowContextMenuMessage(params) { + params.topicName = this.state.topic; + var menuItems = ['message_delete']; + var topic = this.props.tinode.getTopic(params.topicName); + if (topic) { + var acs = topic.getAccessMode(); + if (acs && acs.isDeleter()) { + menuItems.push('message_delete_hard'); + } + } + this.props.showContextMenu(params, menuItems); + } + + handleBackNavigation() { + this.props.onHideMessagesView(); + } + + render() { + const {formatMessage} = this.props.intl; + + let component; + + if (this.state.topic) { + let messageNodes = []; + let topic = this.props.tinode.getTopic(this.state.topic); + let groupTopic = topic.getType() == 'grp'; + let previousFrom = null; + let chatBoxClass = null; + for (let i=0; i + ); + } + + let lastSeen = null; + let cont = this.props.tinode.getMeTopic().getContact(this.state.topic); + if (cont && Tinode.topicType(cont.topic) == 'p2p') { + if (cont.online) { + lastSeen = formatMessage(messages.online_now); + } else if (cont.seen) { + lastSeen = formatMessage(messages.last_seen) + ": " + shortDateFormat(cont.seen.when); + // TODO: also handle user agent in c.seen.ua + } + } + let avatar = this.state.avatar || true; + let online = this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline'; + + component = ( +
+
+ {this.props.displayMobile ? + + arrow_back + + : + null} +
+ + +
+
+
{ + this.state.title || + + }
+
{lastSeen}
+
+ {groupTopic ? + : +
+ } + +
+ {this.props.displayMobile ? + + : null} + +
+
+
    + {messageNodes} +
+
+ {this.state.writeOnly ? +
+
+ +
+
+ : + null + } +
+ + {this.state.imagePreview ? + : null} +
+ ); + } else { + component = ( + + ); + } + return component; + } +}; + +export default injectIntl(MessagesView); diff --git a/src/views/new-topic-view.jsx b/src/views/new-topic-view.jsx new file mode 100644 index 00000000..47e521d9 --- /dev/null +++ b/src/views/new-topic-view.jsx @@ -0,0 +1,108 @@ +// Create new topic and invite users or send an invite. +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +import ContactList from '../widgets/contact-list.jsx'; +import NewTopicById from '../widgets/new-topic-by-id.jsx'; +import NewTopicGroup from '../widgets/new-topic-group.jsx'; +import SearchContacts from '../widgets/search-contacts.jsx'; + +import HashNavigation from '../lib/navigation.js'; + +export default class NewTopicView extends React.Component { + constructor(props) { + super(props); + + this.state = { + tabSelected: 'p2p', + contactList: props.searchResults + }; + + this.handleTabClick = this.handleTabClick.bind(this); + this.handleContactSelected = this.handleContactSelected.bind(this); + this.handleNewGroupSubmit = this.handleNewGroupSubmit.bind(this); + this.handleGroupByID = this.handleGroupByID.bind(this); + } + + componentDidMount() { + this.props.onInitFind(); + } + + static getDerivedStateFromProps(nextProps, prevState) { + return { + contactList: nextProps.searchResults + }; + } + + handleTabClick(e) { + e.preventDefault(); + HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id)); + this.setState({tabSelected: e.currentTarget.dataset.id}); + } + + handleContactSelected(sel) { + if (this.state.tabSelected === 'p2p') { + HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab')); + this.props.onCreateTopic(sel, undefined); + } + } + + handleNewGroupSubmit(name, dataUrl, priv, tags) { + HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab')); + this.props.onCreateTopic(undefined, vcard(name, dataUrl), priv, tags); + } + + handleGroupByID(topicName) { + HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab')); + this.props.onCreateTopic(topicName); + } + + render() { + return ( +
+ + {this.state.tabSelected === 'grp' ? + : + this.state.tabSelected === 'byid' ? + : +
+ + { + (search_for_contacts) => + } +
} +
+ ); + } +}; diff --git a/src/views/password-reset-view.jsx b/src/views/password-reset-view.jsx new file mode 100644 index 00000000..ddbaefab --- /dev/null +++ b/src/views/password-reset-view.jsx @@ -0,0 +1,87 @@ +// A password reset form. +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +import VisiblePassword from '../widgets/visible-password.jsx'; + +import HashNavigation from '../lib/navigation.js'; + +export default class PasswordResetView extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + email: '', + password: '' + }; + + this.handleSubmit = this.handleSubmit.bind(this); + this.handleEmailChange = this.handleEmailChange.bind(this); + this.handlePasswordChange = this.handlePasswordChange.bind(this); + } + + componentDidMount() { + let parsed = HashNavigation.parseUrlHash(window.location.hash); + this.setState({token: parsed.params.token, scheme: parsed.params.scheme}); + } + + handleSubmit(e) { + e.preventDefault(); + if (this.state.token) { + this.props.onReset(this.state.scheme, this.state.password.trim(), this.state.token); + } else { + this.props.onRequest('email', this.state.email.trim()); + } + } + + handleEmailChange(e) { + this.setState({email: e.target.value}); + } + + handlePasswordChange(e) { + this.setState({password: e.target.value}); + } + + render() { + let reset = (this.state.token && this.state.scheme); + return ( +
+ {reset ? + { + (placeholder) => + } + : + + + { + (placeholder) => + } + + } +
+ +
+
+ ); + } +} diff --git a/src/views/settings-view.jsx b/src/views/settings-view.jsx new file mode 100644 index 00000000..8d1802ed --- /dev/null +++ b/src/views/settings-view.jsx @@ -0,0 +1,85 @@ +// Tinode config panel. +import React from 'react'; +import { FormattedMessage } from 'react-intl'; + +import HostSelector from '../widgets/host-selector.jsx'; + +export default class SettingsView extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + transport: props.transport || 'def', + serverAddress: props.serverAddress, + }; + + this.handleSubmit = this.handleSubmit.bind(this); + this.handleTransportSelected = this.handleTransportSelected.bind(this); + this.handleServerAddressChange = this.handleServerAddressChange.bind(this); + } + + handleSubmit(e) { + e.preventDefault(); + this.props.onUpdate({ + transport: this.state.transport, + serverAddress: this.state.serverAddress + }); + } + + handleTransportSelected(e) { + this.setState({transport: e.currentTarget.value}); + } + + handleServerAddressChange(name) { + this.setState({serverAddress: name}); + this.props.onServerAddressChange(name); + } + + render() { + const names = {def: "default", ws: "websocket", lp: "long polling"}; + var transportOptions = []; + var instance = this; + ['def', 'ws', 'lp'].map(function(item) { + var id = 'transport-' + item; + var name = names[item]; + transportOptions.push( +
  • + + +
  • + ); + }); + + return ( +
    +
    + +
    + +
    + +
    +
    +
      + {transportOptions} +
    +
    +
    + +
    + + ); + } +}; diff --git a/src/views/sidepanel-view.jsx b/src/views/sidepanel-view.jsx new file mode 100644 index 00000000..c213cde0 --- /dev/null +++ b/src/views/sidepanel-view.jsx @@ -0,0 +1,172 @@ +import React from 'react'; +import { defineMessages, injectIntl } from 'react-intl'; + +import SideNavbar from '../widgets/side-navbar.jsx'; +import ErrorPanel from '../widgets/error-panel.jsx'; + +import ContactsView from './contacts-view.jsx'; +import CreateAccountView from './create-account-view.jsx'; +import EditAccountView from './edit-account-view.jsx'; +import LoginView from './login-view.jsx'; +import NewTopicView from './new-topic-view.jsx'; +import PasswordResetView from './password-reset-view.jsx'; +import SettingsView from './settings-view.jsx'; +import ValidationView from './validation-view.jsx'; + +// Panel titles for translation. +const messages = defineMessages({ + 'login': { + id: 'sidepanel_title_login', + description: 'Sidepanel title for LoginView.', + defaultMessage: 'Sign In' + }, + 'register': { + id: 'sidepanel_title_register', + description: 'Sidepanel title for CreateAccountView.', + defaultMessage: 'Create Account' + }, + 'settings': { + id: 'sidepanel_title_settings', + description: 'Sidepanel title for SettingsView.', + defaultMessage: 'Settings' + }, + 'edit': { + id: 'sidepanel_title_edit_account', + description: 'Sidepanel title for EditAccountView.', + defaultMessage: 'Edit Account' + }, + 'newtpk': { + id: 'sidepanel_title_newtpk', + description: 'Sidepanel title for NewTopicView.', + defaultMessage: 'Start New Chat' + }, + 'cred': { + id: 'sidepanel_title_cred', + description: 'Sidepanel title for ValidationView.', + defaultMessage: 'Confirm Credentials' + }, + 'reset': { + id: 'sidepanel_title_reset', + description: 'Sidepanel title for PasswordResetView.', + defaultMessage: 'Reset Password' + } +}); + +class SidepanelView extends React.Component { + constructor(props) { + super(props); + + this.handleLoginRequested = this.handleLoginRequested.bind(this); + } + + handleLoginRequested(login, password) { + this.props.onLoginRequest(login, password); + } + + render() { + const {formatMessage} = this.props.intl; + const view = this.props.state || (this.props.myUserId ? 'contacts' : 'login'); + + let title, avatar; + if (view == 'contacts') { + title = this.props.title; + avatar = this.props.avatar ? this.props.avatar : true; + } else { + title = formatMessage(messages[view]); + avatar = false; + } + + let onCancel; + if (view != 'login') { + onCancel = this.props.onCancel; + } + + return ( +
    + + + + + {view === 'login' ? + : + + view === 'register' ? + : + + view === 'settings' ? + : + + view === 'edit' ? + : + + view === 'contacts' ? + : + + view === 'newtpk' ? + : + + view === 'cred' ? + : + + view === 'reset' ? + : + null} +
    + ); + } +}; + +export default injectIntl(SidepanelView); diff --git a/src/views/tinode-web.jsx b/src/views/tinode-web.jsx new file mode 100644 index 00000000..ae3edf90 --- /dev/null +++ b/src/views/tinode-web.jsx @@ -0,0 +1,1137 @@ +// The top-level class to hold all functionality together. +import React from 'react'; +import ReactDOM from 'react-dom'; +import { injectIntl } from 'react-intl'; + +import * as firebase from 'firebase/app'; +import 'firebase/messaging'; + +import Tinode from 'tinode-sdk'; + +import ContextMenu from '../widgets/context-menu.jsx'; + +import InfoView from './info-view.jsx'; +import MessagesView from './messages-view.jsx'; +import SidepanelView from './sidepanel-view.jsx'; + +import { API_KEY, APP_NAME, MEDIA_BREAKPOINT, READ_DELAY, RECEIVED_DELAY } from '../config.js'; +import { makeImageUrl } from '../lib/blob-helpers.js'; +import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js'; +import LocalStorageUtil from '../lib/local-storage.js'; +import HashNavigation from '../lib/navigation.js'; +import { updateFavicon } from '../lib/utils.js'; + +// Sound to play on message received. +const POP_SOUND = new Audio('audio/msg.mp3'); + +class TinodeWeb extends React.Component { + constructor(props) { + super(props); + + this.state = this.getBlankState(); + + this.handleResize = this.handleResize.bind(this); + this.handleHashRoute = this.handleHashRoute.bind(this); + this.handleOnline = this.handleOnline.bind(this); + this.handleAppVisibility = this.handleAppVisibility.bind(this); + this.handleReadTimer = this.handleReadTimer.bind(this); + this.handleVisibilityEvent = this.handleVisibilityEvent.bind(this); + this.handleError = this.handleError.bind(this); + this.handleLoginRequest = this.handleLoginRequest.bind(this); + this.handleConnected = this.handleConnected.bind(this); + this.doLogin = this.doLogin.bind(this); + this.handleCredentialsRequest = this.handleCredentialsRequest.bind(this); + this.handleLoginSuccessful = this.handleLoginSuccessful.bind(this); + this.handleDisconnect = this.handleDisconnect.bind(this); + this.tnMeMetaDesc = this.tnMeMetaDesc.bind(this); + this.tnMeContactUpdate = this.tnMeContactUpdate.bind(this); + this.tnMeSubsUpdated = this.tnMeSubsUpdated.bind(this); + this.resetContactList = this.resetContactList.bind(this); + this.tnData = this.tnData.bind(this); + this.tnInitFind = this.tnInitFind.bind(this); + this.tnFndSubsUpdated = this.tnFndSubsUpdated.bind(this); + this.handleSearchContacts = this.handleSearchContacts.bind(this); + this.handleTopicSelected = this.handleTopicSelected.bind(this); + this.handleHideMessagesView = this.handleHideMessagesView.bind(this); + this.handleSendMessage = this.handleSendMessage.bind(this); + this.handleNewAccount = this.handleNewAccount.bind(this); + this.handleNewAccountRequest = this.handleNewAccountRequest.bind(this); + this.handleUpdateAccountRequest = this.handleUpdateAccountRequest.bind(this); + this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this); + this.handleSettings = this.handleSettings.bind(this); + this.handleGlobalSettings = this.handleGlobalSettings.bind(this); + this.handleToggleMessageSounds = this.handleToggleMessageSounds.bind(this); + this.initDesktopAlerts = this.initDesktopAlerts.bind(this); + this.togglePushToken = this.togglePushToken.bind(this); + this.requestPushToken = this.requestPushToken.bind(this); + this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this); + this.handleNewTopic = this.handleNewTopic.bind(this); + this.handleNewTopicRequest = this.handleNewTopicRequest.bind(this); + this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this); + this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this); + this.handleChangePermissions = this.handleChangePermissions.bind(this); + this.handleTagsUpdated = this.handleTagsUpdated.bind(this); + this.handleLogout = this.handleLogout.bind(this); + this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this); + this.handleDialogCancel = this.handleDialogCancel.bind(this); + this.handleShowContextMenu = this.handleShowContextMenu.bind(this); + this.defaultTopicContextMenu = this.defaultTopicContextMenu.bind(this); + this.handleHideContextMenu = this.handleHideContextMenu.bind(this); + this.handleShowInfoView = this.handleShowInfoView.bind(this); + this.handleHideInfoView = this.handleHideInfoView.bind(this); + this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this); + this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this); + this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this); + this.handleResetPassword = this.handleResetPassword.bind(this); + } + + getBlankState() { + let settings = LocalStorageUtil.getObject("settings") || {}; + + return { + connected: false, + transport: settings.transport || null, + serverAddress: settings.serverAddress || detectServerAddress(), + serverVersion: "no connection", + // "On" is the default, so saving the "off" state. + messageSounds: !settings.messageSoundsOff, + desktopAlerts: settings.desktopAlerts, + desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) && + (typeof firebase != 'undefined') && (typeof navigator != 'undefined') && + (typeof FIREBASE_INIT != 'undefined'), + firebaseToken: LocalStorageUtil.getObject("firebase-token"), + + errorText: '', + errorLevel: null, + + sidePanelSelected: 'login', + sidePanelTitle: null, + sidePanelAvatar: null, + dialogSelected: null, + contextMenuVisible: false, + login: '', + password: '', + myUserId: null, + liveConnection: navigator.onLine, + topicSelected: '', + topicSelectedOnline: false, + topicSelectedAcs: null, + newGroupTopicParams: null, + loginDisabled: false, + displayMobile: (window.innerWidth <= MEDIA_BREAKPOINT), + showInfoPanel: false, + mobilePanel: 'sidepanel', + + contextMenuVisible: false, + contextMenuBounds: null, + contextMenuClickAt: null, + contextMenuParams: null, + contextMenuItems: [], + + // Chats as shown in the ContactsView + chatList: [], + // Contacts returned by a search query. + searchResults: [], + // Merged results of a search query and p2p chats. + searchableContacts: [], + credMethod: undefined, + credCode: undefined + }; + } + + componentDidMount() { + window.addEventListener('resize', this.handleResize); + window.addEventListener('online', (e) => { this.handleOnline(true); }); + window.addEventListener('offline', (e) => { this.handleOnline(false); }); + window.addEventListener('hashchange', this.handleHashRoute); + // Window/tab visible or invisible for pausing timers. + document.addEventListener('visibilitychange', this.handleVisibilityEvent); + + this.setState({viewportWidth: document.documentElement.clientWidth}); + + this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport); + this.tinode.onConnect = this.handleConnected; + this.tinode.onDisconnect = this.handleDisconnect; + + // Initialize desktop alerts. + if (this.state.desktopAlertsEnabled) { + try { + this.fbPush = firebase.initializeApp(FIREBASE_INIT, APP_NAME).messaging(); + this.fbPush.usePublicVapidKey(FIREBASE_INIT.messagingVapidKey); + navigator.serviceWorker.register('/service-worker.js').then((reg) => { + this.fbPush.useServiceWorker(reg); + this.initDesktopAlerts(); + if (this.state.desktopAlerts) { + if (!this.state.firebaseToken) { + this.togglePushToken(true); + } else { + this.tinode.setDeviceToken(this.state.firebaseToken); + } + } + }).catch((err) => { + // registration failed :( + console.log("Failed to register service worker:", err); + }); + } catch (err) { + this.handleError("Failed to initialize push notifications", 'err'); + console.log("Failed to initialize push notifications", err); + this.setState({desktopAlertsEnabled: false}); + } + } + + let token = LocalStorageUtil.getObject('keep-logged-in') ? + LocalStorageUtil.getObject('auth-token') : undefined; + + let parsedNav = HashNavigation.parseUrlHash(window.location.hash); + if (token) { + // When reading from storage, date is returned as string. + token.expires = new Date(token.expires); + this.tinode.setAuthToken(token); + this.tinode.connect().catch((err) => { + // Socket error + this.handleError(err.message, 'err'); + }); + delete parsedNav.params.info; + delete parsedNav.params.tab; + parsedNav.path[0] = ''; + HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsedNav.path, parsedNav.params)); + } else if (!parsedNav.params.token) { + HashNavigation.navigateTo(''); + } + + this.readTimer = null; + this.readTimerCallback = null; + + this.handleHashRoute(); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.handleResize); + window.removeEventListener('hashchange', this.handleHashRoute); + document.removeEventListener('visibilitychange', this.handleVisibilityEvent); + } + + // Setup transport (usually websocket) and server address. This will terminate connection with the server. + static tnSetup(serverAddress, transport) { + let tinode = new Tinode(APP_NAME, serverAddress, API_KEY, transport, isSecureConnection()); + tinode.enableLogging(true, true); + return tinode; + } + + handleResize() { + var mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT; + this.setState({viewportWidth: document.documentElement.clientWidth}); + if (this.state.displayMobile != mobile) { + this.setState({displayMobile: mobile}); + } + } + + // Handle for hashchange event: display appropriate panels. + handleHashRoute() { + var hash = HashNavigation.parseUrlHash(window.location.hash); + if (hash.path && hash.path.length > 0) { + // Left-side panel selector. + if (['register','settings','edit','cred','reset','newtpk','contacts',''].includes(hash.path[0])) { + this.setState({sidePanelSelected: hash.path[0]}); + } else { + console.log("Unknown sidepanel view", hash.path[0]); + } + + // Topic for MessagesView selector. + if (hash.path.length > 1 && hash.path[1] != this.state.topicSelected) { + this.setState({ + topicSelected: Tinode.topicType(hash.path[1]) ? hash.path[1] : null + }); + } + } else { + // Empty hashpath + this.setState({sidePanelSelected: ''}); + } + + // Save validation credentials, if available. + if (hash.params.method) { + this.setState({ credMethod: hash.params.method }); + } + if (hash.params.code) { + this.setState({ credCode: hash.params.code }); + } + + // Additional parameters of panels. + this.setState({ + showInfoPanel: hash.params.info, + newTopicTabSelected: hash.params.tab + }); + } + + handleOnline(online) { + var newState = {liveConnection: online}; + if (online) { + this.handleError('', null); + } else { + this.handleError("No connection", 'warn'); + } + this.setState({liveConnection: online}); + } + + // Handling read notifications here to be able to pause + // them then the window/tab is not visible. + handleAppVisibility(visible, callback) { + clearTimeout(this.readTimer); + this.readTimerCallback = callback; + if (visible && callback) { + this.readTimer = setTimeout(callback, READ_DELAY); + } else { + this.readTimer = null; + } + } + + handleReadTimer(callback) { + this.handleAppVisibility(!document.hidden, callback); + } + + handleVisibilityEvent() { + this.handleAppVisibility(!document.hidden, this.readTimerCallback); + } + + handleError(err, level) { + this.setState({errorText: err, errorLevel: level}); + } + + // User clicked Login button in the side panel. + handleLoginRequest(login, password) { + this.setState({loginDisabled: true, login: login, password: password}); + this.handleError('', null); + + if (this.tinode.isConnected()) { + this.doLogin(login, password, {meth: this.state.credMethod, resp: this.state.credCode}); + } else { + this.tinode.connect().catch((err) => { + // Socket error + this.setState({loginDisabled: false}); + this.handleError(err.message, 'err'); + }); + } + } + + // Connection succeeded. + handleConnected() { + var params = this.tinode.getServerInfo(); + this.setState({ + serverVersion: params.ver + ' ' + (params.build ? params.build : 'none') + '; ' + }); + this.doLogin(this.state.login, this.state.password, {meth: this.state.credMethod, resp: this.state.credCode}); + } + + doLogin(login, password, cred) { + if (this.tinode.isAuthenticated()) { + // Already logged in. Go to default screen. + HashNavigation.navigateTo(''); + return; + } + // Sanitize and package credentail. + cred = Tinode.credential(cred); + // Try to login with login/password. If they are not available, try token. If no token, ask for login/password. + let promise = null; + let token = this.tinode.getAuthToken(); + if (login && password) { + this.setState({password: null}); + promise = this.tinode.loginBasic(login, password, cred); + } else if (token) { + promise = this.tinode.loginToken(token.token, cred); + } + + if (promise) { + promise.then((ctrl) => { + if (ctrl.code >= 300 && ctrl.text === 'validate credentials') { + if (cred) { + this.handleError("Code does not match", 'warn'); + } + this.handleCredentialsRequest(ctrl.params); + } else { + this.handleLoginSuccessful(); + } + }).catch((err) => { + // Login failed, report error. + this.setState({loginDisabled: false, credMethod: undefined, credCode: undefined}); + this.handleError(err.message, 'err'); + localStorage.removeItem('auth-token'); + HashNavigation.navigateTo(''); + }); + } else { + // No login credentials provided. + // Make sure we are on the login page. + HashNavigation.navigateTo(''); + this.setState({loginDisabled: false}); + } + } + + handleCredentialsRequest(params) { + var parsed = HashNavigation.parseUrlHash(window.location.hash); + parsed.path[0] = 'cred'; + parsed.params['method'] = params.cred[0]; + HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params)); + } + + handleLoginSuccessful() { + this.handleError('', null); + + // Refresh authentication token. + if (LocalStorageUtil.getObject('keep-logged-in')) { + LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken()); + } + // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view. + var me = this.tinode.getMeTopic(); + me.onMetaDesc = this.tnMeMetaDesc; + me.onContactUpdate = this.tnMeContactUpdate; + me.onSubsUpdated = this.tnMeSubsUpdated; + this.setState({ + connected: true, + credMethod: undefined, + credCode: undefined, + myUserId: this.tinode.getCurrentUserID() + }); + // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched. + me.subscribe( + me.startMetaQuery(). + withLaterSub(). + withDesc(). + build() + ).catch((err) => { + localStorage.removeItem('auth-token'); + this.handleError(err.message, 'err'); + HashNavigation.navigateTo(''); + }); + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'contacts')); + } + + handleDisconnect(err) { + this.setState({ + connected: false, + topicSelectedOnline: false, + dialogSelected: null, + errorText: err && err.message ? err.message : "Disconnected", + errorLevel: err && err.message ? 'err' : 'warn', + loginDisabled: false, + contextMenuVisible: false, + serverVersion: "no connection" + }); + } + + tnMeMetaDesc(desc) { + if (desc && desc.public) { + this.setState({ + sidePanelTitle: desc.public.fn, + sidePanelAvatar: makeImageUrl(desc.public.photo) + }); + } + } + + // Reactions to updates to the contact list. + tnMeContactUpdate(what, cont) { + if (what == 'on' || what == 'off') { + this.resetContactList(); + if (this.state.topicSelected == cont.topic) { + this.setState({topicSelectedOnline: (what == 'on')}); + } + } else if (what == 'read') { + this.resetContactList(); + } else if (what == 'msg') { + // New message received + // Skip update if the topic is currently open, otherwise the badge will annoyingly flash. + if (this.state.topicSelected != cont.topic) { + if (this.state.messageSounds) { + POP_SOUND.play(); + } + this.resetContactList(); + } else if (document.hidden && this.state.messageSounds) { + POP_SOUND.play(); + } + } else if (what == 'recv') { + // Explicitly ignoring "recv" -- it causes no visible updates to contact list. + } else if (what == 'gone' || what == 'unsub') { + // Topic deleted or user unsubscribed. Remove topic from view. + // If the currently selected topic is gone, clear the selection. + if (this.state.topicSelected == cont.topic) { + this.handleTopicSelected(null); + } + // Redraw without the deleted topic. + this.resetContactList(); + } else if (what == 'acs') { + // Permissions changed. If it's for the currently selected topic, + // update the views. + if (this.state.topicSelected == cont.topic) { + this.setState({topicSelectedAcs: cont.acs}); + } + } else if (what == 'del') { + // messages deleted (hard or soft) -- update pill counter. + } else { + // TODO(gene): handle other types of notifications: + // * ua -- user agent changes (maybe display a pictogram for mobile/desktop). + // * upd -- topic 'public' updated, issue getMeta(). + console.log("Unsupported (yet) presence update:" + what + " in: " + cont.topic); + } + } + + tnMeSubsUpdated(unused) { + this.resetContactList(); + } + + // Merge search results and contact list to create a single flat + // list of kown contacts for GroupManager to use. + static prepareSearchableContacts(chatList, foundContacts) { + let merged = {}; + + // For chatList topics merge only p2p topics and convert them to the + // same format as foundContacts. + for (const c of chatList) { + if (Tinode.topicType(c.topic) == 'p2p') { + merged[c.topic] = { + user: c.topic, + updated: c.updated, + public: c.public, + private: c.private, + acs: c.acs + }; + } + } + + // Add all foundCountacts if they have not been added already. + for (const c of foundContacts) { + if (!merged[c.user]) { + merged[c.user] = c; + } + } + + return Object.values(merged); + } + + resetContactList() { + let newState = { + chatList: [] + }; + this.tinode.getMeTopic().contacts((c) => { + newState.chatList.push(c); + if (this.state.topicSelected == c.topic) { + newState.topicSelectedOnline = c.online; + newState.topicSelectedAcs = c.acs; + } + }); + // Merge search results and chat list. + newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults); + this.setState(newState); + } + + // Sending "received" notifications + tnData(data) { + let topic = this.tinode.getTopic(data.topic); + if (topic.msgStatus(data) >= Tinode.MESSAGE_STATUS_SENT) { + clearTimeout(this.receivedTimer); + this.receivedTimer = setTimeout(() => { + this.receivedTimer = undefined; + topic.noteRecv(data.seq); + }, RECEIVED_DELAY); + } + } + + /* Fnd topic: find contacts by tokens */ + tnInitFind() { + let fnd = this.tinode.getFndTopic(); + if (fnd.isSubscribed()) { + this.tnFndSubsUpdated(); + } else { + fnd.onSubsUpdated = this.tnFndSubsUpdated; + fnd.subscribe(fnd.startMetaQuery().withSub().withTags().build()).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + } + + tnFndSubsUpdated() { + let foundContacts = []; + // Don't attempt to create P2P topics which already exist. Server will reject the duplicates. + this.tinode.getFndTopic().contacts((s) => { + foundContacts.push(s); + }); + this.setState({ + searchResults: foundContacts, + searchableContacts: TinodeWeb.prepareSearchableContacts(this.state.chatList, foundContacts) + }); + } + + /** Called when the user enters a contact into the contact search field in the NewAccount panel + @param query {Array} is an array of contacts to search for + */ + handleSearchContacts(query) { + var fnd = this.tinode.getFndTopic(); + fnd.setMeta({desc: {public: query}}).then((ctrl) => { + return fnd.getMeta(fnd.startMetaQuery().withSub().build()); + }).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + + // User clicked on a contact in the side panel or deleted a contact. + handleTopicSelected(topicName, unused_index, online, acs) { + if (topicName) { + this.setState({ + errorText: '', + errorLevel: null, + mobilePanel: 'topic-view', + showInfoPanel: false + }); + // Different contact selected. + if (this.state.topicSelected != topicName) { + this.setState({ + topicSelectedOnline: online, + topicSelectedAcs: acs + }); + HashNavigation.navigateTo(HashNavigation.setUrlTopic('', topicName)); + } + } else { + // Currently selected contact deleted + this.setState({ + errorText: '', + errorLevel: null, + mobilePanel: 'sidepanel', + topicSelectedOnline: false, + topicSelectedAcs: null, + showInfoPanel: false + }); + if (this.state.topicSelected) { + HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null)); + } + } + } + + // In mobile view user requested to show sidepanel + handleHideMessagesView() { + this.setState({ + mobilePanel: 'sidepanel' + }); + HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, null)); + } + + // User is sending a message, either plain text or a drafty object, possibly + // with attachments. + handleSendMessage(msg, promise, uploader) { + let topic = this.tinode.getTopic(this.state.topicSelected); + + msg = topic.createMessage(msg, false); + // The uploader is used to show progress. + msg._uploader = uploader; + + if (!topic.isSubscribed()) { + if (!promise) { + promise = Promise.resolve(); + } + promise = promise.then(() => { return topic.subscribe(); }); + } + + if (promise) { + promise = promise.catch((err) => { + this.handleError(err.message, 'err'); + }); + } + + topic.publishDraft(msg, promise) + .catch((err) => { + this.handleError(err.message, 'err'); + }); + } + + // User chose a Sign Up menu item. + handleNewAccount() { + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'register')); + } + + // Actual registration of a new account. + handleNewAccountRequest(login_, password_, public_, cred_, tags_) { + this.tinode.connect(this.state.serverAddress) + .then(() => { + return this.tinode.createAccountBasic(login_, password_, + {public: public_, tags: tags_, cred: Tinode.credential(cred_)}); + }).then((ctrl) => { + if (ctrl.code >= 300 && ctrl.text == 'validate credentials') { + this.handleCredentialsRequest(ctrl.params); + } else { + this.handleLoginSuccessful(this); + } + }).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + + handleUpdateAccountRequest(password, pub, defacs) { + if (pub || defacs) { + this.tinode.getMeTopic().setMeta({desc: {public: pub, defacs: defacs}}).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + if (password) { + this.tinode.updateAccountBasic(null, this.tinode.getCurrentLogin(), password).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + } + + handleUpdateAccountTagsRequest(tags) { + this.tinode.getFndTopic().setMeta({tags: tags}) + .catch((err) => { + this.handleError(err.message, 'err'); + }); + } + + // User chose Settings menu item. + handleSettings() { + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, + this.state.myUserId ? 'edit' : 'settings')); + } + + // User updated global parameters. + handleGlobalSettings(settings) { + let serverAddress = settings.serverAddress || this.state.serverAddress; + let transport = settings.transport || this.state.transport; + if (this.tinode) { + this.tinode.onDisconnect = undefined; + this.tinode.disconnect(); + } + this.tinode = TinodeWeb.tnSetup(serverAddress, transport); + this.tinode.onConnect = this.handleConnected; + this.tinode.onDisconnect = this.handleDisconnect; + + this.setState({ + serverAddress: serverAddress, + transport: transport + }); + LocalStorageUtil.setObject('settings', { + serverAddress: serverAddress, + transport: transport + }); + + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, '')); + } + + // Initialize desktop alerts = push notifications. + initDesktopAlerts() { + // Google could not be bothered to mention that + // onTokenRefresh is never called. + this.fbPush.onTokenRefresh(() => { + this.requestPushToken(true); + }); + + this.fbPush.onMessage((payload) => { + // No need to do anything about it. + // All the magic happends in the service worker. + }); + } + + togglePushToken(enabled) { + if (enabled) { + if (!this.state.firebaseToken) { + this.fbPush.requestPermission().then(() => { + this.requestPushToken(true); + }).catch((err) => { + this.handleError(err.message, 'err'); + this.setState({desktopAlerts: false, firebaseToken: null}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: false}); + console.log("Failed to get permission to notify.", err); + }); + } else { + this.setState({desktopAlerts: true}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: true}); + } + } else if (this.state.firebaseToken) { + this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => { + console.log("Unable to delete token.", err); + }).finally(() => { + LocalStorageUtil.updateObject('settings', {desktopAlerts: false}); + localStorage.removeItem('firebase-token'); + this.setState({desktopAlerts: false, firebaseToken: null}); + }); + } else { + this.setState({desktopAlerts: false, firebaseToken: null}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: false}); + } + } + + requestPushToken(sendToServer) { + this.fbPush.getToken().then((refreshedToken) => { + if (refreshedToken != this.state.firebaseToken) { + this.tinode.setDeviceToken(refreshedToken, sendToServer); + LocalStorageUtil.setObject('firebase-token', refreshedToken); + } + this.setState({firebaseToken: refreshedToken, desktopAlerts: true}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: true}); + }).catch((err) => { + this.handleError(err.message, 'err'); + console.log("Failed to retrieve firebase token", err); + }); + } + + handleToggleMessageSounds(enabled) { + this.setState({messageSounds: enabled}); + LocalStorageUtil.updateObject('settings', { + messageSoundsOff: !enabled + }); + } + + // User clicked Cancel button in Setting or Sign Up panel. + handleSidepanelCancel() { + var parsed = HashNavigation.parseUrlHash(window.location.hash); + parsed.path[0] = this.state.myUserId ? 'contacts' : ''; + if (parsed.params) { + delete parsed.params.code; + delete parsed.params.method; + delete parsed.params.tab; + } + HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params)); + this.setState({errorText: '', errorLevel: null}); + } + + // User clicked a (+) menu item. + handleNewTopic() { + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'newtpk')); + } + + // Request to start a new topic. New P2P topic requires peer's name. + handleNewTopicRequest(peerName, pub, priv, tags) { + var topicName = peerName || this.tinode.newGroupTopicName(); + if (!peerName) { + this.setState( + {newGroupTopicParams: {desc: {public: pub, private: {comment: priv}}, tags: tags}}, + () => {this.handleTopicSelected(topicName)} + ); + } else { + this.handleTopicSelected(topicName); + } + } + + // New topic was creted, here is the new topic name. + handleNewTopicCreated(oldName, newName) { + if (this.state.topicSelected == oldName && oldName != newName) { + // If the current URl contains the old topic name, replace it with new. + // Update the name of the selected topic first so the navigator doen't clear + // the state. + this.setState({topicSelected: newName}, () => { + HashNavigation.navigateTo(HashNavigation.setUrlTopic('', newName)); + }); + } + } + + handleTopicUpdateRequest(topicName, pub, priv, permissions) { + var topic = this.tinode.getTopic(topicName); + if (topic) { + var params = {}; + if (pub) { + params.public = pub; + } + if (priv) { + params.private = (priv === Tinode.DEL_CHAR) ? + Tinode.DEL_CHAR : {comment: priv}; + } + if (permissions) { + params.defacs = permissions; + } + topic.setMeta({desc: params}).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + } + + handleChangePermissions(topicName, mode, uid) { + var topic = this.tinode.getTopic(topicName); + if (topic) { + var am = topic.getAccessMode(); + if (uid) { + am.updateGiven(mode); + mode = am.getGiven(); + } else { + am.updateWant(mode); + mode = am.getWant(); + } + topic.setMeta({sub: {user: uid, mode: mode}}).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + } + + handleTagsUpdated(topicName, tags) { + var topic = this.tinode.getTopic(topicName); + if (topic) { + var instance = this; + topic.setMeta({tags: tags}).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + } + + handleLogout() { + updateFavicon(0); + localStorage.removeItem('auth-token'); + if (this.tinode) { + this.tinode.onDisconnect = undefined; + this.tinode.disconnect(); + } + this.setState(this.getBlankState()); + this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport); + this.tinode.onConnect = this.handleConnected; + this.tinode.onDisconnect = this.handleDisconnect; + HashNavigation.navigateTo(''); + } + + handleLeaveUnsubRequest(topicName) { + var topic = this.tinode.getTopic(topicName); + if (!topic) { + return; + } + + topic.leave(true).then((ctrl) => { + // Hide MessagesView and InfoView panels. + HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, '')); + }).catch((err) => { + this.handleError(err.message, 'err'); + }); + } + + handleDialogCancel() { + this.setState({dialogSelected: null}); + } + + handleShowContextMenu(params, menuItems) { + this.setState({ + contextMenuVisible: true, + contextMenuClickAt: {x: params.x, y: params.y}, + contextMenuParams: params, + contextMenuItems: menuItems || this.defaultTopicContextMenu(params.topicName), + contextMenuBounds: ReactDOM.findDOMNode(this).getBoundingClientRect() + }); + } + + defaultTopicContextMenu(topicName) { + const topic = this.tinode.getTopic(topicName); + const {formatMessage} = this.props.intl; + + let muted = false, blocked = false, subscribed = false, deleter = false; + if (topic) { + if (topic.isSubscribed()) { + subscribed = true; + + let acs = topic.getAccessMode(); + muted = acs && acs.isMuted(); + blocked = acs && !acs.isJoiner(); + deleter = acs && acs.isDeleter(); + } + } + + return [ + subscribed ? {title: formatMessage({id: 'menu_item_info'}), handler: this.handleShowInfoView} : null, + subscribed ? 'messages_clear' : null, + subscribed && deleter ? 'messages_clear_hard' : null, + subscribed ? (muted ? 'topic_unmute' : 'topic_mute') : null, + subscribed ? (blocked ? 'topic_unblock' : 'topic_block') : null, + 'topic_delete' + ]; + } + + handleHideContextMenu() { + this.setState({ + contextMenuVisible: false, + contextMenuClickAt: null, + contextMenuParams: null, + contextMenuBounds: null + }); + } + + handleShowInfoView() { + HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', true)); + this.setState({showInfoPanel: true}); + } + + handleHideInfoView() { + HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'info')); + this.setState({showInfoPanel: false}); + } + + handleMemberUpdateRequest(topicName, added, removed) { + if (!topicName) { + return; + } + + var topic = this.tinode.getTopic(topicName); + if (!topic) { + return; + } + + if (added && added.length > 0) { + added.map((uid) => { + topic.invite(uid, null).catch((err) => { + this.handleError(err.message, 'err'); + }); + }); + } + + if (removed && removed.length > 0) { + removed.map((uid) => { + topic.delSubscription(uid).catch((err) => { + this.handleError(err.message, 'err'); + }); + }); + } + } + + handleValidateCredentialsRequest(cred, code) { + this.setState({credMethod: cred, credCode: code}); + this.doLogin(null, null, {meth: cred, resp: code}); + } + + handlePasswordResetRequest(method, value) { + // If already connected, connnect() will return a resolved promise. + this.tinode.connect() + .then(() => { + return this.tinode.requestResetAuthSecret('basic', method, value) + }) + .catch((err) => { + // Socket error + this.handleError(err.message, 'err'); + }); + } + + handleResetPassword(scheme, newPassword, token) { + token = base64ReEncode(token); + if (!token) { + this.handleError("Invalid security token", 'err'); + } else { + this.tinode.connect() + .then(() => { + return this.tinode.updateAccountBasic(null, null, newPassword, {token: token}); + }) + .catch((err) => { + // Socket error + this.handleError(err.message, 'err'); + }); + } + } + + render() { + return ( +
    + {this.state.contextMenuVisible ? + + : + null + } + + + + + + {this.state.showInfoPanel ? + + : + null + } +
    + ); + } +}; + +export default injectIntl(TinodeWeb); diff --git a/src/views/validation-view.jsx b/src/views/validation-view.jsx new file mode 100644 index 00000000..6e839987 --- /dev/null +++ b/src/views/validation-view.jsx @@ -0,0 +1,81 @@ +// ValidationView: panel for confirming credentials, like email or phone. +import React from 'react'; +import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; + +const messages = defineMessages({ + phone: { + id: 'phone_dative', + defaultMessage: 'phone', + description: "Dative case of 'phone', i.e. 'phone' in 'by phone'", + }, + email: { + id: 'email_dative', + defaultMessage: 'email', + description: "Dative case of 'email', i.e. 'email' in 'by email'", + } +}); + +class ValidationView extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + code: props.credCode || '' + }; + + this.handleChange = this.handleChange.bind(this); + this.handleKeyPress = this.handleKeyPress.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleChange(e) { + this.setState({code: e.target.value}); + } + + handleKeyPress(e) { + if (e.key === 'Enter') { + this.handleSubmit(e); + } + } + + handleSubmit(e) { + e.preventDefault(); + if (this.state.code && this.state.code.trim()) { + this.props.onSubmit(this.props.credMethod, this.state.code.trim()); + } + } + + render() { + const { formatMessage } = this.props.intl; + const methods = {'email': formatMessage(messages.email), 'tel': formatMessage(messages.phone)}; + let method = methods[this.props.credMethod] || this.props.credMethod; + return ( +
    +
    + +
    +
    + { + (numbers_only) => + } +
    +
    + +
    +
    + ); + } +}; + +export default injectIntl(ValidationView); diff --git a/src/webapp.jsx b/src/webapp.jsx deleted file mode 100644 index c3810ddd..00000000 --- a/src/webapp.jsx +++ /dev/null @@ -1,5631 +0,0 @@ -// Babel JSX - -'use strict'; - -if (typeof require == 'function') { - if (typeof React == 'undefined') { - var React = require('react'); - var ReactDOM = require('react-dom'); - } - if (typeof Tinode == 'undefined') { - var Tinode = require('tinode-sdk'); - var Drafty = Tinode.Drafty; - } - if (typeof firebase == 'undefined') { - var firebase = require('firebase/app'); - require('firebase/messaging'); - } - var package_version = require('../version.json').version; -} - -// Name of this application, used in the User-Agent. -const APP_NAME = 'TinodeWeb/' + (package_version || '0.15'); - -const KNOWN_HOSTS = {hosted: 'api.tinode.co', local: 'localhost:6060'}; -// Default host name and port to connect to. -const DEFAULT_HOST = KNOWN_HOSTS.hosted; -// var DEFAULT_HOST = KNOWN_HOSTS.local; - -// Sound to play on message received. -const POP_SOUND = new Audio('audio/msg.mp3'); - -// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own -const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K'; - -// Minimum time between two keypress notifications, milliseconds. -const KEYPRESS_DELAY = 3*1000; -// Delay before sending a {note} for reciving a message, milliseconds. -const RECEIVED_DELAY = 500; -// Delay before sending a read notification, milliseconds. -const READ_DELAY = 1000; - -// The shortest allowed tag length. Matches one on the server. -const MIN_TAG_LENGTH = 4; - -// Mediaquery breakpoint between desktop and mobile, in px. Should match the value -// in @meadia (max-size: 640px) in base.css -const MEDIA_BREAKPOINT = 640; -// Size of css 'rem' unit in pixels. Default 1rem = 10pt = 13px. -const REM_SIZE = 13; - -// Size of the avatar image -const AVATAR_SIZE = 128; - -// Number of chat messages to fetch in one call. -const MESSAGES_PAGE = 24; - -// Maximum in-band (included directly into the message) attachment size which fits into -// a message of 256K in size, assuming base64 encoding and 1024 bytes of overhead. -// This is size of an object *before* base64 encoding is applied. -// Increase this limit to a greater value in production, if desired. Also increase -// max_message_size in server config. -// MAX_INBAND_ATTACHMENT_SIZE = base64DecodedLen(max_message_size - overhead); -const MAX_INBAND_ATTACHMENT_SIZE = 195840; - -// Absolute maximum attachment size to be used with the server = 8MB. Increase to -// something like 100MB in production. -const MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23; - -// Maximum allowed linear dimension of an inline image in pixels. You may want -// to adjust it to 1600 or 2400 for production. -const MAX_IMAGE_DIM = 768; - -// Supported image MIME types and corresponding file extensions. -const SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml']; -const MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg']; - -// Helper functions for storing values in localStorage. -// By default localStorage can store only strings, not objects or other types. - -// Replace old object with the new one. -Storage.prototype.setObject = function(key, value) { - this.setItem(key, JSON.stringify(value)); -} -// Get stored object. -Storage.prototype.getObject = function(key) { - let value = this.getItem(key); - return value && JSON.parse(value); -} -// Partially or wholly update stored object. -Storage.prototype.updateObject = function(key, value) { - let oldVal = this.getObject(key); - this.setObject(key, Object.assign(oldVal || {}, value)); -} - -// Short representation of time in the past. -function shortDateFormat(then) { - var locale = window.navigator.userLanguage || window.navigator.language; - var now = new Date(); - if (then.getFullYear() == now.getFullYear()) { - if (then.getMonth() == now.getMonth() && then.getDate() == now.getDate()) { - return then.toLocaleTimeString(locale, {hour12: false, hour: '2-digit', minute: '2-digit'}); - } else { - return then.toLocaleDateString(locale, - {hour12: false, month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'}); - } - } - return then.toLocaleDateString(locale, - {hour12: false, year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'}); -} - -// Convert a number of bytes to human-readable format. -function bytesToHumanSize(bytes) { - if (!bytes || bytes == 0) { - return '0 Bytes'; - } - - var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; - var bucket = Math.min(Math.floor(Math.log2(bytes) / 10) | 0, sizes.length-1); - var count = bytes / Math.pow(1024, bucket); - var round = bucket > 0 ? (count < 3 ? 2 : (count < 30 ? 1 : 0)) : 0; - return count.toFixed(round) + ' ' + sizes[bucket]; -} - -// Make a data URL from public.photo -function makeImageUrl(photo) { - return (photo && photo.type && photo.data) ? - 'data:image/' + photo.type + ';base64,' + photo.data : null; -} - -// Calculate linear dimensions for scaling image down to fit under a certain size. -// Returns an object which contains destination sizes, source sizes, and offsets -// into source (when making square images). -function fitImageSize(width, height, maxWidth, maxHeight, forceSquare) { - if (!width || !height || !maxWidth || !maxHeight) { - return null; - } - - if (forceSquare) { - maxWidth = maxHeight = Math.min(maxWidth, maxHeight); - } - - let scale = Math.min( - Math.min(width, maxWidth) / width, - Math.min(height, maxHeight) / height - ); - - let size = { - dstWidth: (width * scale) | 0, - dstHeight: (height * scale) | 0, - }; - - if (forceSquare) { - // Also calculate parameters for making the image square. - size.dstWidth = size.dstHeight = Math.min(size.dstWidth, size.dstHeight); - size.srcWidth = size.srcHeight = Math.min(width, height); - size.xoffset = ((width - size.srcWidth) / 2) | 0; - size.yoffset = ((height - size.srcWidth) / 2) | 0; - } else { - size.xoffset = size.yoffset = 0; - size.srcWidth = width; - size.srcHeight = height; - } - return size; -} - -// Ensure file's extension matches mime content type -function fileNameForMime(fname, mime) { - var idx = SUPPORTED_IMAGE_FORMATS.indexOf(mime); - var ext = MIME_EXTENSIONS[idx]; - - var at = fname.lastIndexOf('.'); - if (at >= 0) { - fname = fname.substring(0, at); - } - return fname + '.' + ext; -} - -// Get mime type from data URL header. -function getMimeType(header) { - var mime = /^data:(image\/[-+a-z0-9.]+);base64/.exec(header); - return (mime && mime.length > 1) ? mime[1] : null; -} - -// Given length of a binary object in bytes, calculate the length after -// base64 encoding. -function base64EncodedLen(n) { - return Math.floor((n + 2) / 3) * 4; -} - -// Given length of a base64-encoded object, calculate decoded size of the -// pbject in bytes. -function base64DecodedLen(n) { - return Math.floor(n / 4) * 3; -} - -// Re-encode string to standard base64 encoding with padding. -// The string may be base64-URL encoded without the padding. -function base64ReEncode(str) { - if (str) { - str = str.replace('-', '+').replace('_', '/'); - try { - str = btoa(atob(str)); - } catch(err) { - console.log("Failed to base64 re-decode string"); - str = null; - } - } - return str; -} - -// Convert uploaded image into a base64-encoded string possibly scaling -// linear dimensions or constraining to a square. -function imageFileScaledToBase64(file, width, height, forceSquare, onSuccess, onError) { - var img = new Image(); - img.crossOrigin = 'Anonymous'; - img.onerror = function(err) { - onError("Image format unrecognized"); - } - img.onload = function() { - var dim = fitImageSize(this.width, this.height, width, height, forceSquare); - if (!dim) { - onError("Invalid image"); - return; - } - var canvas = document.createElement('canvas'); - canvas.width = dim.dstWidth; - canvas.height = dim.dstHeight; - var ctx = canvas.getContext('2d'); - ctx.imageSmoothingEnabled = true; - ctx.drawImage(this, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight, - 0, 0, dim.dstWidth, dim.dstHeight); - - var mime = (this.width != dim.dstWidth || - this.height != dim.dstHeight || - SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) ? 'image/jpeg' : file.type; - var imageBits = canvas.toDataURL(mime); - var parts = imageBits.split(','); - // Get actual image type: 'data:image/png;base64,' - mime = getMimeType(parts[0]); - if (!mime) { - onError("Unsupported image format"); - return; - } - // Ensure the image is not too large - var quality = 0.78; - if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) { - mime = 'image/jpeg'; - } - if (mime == 'image/jpeg') { - // Reduce size of the jpeg by reducing image quality - while (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE && quality > 0.45) { - imageBits = canvas.toDataURL(mime, quality); - quality *= 0.84; - } - } - if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) { - onError("The image size " + bytesToHumanSize(base64DecodedLen(imageBits.length)) + - " exceeds the " + bytesToHumanSize(MAX_INBAND_ATTACHMENT_SIZE) + " limit.", "err"); - return; - } - canvas = null; - onSuccess(imageBits.split(',')[1], mime, dim.dstWidth, dim.dstHeight, fileNameForMime(file.name, mime)); - }; - img.src = URL.createObjectURL(file); -} - -// Convert uploaded image file to base64-encoded string without scaling/converting the image -function imageFileToBase64(file, onSuccess, onError) { - var reader = new FileReader(); - reader.addEventListener('load', function() { - var parts = reader.result.split(','); - var mime = getMimeType(parts[0]); - if (!mime) { - onError("Failed to process image file"); - return; - } - - // Get image size. - var img = new Image(); - img.crossOrigin = 'Anonymous'; - img.onload = function() { - onSuccess(parts[1], mime, this.width, this.height, fileNameForMime(file.name, mime)); - } - img.onerror = function(err) { - onError("Image format not recognized"); - } - img.src = URL.createObjectURL(file); - }, false); - reader.readAsDataURL(file); -} - -function fileToBase64(file, onSuccess, onError) { - var reader = new FileReader(); - reader.addEventListener('load', function() { - onSuccess(file.type, reader.result.split(',')[1], file.name); - }); - reader.readAsDataURL(file); -} - -// File pasted from the clipboard. It's either an inline image or a file attachment. -// FIXME: handle large files out of band. -function filePasted(event, onImageSuccess, onAttachmentSuccess, onError) { - var items = (event.clipboardData || event.originalEvent.clipboardData || {}).items; - for (var i in items) { - var item = items[i]; - if (item.kind === 'file') { - var file = item.getAsFile(); - if (!file) { - console.log("Failed to get file object from pasted file item", item.kind, item.type); - continue; - } - if (file.type && file.type.split('/')[0] == 'image') { - // Handle inline image - if (file.size > MAX_INBAND_ATTACHMENT_SIZE || SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) { - imageFileScaledToBase64(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, false, onImageSuccess, onError); - } else { - imageFileToBase64(file, onImageSuccess, onError); - } - } else { - // Handle file attachment - fileToBase64(file, onAttachmentSuccess, onError) - } - // Indicate that the pasted data contains a file. - return true; - } - } - // No file found. - return false; -} - -// Make shortcut icon appear with a green dot + show unread count in title. -function updateFavicon(count) { - var oldIcon = document.getElementById('shortcut-icon'); - if (oldIcon) { - var head = document.head || document.getElementsByTagName('head')[0]; - var newIcon = document.createElement('link'); - newIcon.type = 'image/png'; - newIcon.id = 'shortcut-icon'; - newIcon.rel = 'shortcut icon'; - newIcon.href = 'img/logo32x32' + (count > 0 ? 'a' : '') + '.png'; - head.removeChild(oldIcon); - head.appendChild(newIcon); - } - document.title = (count > 0 ? '('+count+') ' : '') + 'Tinode'; -} - -// Get 32 bit integer hash value for a string. Ideally it should produce the same value -// as Java's String#hash(). -function stringHash(value) { - var hash = 0; - value = '' + value; - for (var i = 0; i < value.length; i++) { - hash = ((hash << 5) - hash) + value.charCodeAt(i); - hash = hash & hash; // Convert to 32bit integer - } - return hash; -} - -// Helper functions for hash navigation. - -// Parse hash as in http://www.example.com/path#hash as if it were -// path and arguments. -function parseUrlHash(hash) { - // Split path from args, path -> parts[0], args->path[1] - let parts = hash.split('?', 2); - let params = {}; - let path = []; - if (parts[0]) { - path = parts[0].substr(1).split('/'); - } - if (parts[1]) { - parts[1].split('&').forEach(function(part) { - let item = part.split('='); - if (item[0]) { - params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]); - } - }); - } - return {path: path, params: params}; -} - -function navigateTo(url) { - window.location.hash = url; -} - -function composeUrlHash(path, params) { - var url = path.join('/'); - var args = []; - for (var key in params) { - if (params.hasOwnProperty(key)) { - args.push(key + '=' + params[key]); - } - } - if (args.length > 0) { - url += '?' + args.join('&'); - } - return url; -} - -function addUrlParam(hash, key, value) { - var parsed = parseUrlHash(hash); - parsed.params[key] = value; - return composeUrlHash(parsed.path, parsed.params); -} - -function removeUrlParam(hash, key) { - var parsed = parseUrlHash(hash); - delete parsed.params[key]; - return composeUrlHash(parsed.path, parsed.params); -} -function setUrlSidePanel(hash, sidepanel) { - var parsed = parseUrlHash(hash); - parsed.path[0] = sidepanel; - return composeUrlHash(parsed.path, parsed.params); -} -function setUrlTopic(hash, topic) { - var parsed = parseUrlHash(hash); - parsed.path[1] = topic; - // Close InfoView on topic change. - delete parsed.params.info; - return composeUrlHash(parsed.path, parsed.params); -} -// END of hash navigation helper functions - - -// Detect server address from the URL -function detectServerAddress() { - let host = DEFAULT_HOST; - if (typeof window.location == 'object') { - if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') { - host = KNOWN_HOSTS.local; - } else if (window.location.hostname) { - host = window.location.hostname + (window.location.port ? ':' + window.location.port : ''); - } - } - return host; -} - -// Detect if the page is served over HTTPS. -function isSecureConnection() { - if (typeof window.location == 'object') { - return window.location.protocol == 'https:'; - } - return false; -} - -function isLocalHost() { - if (typeof window.location == 'object') { - return window.location.hostname == 'localhost'; - } - return false; -} - -// Create VCard which represents topic 'public' info -function vcard(fn, imageDataUrl) { - var card = null; - - if ((fn && fn.trim()) || imageDataUrl) { - card = {}; - if (fn) { - card.fn = fn.trim(); - } - if (imageDataUrl) { - var dataStart = imageDataUrl.indexOf(','); - card.photo = { - data: imageDataUrl.substring(dataStart+1), - type: 'jpg' - }; - } - } - return card; -} - -// Deep-shallow compare two arrays: shallow compare each element. -function arrayEqual(a, b) { - // Compare lengths first. - if (a.length != b.length) { - return false; - } - // Order of elements is ignored. - a.sort(); - b.sort(); - for (var i = 0, l = a.length; i < l; i++) { - if (a[i] !== b[i]) { - return false; - } - } - return true; -} - -/* BEGIN Context Menu: popup/dropdown menu */ - -class ContextMenu extends React.Component { - constructor(props) { - super(props); - - this.handlePageClick = this.handlePageClick.bind(this); - this.handleEscapeKey = this.handleEscapeKey.bind(this); - this.handleClick = this.handleClick.bind(this); - - // Preconfigured menu items. - this.MenuItems = { - 'topic_info': {title: "Info", handler: null}, - - 'messages_clear': {title: "Clear messages", handler: (params, errorHandler) => { - this.deleteMessages(true, false, params, errorHandler); - }}, - 'messages_clear_hard': {title: "Clear for All", handler: (params, errorHandler) => { - this.deleteMessages(true, true, params, errorHandler); - }}, - 'message_delete': {title: "Delete", handler: (params, errorHandler) => { - this.deleteMessages(false, false, params, errorHandler); - }}, - 'message_delete_hard': {title: "Delete for All", handler: (params, errorHandler) => { - this.deleteMessages(false, true, params, errorHandler); - }}, - 'topic_unmute': {title: "Unmute", handler: this.topicPermissionSetter.bind(this, '+P')}, - 'topic_mute': {title: "Mute", handler: this.topicPermissionSetter.bind(this, '-P')}, - 'topic_unblock': {title: "Unblock", handler: this.topicPermissionSetter.bind(this, '+J')}, - 'topic_block': {title: "Block", handler: this.topicPermissionSetter.bind(this, '-J')}, - 'topic_delete': {title: "Delete", handler: (params, errorHandler) => { - let topic = this.props.tinode.getTopic(params.topicName); - if (!topic) { - console.log("Topic not found: ", params.topicName); - return; - } - topic.delTopic().catch((err) => { - if (errorHandler) { - errorHandler(err.message, 'err'); - } - }); - }}, - - 'permissions': {title: "Edit permissions", handler: null}, - 'member_delete': {title: "Remove", handler: (params, errorHandler) => { - let topic = this.props.tinode.getTopic(params.topicName); - if (!topic || !params.user) { - console.log("Topic or user not found: '" + params.topicName + "', '" + params.user + "'"); - return; - } - topic.delSubscription(params.user).catch((err) => { - if (errorHandler) { - errorHandler(err.message, 'err'); - } - }); - }}, - 'member_mute': {title: "Mute", handler: this.topicPermissionSetter.bind(this, '-P')}, - 'member_unmute': {title: "Unmute", handler: this.topicPermissionSetter.bind(this, '+P')}, - 'member_block': {title: "Block", handler: this.topicPermissionSetter.bind(this, '-J')}, - 'member_unblock': {title: "Unblock", handler: this.topicPermissionSetter.bind(this, '+J')}, - }; - } - - componentDidMount() { - document.addEventListener('mousedown', this.handlePageClick, false); - document.addEventListener('keyup', this.handleEscapeKey, false); - } - - componentWillUnmount() { - document.removeEventListener('mousedown', this.handlePageClick, false); - document.removeEventListener('keyup', this.handleEscapeKey, false); - } - - handlePageClick(e) { - if (ReactDOM.findDOMNode(this).contains(e.target)) { - return; - } - e.preventDefault(); - e.stopPropagation(); - this.props.hide(); - } - - handleEscapeKey(e) { - if (e.keyCode === 27) { - this.props.hide(); - } - } - - handleClick(e) { - e.preventDefault(); - e.stopPropagation(); - this.props.hide(); - let item = this.props.items[e.currentTarget.dataset.id]; - if (typeof item == 'string') { - item = this.MenuItems[item]; - } - item.handler(this.props.params, this.props.onError); - } - - // Menu Actions - - deleteMessages(all, hard, params, errorHandler) { - var topic = this.props.tinode.getTopic(params.topicName); - if (!topic) { - console.log("Topic not found: ", params.topicName); - return; - } - // We don't know if the message is still pending (e.g. attachment is being uploaded), - // so try cancelling first. No harm if we can't cancel. - if (topic.cancelSend(params.seq)) { - return Promise.resolve(); - } - // Can't cancel. Delete instead. - var promise = all ? - topic.delMessagesAll(hard) : - topic.delMessagesList([params.seq], hard); - promise.catch((err) => { - if (errorHandler) { - errorHandler(err.message, 'err'); - } - }); - } - - // Function is used by context menu to set permissions. - topicPermissionSetter(mode, params, errorHandler) { - var topic = this.props.tinode.getTopic(params.topicName); - if (!topic) { - console.log("Topic not found", params.topicName); - return; - } - - var am, user; - if (params.user) { - user = topic.subscriber(params.user); - if (!user) { - console.log("Subscriber not found", params.topicName + "[" + params.user + "]"); - return; - } - am = user.acs.updateGiven(mode).getGiven(); - } else { - am = topic.getAccessMode().updateWant(mode).getWant(); - } - - topic.setMeta({sub: {user: params.user, mode: am}}).catch((err) => { - if (errorHandler) { - errorHandler(err.message, 'err'); - } - }); - } - - render() { - let count = 0; - let menu = []; - this.props.items.map((item) => { - if (typeof item == 'string') { - item = this.MenuItems[item]; - } - if (item && item.title) { - menu.push( - item.title == '-' ? -
  • - : -
  • {item.title}
  • - ); - } - count++; - }); - - // Ensure that menu is inside the app-container. - var hSize = 12 * REM_SIZE; - var vSize = REM_SIZE * (0.7 + menu.length * 2.5); - var left = (this.props.bounds.right - this.props.clickAt.x < hSize) ? - (this.props.clickAt.x - this.props.bounds.left - hSize) : - (this.props.clickAt.x - this.props.bounds.left); - var top = (this.props.bounds.bottom - this.props.clickAt.y < vSize) ? - (this.props.clickAt.y - this.props.bounds.top - vSize) : - (this.props.clickAt.y - this.props.bounds.top); - - var position = { - left: left + 'px', - top: top + 'px' - }; - - return ( -
      - {menu} -
    - ); - } -} -/* END Popup/dropdown menu */ - -/* The X menu to be displayed in title bars */ -class MenuCancel extends React.PureComponent { - constructor(props) { - super(props); - } - - render() { - return ( - close - ); - } -} - -class LoadSpinner extends React.PureComponent { - render() { - return (this.props.show ? -
    : null); - } -} - -// Toggle [Title text >] -> [Title text v] -class MoreButton extends React.PureComponent { - constructor(props) { - super(props); - - this.state = { - open: props.open - }; - this.handleToggle = this.handleToggle.bind(this); - } - - handleToggle() { - let open = !this.state.open; - this.setState({open: open}); - if (this.props.onToggle) { - this.props.onToggle(open); - } - } - - render() { - return (); - } -} - -/* BEGIN Lettertile: Avatar box: either a bitmap or a letter tile or a stock icon. */ -class LetterTile extends React.PureComponent { - render() { - let avatar; - if (this.props.avatar === true) { - if (this.props.topic && this.props.title && this.props.title.trim()) { - let letter = this.props.title.trim().charAt(0); - let color = "lettertile dark-color" + (Math.abs(stringHash(this.props.topic)) % 16); - avatar = (
    {letter}
    ) - } else { - avatar = (Tinode.topicType(this.props.topic) == 'grp') ? - group : - person; - } - } else if (this.props.avatar) { - avatar = avatar; - } else { - avatar = null; - } - return avatar; - } -} -/* END Lettertile */ - -/* BEGIN In-place text editor. Shows text with an icon - * which toggles it into an input field */ -class InPlaceEdit extends React.Component { - constructor(props) { - super(props); - - this.state = { - active: props.active, - initialValue: props.value || '', - value: props.value || '' - }; - - this.handeTextChange = this.handeTextChange.bind(this); - this.handleKeyDown = this.handleKeyDown.bind(this); - this.handleStartEditing = this.handleStartEditing.bind(this); - this.handleEditingFinished = this.handleEditingFinished.bind(this); - this.handlePasswordFinished = this.handlePasswordFinished.bind(this); - } - - static getDerivedStateFromProps(nextProps, prevState) { - // If text has changed while in read mode, update text and discard changes. - // Ignore update if in edit mode. - if (prevState.initialValue != nextProps.value && !prevState.active) { - return { - initialValue: nextProps.value || '', - value: nextProps.value || '' - }; - } - return null; - } - - handeTextChange(e) { - this.setState({value: e.target.value}); - } - - handleKeyDown(e) { - if (e.keyCode === 27) { - // Escape pressed - this.setState({value: this.props.value, active: false}); - } else if (e.keyCode === 13) { - // Enter pressed - this.handleEditingFinished(); - } - } - - handleStartEditing() { - if (!this.props.readOnly) { - ReactDOM.findDOMNode(this).focus(); - this.setState({active: true}); - } - } - - handleEditingFinished() { - this.setState({active: false}); - let value = this.state.value.trim(); - if ((value || this.props.value) && (value !== this.props.value)) { - this.props.onFinished(value); - } - } - - handlePasswordFinished(value) { - this.setState({active: false}); - if (value && (value !== this.props.value)) { - this.props.onFinished(value); - } - } - - render() { - if (this.state.active) { - var fieldType = this.props.type || 'text'; - } else { - var spanText = this.props.type == 'password' ? '••••••••' : this.state.value; - var spanClass = 'in-place-edit' + - (this.props.readOnly ? ' disabled' : ''); - if (!spanText) { - spanText = this.props.placeholder; - spanClass += ' placeholder'; - } - if (spanText.length > 20) { - // FIXME: this is wrong for RTL languages. - spanText = spanText.substring(0, 19) + '...'; - } - } - return ( - this.state.active ? - (fieldType == 'password' ? - - : - - ) - : - - {spanText} - - ); - } -}; - -class VisiblePassword extends React.PureComponent { - constructor(props) { - super(props); - - this.state = { - value: this.props.value, - visible: false - }; - - this.handleVisibility = this.handleVisibility.bind(this); - this.handeTextChange = this.handeTextChange.bind(this); - this.handleKeyDown = this.handleKeyDown.bind(this); - this.handleEditingFinished = this.handleEditingFinished.bind(this); - } - - handeTextChange(e) { - this.setState({value: e.target.value}); - if (this.props.onChange) { - this.props.onChange(e); - } - } - - handleVisibility(e) { - e.preventDefault(); - this.setState({visible: !this.state.visible}); - } - - handleKeyDown(e) { - if (e.keyCode == 27) { - // Escape pressed - this.setState({value: this.props.value, visible: false}); - if (this.props.onFinished) { - this.props.onFinished(); - } - } else if (e.keyCode == 13) { - // Enter pressed - this.handleEditingFinished(); - } - } - - handleEditingFinished(e) { - if (e) { - let currentTarget = e.currentTarget; - setTimeout(() => { - if (!currentTarget.contains(document.activeElement)) { - if (this.props.onFinished) { - this.props.onFinished(this.state.value); - } - } - }, 0); - } else if (this.props.onFinished) { - this.props.onFinished(this.state.value.trim()); - } - } - - render() { - return ( -
    - - - - {this.state.visible ? 'visibility' : 'visibility_off'} - - -
    - ); - } -} - -/* END InPlaceEdit */ - -/* BEGIN Combobox for selecting host name */ -class HostSelector extends React.PureComponent { - constructor(props) { - super(props); - - this.state = { - hostName: props.serverAddress, - changed: false - }; - - this.handleHostNameChange = this.handleHostNameChange.bind(this); - this.handleEditingFinished = this.handleEditingFinished.bind(this); - } - - handleHostNameChange(e) { - this.setState({hostName: e.target.value, changed: true}); - } - - handleEditingFinished() { - if (this.state.changed) { - this.setState({changed: false}); - this.props.onServerAddressChange(this.state.hostName.trim()); - } - } - - render() { - var hostOptions = []; - for (var key in KNOWN_HOSTS) { - var item = KNOWN_HOSTS[key]; - hostOptions.push( -