From b90c3ba2dfee0c2ae5bd6c0cc4936a652714c7d0 Mon Sep 17 00:00:00 2001 From: She Jiayu <42549358+jiayushe@users.noreply.github.com> Date: Thu, 14 Feb 2019 17:29:15 +0800 Subject: [PATCH 01/15] Add collaborative-editing feature using sharedb-ace --- package.json | 1 + sharedb-ace.d.ts | 1 + src/components/workspace/Editor.tsx | 39 ++ yarn.lock | 664 ++++++++++++++++++++++++++-- 4 files changed, 660 insertions(+), 45 deletions(-) create mode 100644 sharedb-ace.d.ts diff --git a/package.json b/package.json index 61198584b6..3e5d96011f 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "redux-saga": "^0.15.6", "showdown": "^1.9.0", "typesafe-actions": "^3.2.1", + "sharedb-ace": "^1.0.5", "utility-types": "^2.0.0" }, "devDependencies": { diff --git a/sharedb-ace.d.ts b/sharedb-ace.d.ts new file mode 100644 index 0000000000..0a314b4dbc --- /dev/null +++ b/sharedb-ace.d.ts @@ -0,0 +1 @@ +declare module 'sharedb-ace' diff --git a/src/components/workspace/Editor.tsx b/src/components/workspace/Editor.tsx index 3462d3064f..086d2a7a5d 100644 --- a/src/components/workspace/Editor.tsx +++ b/src/components/workspace/Editor.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import AceEditor, { Annotation } from 'react-ace'; import { HotKeys } from 'react-hotkeys'; +import sharedbAce from "sharedb-ace"; import 'brace/ext/searchbox'; import 'brace/mode/javascript'; @@ -21,12 +22,18 @@ export interface IEditorProps { handleUpdateHasUnsavedChanges?: (hasUnsavedChanges: boolean) => void; } +export interface IJSONData { + id: number; +} + class Editor extends React.PureComponent { private onChangeMethod: (newCode: string) => void; private onValidateMethod: (annotations: Annotation[]) => void; + private ace: any; constructor(props: IEditorProps) { super(props); + this.ace = React.createRef(); this.onChangeMethod = (newCode: string) => { if (this.props.handleUpdateHasUnsavedChanges) { this.props.handleUpdateHasUnsavedChanges(true); @@ -40,6 +47,25 @@ class Editor extends React.PureComponent { }; } + public componentDidMount() { + // this editor is the same as the one in line 5 of index.js of sharedb-ace-example + const editor = this.ace.current.editor; + // const session = editor.getSession(); + this.get("http://localhost:4000/gists/latest", (data: IJSONData) => { + const ShareAce = new sharedbAce(data.id, { + WsUrl: "ws://localhost:4000/ws", + pluginWsUrl: "ws://localhost:3108/ws", + namespace: "codepad", + }); + ShareAce.on('ready', () => { + ShareAce.add(editor, ["code"], [ + // SharedbAceRWControl, + // SharedbAceMultipleCursors + ]); + }); + }); + } + public render() { return ( @@ -59,6 +85,7 @@ class Editor extends React.PureComponent { editorProps={{ $blockScrolling: Infinity }} + ref={this.ace} fontSize={14} height="100%" highlightActiveLine={false} @@ -73,6 +100,18 @@ class Editor extends React.PureComponent { ); } + + private get(url: string, callback: (data: IJSONData) => void){ + const xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = () => { + if (xmlhttp.readyState === 4 && xmlhttp.status === 200){ + callback(JSON.parse(xmlhttp.responseText)); + } + }; + xmlhttp.open("GET", url, true); + xmlhttp.send(); + } + } /* Override handler, so does not trigger when focus is in editor */ diff --git a/yarn.lock b/yarn.lock index ccdc8a3842..ca6b7727c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -199,6 +199,44 @@ version "2.6.0" resolved "https://registry.yarnpkg.com/@pixi/filter-zoom-blur/-/filter-zoom-blur-2.6.0.tgz#4ad966a309372b731e0fc469d97dd51bf79d1bd6" +"@semantic-release/commit-analyzer@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@semantic-release/commit-analyzer/-/commit-analyzer-2.0.0.tgz#924d1e2c30167c6a472bed9f66ee8f8e077489b2" + integrity sha1-kk0eLDAWfGpHK+2fZu6Pjgd0ibI= + dependencies: + conventional-changelog "0.0.17" + +"@semantic-release/condition-travis@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@semantic-release/condition-travis/-/condition-travis-5.0.2.tgz#f4bb777a6c6db5565d70754a9b629233bd4a6597" + integrity sha1-9Lt3emxttVZdcHVKm2KSM71KZZc= + dependencies: + "@semantic-release/error" "^1.0.0" + semver "^5.0.3" + travis-deploy-once "1.0.0-node-0.10-support" + +"@semantic-release/error@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-1.0.0.tgz#bb8f8eeedd5c7f8c46f96b37ef39e1b8c376c1cc" + integrity sha1-u4+O7t1cf4xG+Ws37znhuMN2wcw= + +"@semantic-release/last-release-npm@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@semantic-release/last-release-npm/-/last-release-npm-1.2.1.tgz#ff748142ecf15354b833a86ba18205f7fce594ee" + integrity sha1-/3SBQuzxU1S4M6hroYIF9/zllO4= + dependencies: + "@semantic-release/error" "^1.0.0" + npm-registry-client "^7.0.1" + npmlog "^1.2.1" + +"@semantic-release/release-notes-generator@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@semantic-release/release-notes-generator/-/release-notes-generator-2.0.0.tgz#7c5da65689466d536a53fdfa9f4d62a3bd13c16e" + integrity sha1-fF2mVolGbVNqU/36n01io70TwW4= + dependencies: + conventional-changelog "0.0.17" + github-url-from-git "^1.4.0" + "@types/acorn@^4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.3.tgz#d1f3e738dde52536f9aad3d3380d14e448820afd" @@ -421,6 +459,14 @@ ag-grid@^18.0.1: version "18.0.1" resolved "https://registry.yarnpkg.com/ag-grid/-/ag-grid-18.0.1.tgz#e72ee2f7cd79063466fc99fa22f7f4e9880adda2" +agent-base@2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7" + integrity sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc= + dependencies: + extend "~3.0.0" + semver "~5.0.1" + agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce" @@ -523,6 +569,11 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi@^0.3.0, ansi@~0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21" + integrity sha1-DELU+xcWDVqa8eSEus4cZpIsGyE= + ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" @@ -563,6 +614,14 @@ archy@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" +are-we-there-yet@~1.0.0: + version "1.0.6" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz#a2d28c93102aa6cc96245a26cb954de06ec53f0c" + integrity sha1-otKMkxAqpsyWJFomy5VN4G7FPww= + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.0 || ^1.1.13" + are-we-there-yet@~1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" @@ -647,6 +706,11 @@ array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" +arraydiff@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/arraydiff/-/arraydiff-0.1.3.tgz#86a5436d7b72f1bdda5fd6d74e8724e42f83ce4d" + integrity sha1-hqVDbXty8b3aX9bXTock5C+Dzk0= + arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -709,7 +773,7 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@^1.4.0, async@^1.5.2: +async@^1.4.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -1294,6 +1358,15 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" +babel-polyfill@^6.16.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + integrity sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM= + dependencies: + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" + babel-preset-env@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48" @@ -1496,6 +1569,11 @@ block-stream@*: dependencies: inherits "~2.0.0" +bluebird@^3.4.6: + version "3.5.3" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" + integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== + bluebird@^3.4.7, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@~3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -2235,6 +2313,14 @@ config-chain@~1.1.11: ini "^1.3.4" proto-list "~1.2.1" +config-chain@~1.1.8: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + configstore@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" @@ -2272,6 +2358,17 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" +conventional-changelog@0.0.17: + version "0.0.17" + resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-0.0.17.tgz#5e0216600f4686190f0c82efbb0b3dd11b49ce34" + integrity sha1-XgIWYA9GhhkPDILvuws90RtJzjQ= + dependencies: + dateformat "^1.0.11" + event-stream "^3.3.0" + github-url-from-git "^1.4.0" + lodash "^3.6.0" + normalize-package-data "^1.0.3" + convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" @@ -2313,7 +2410,7 @@ core-js@^2.4.0, core-js@^2.5.0: version "2.5.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b" -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@1.0.2, core-util-is@^1.0.1, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2597,7 +2694,15 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +dateformat@^1.0.11: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + +debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2633,7 +2738,7 @@ deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" -deep-is@~0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -2762,7 +2867,7 @@ detect-port-alt@1.1.6: address "^1.0.1" debug "^2.6.0" -dezalgo@^1.0.0, dezalgo@~1.0.3: +dezalgo@^1.0.0, dezalgo@^1.0.1, dezalgo@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" dependencies: @@ -3189,6 +3294,11 @@ etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" +event-emitter-es6@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/event-emitter-es6/-/event-emitter-es6-1.1.5.tgz#ef95311b2e17aa39be763b031ce4af7ee9cb7849" + integrity sha1-75UxGy4Xqjm+djsDHOSvfunLeEk= + event-emitter@~0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" @@ -3196,6 +3306,19 @@ event-emitter@~0.3.5: d "1" es5-ext "~0.10.14" +event-stream@^3.3.0: + version "3.3.5" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.5.tgz#e5dd8989543630d94c6cf4d657120341fa31636b" + integrity sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g== + dependencies: + duplexer "^0.1.1" + from "^0.1.7" + map-stream "0.0.7" + pause-stream "^0.0.11" + split "^1.0.1" + stream-combiner "^0.2.2" + through "^2.3.8" + event-stream@~3.3.0: version "3.3.4" resolved "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" @@ -3357,14 +3480,14 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extend@3, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - external-editor@^2.0.4: version "2.2.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" @@ -3416,6 +3539,7 @@ fast-deep-equal@^1.0.0: fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= fast-json-stable-stringify@^2.0.0: version "2.0.0" @@ -3588,6 +3712,14 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-0.0.7.tgz#34b90bab2a911aa347571da90f22bd36ecd8a919" + integrity sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk= + dependencies: + debug "^2.2.0" + stream-consume "^0.1.0" + for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -3602,6 +3734,11 @@ foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" +foreachasync@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6" + integrity sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY= + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -3671,7 +3808,7 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -from@~0: +from@^0.1.7, from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" @@ -3771,6 +3908,17 @@ function.prototype.name@^1.0.3: function-bind "^1.1.1" is-callable "^1.1.3" +gauge@~1.2.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93" + integrity sha1-6c7FSD09TuDvRLYKfZnkk14TbZM= + dependencies: + ansi "^0.3.0" + has-unicode "^2.0.0" + lodash.pad "^4.1.0" + lodash.padend "^4.1.0" + lodash.padstart "^4.1.0" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -3841,6 +3989,47 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +git-head@^1.2.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/git-head/-/git-head-1.20.1.tgz#036d16a4b374949e4e3daf15827903686d3ccd52" + integrity sha1-A20WpLN0lJ5OPa8VgnkDaG08zVI= + dependencies: + git-refs "^1.1.3" + +git-refs@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/git-refs/-/git-refs-1.1.3.tgz#83097cb3a92585c4a4926ec54e2182df9e20e89d" + integrity sha1-gwl8s6klhcSkkm7FTiGC354g6J0= + dependencies: + path-object "^2.3.0" + slash "^1.0.0" + walk "^2.3.9" + +github-url-from-git@^1.3.0, github-url-from-git@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/github-url-from-git/-/github-url-from-git-1.5.0.tgz#f985fedcc0a9aa579dc88d7aff068d55cc6251a0" + integrity sha1-+YX+3MCpqledyI16/waNVcxiUaA= + +github-url-from-username-repo@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/github-url-from-username-repo/-/github-url-from-username-repo-1.0.2.tgz#7dd79330d2abe69c10c2cef79714c97215791dfa" + integrity sha1-fdeTMNKr5pwQws73lxTJchV5Hfo= + +github@^8.0.0: + version "8.2.1" + resolved "https://registry.yarnpkg.com/github/-/github-8.2.1.tgz#616b2211fbcd1cc8631669aed67653e62eb53816" + integrity sha1-YWsiEfvNHMhjFmmu1nZT5i61OBY= + dependencies: + follow-redirects "0.0.7" + https-proxy-agent "^1.0.0" + mime "^1.2.11" + netrc "^0.1.4" + +github@~0.1.10: + version "0.1.16" + resolved "https://registry.yarnpkg.com/github/-/github-0.1.16.tgz#895d2a85b0feb7980d89ac0ce4f44dcaa03f17b5" + integrity sha1-iV0qhbD+t5gNiawM5PRNyqA/F7U= + glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -4097,6 +4286,11 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" +hat@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a" + integrity sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo= + hawk@3.1.3, hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -4172,6 +4366,11 @@ hosted-git-info@^2.1.4: version "2.6.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.0.tgz#23235b29ab230c576aab0d4f13fc046b0b038222" +hosted-git-info@^2.1.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== + hosted-git-info@^2.6.0: version "2.6.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.1.tgz#6e4cee78b01bb849dcf93527708c69fdbee410df" @@ -4315,6 +4514,15 @@ https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" +https-proxy-agent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" + integrity sha1-NffabEjOTdv6JkiRrFk+5f+GceY= + dependencies: + agent-base "2" + debug "2" + extend "3" + https-proxy-agent@^2.2.0, https-proxy-agent@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" @@ -4459,7 +4667,7 @@ inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" -ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@^1.2.0, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" @@ -5346,6 +5554,7 @@ json-schema-traverse@^0.3.0: json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema@0.2.3: version "0.2.3" @@ -5592,6 +5801,19 @@ lodash-es@^4.2.1: version "4.17.8" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.8.tgz#6fa8c8c5d337481df0bdf1c0d899d42473121e45" +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + integrity sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4= + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= + lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -5599,10 +5821,34 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" +lodash._bindcallback@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" + integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= + +lodash._createassigner@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" + integrity sha1-g4pbri/aymOsIt7o4Z+k5taXCxE= + dependencies: + lodash._bindcallback "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash.restparam "^3.0.0" + lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= + lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -5611,6 +5857,15 @@ lodash._root@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" +lodash.assign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" + integrity sha1-POnwI0tLIiPilrj6CsH+6OvKZPo= + dependencies: + lodash._baseassign "^3.0.0" + lodash._createassigner "^3.0.0" + lodash.keys "^3.0.0" + lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -5639,6 +5894,16 @@ lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" @@ -5663,6 +5928,15 @@ lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -5671,6 +5945,26 @@ lodash.mergewith@^4.6.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" +lodash.pad@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70" + integrity sha1-QzCUmoM6fI2iLMIPaibE1Z3runA= + +lodash.padend@^4.1.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" + integrity sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4= + +lodash.padstart@^4.1.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" + integrity sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs= + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -5708,7 +6002,7 @@ lodash.without@~4.4.0: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" -lodash@^3.10.1: +lodash@^3.10.1, lodash@^3.6.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" @@ -5720,10 +6014,20 @@ lodash@^4.17.11: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" +lodash@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.3.1.tgz#a4663b53686b895ff074e2ba504dfb76a8e2b770" + integrity sha1-pGY7U2hriV/wdOK6UE37dqjit3A= + log-driver@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" +logdown@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/logdown/-/logdown-2.2.0.tgz#23698490bb0f4eab8e93205c6209d31c06de35b2" + integrity sha1-I2mEkLsPTquOkyBcYgnTHAbeNbI= + loglevel@^1.4.1: version "1.6.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" @@ -5787,6 +6091,11 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-error@^1.1.1: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + "make-fetch-happen@^2.5.0 || 3 || 4", make-fetch-happen@^4.0.0, make-fetch-happen@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083" @@ -5833,6 +6142,11 @@ map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" +map-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" + integrity sha1-ih8HiW2CsQkmvTdEokIACfiJdKg= + map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" @@ -5979,7 +6293,7 @@ mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" -mime@^1.4.1, mime@^1.5.0: +mime@^1.2.11, mime@^1.4.1, mime@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -6188,6 +6502,16 @@ neo-async@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" +nerf-dart@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nerf-dart/-/nerf-dart-1.0.0.tgz#e6dab7febf5ad816ea81cf5c629c5a0ebde72c1a" + integrity sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo= + +netrc@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444" + integrity sha1-a+lPysqNd63gqWcNxGCRTJRHJEQ= + next-tick@1: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -6371,19 +6695,28 @@ nomnom@~1.6.2: colors "0.5.x" underscore "~1.4.4" -"nopt@2 || 3": +"nopt@2 || 3", nopt@~3.0.1: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: abbrev "1" -nopt@^4.0.1, nopt@~4.0.1: +nopt@^4.0.0, nopt@^4.0.1, nopt@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" dependencies: abbrev "1" osenv "^0.1.4" +normalize-package-data@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-1.0.3.tgz#8be955b8907af975f1a4584ea8bb9b41492312f5" + integrity sha1-i+lVuJB6+XXxpFhOqLubQUkjEvU= + dependencies: + github-url-from-git "^1.3.0" + github-url-from-username-repo "^1.0.0" + semver "2 || 3 || 4" + normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.4.0, "normalize-package-data@~1.0.1 || ^2.0.0", normalize-package-data@~2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" @@ -6462,6 +6795,14 @@ npm-logical-tree@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz#44610141ca24664cad35d1e607176193fd8f5b88" +"npm-package-arg@^3.0.0 || ^4.0.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-4.2.1.tgz#593303fdea85f7c422775f17f9eb7670f680e3ec" + integrity sha1-WTMD/eqF98Qid18X+et2cPaA4+w= + dependencies: + hosted-git-info "^2.1.5" + semver "^5.1.0" + "npm-package-arg@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", "npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", "npm-package-arg@^5.1.2 || 6", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1" @@ -6499,6 +6840,23 @@ npm-profile@^3.0.1: aproba "^1.1.2 || 2" make-fetch-happen "^2.5.0 || 3 || 4" +npm-registry-client@^7.0.1: + version "7.5.0" + resolved "https://registry.yarnpkg.com/npm-registry-client/-/npm-registry-client-7.5.0.tgz#0f6dd6e5d11424cfa99fce5b930feaf09b4f7f04" + integrity sha1-D23W5dEUJM+pn85bkw/q8JtPfwQ= + dependencies: + concat-stream "^1.5.2" + graceful-fs "^4.1.6" + normalize-package-data "~1.0.1 || ^2.0.0" + npm-package-arg "^3.0.0 || ^4.0.0" + once "^1.3.3" + request "^2.74.0" + retry "^0.10.0" + semver "2 >=2.2.1 || 3.x || 4 || 5" + slide "^1.1.3" + optionalDependencies: + npmlog "2 || ^3.1.0 || ^4.0.0" + npm-registry-client@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/npm-registry-client/-/npm-registry-client-8.5.1.tgz#8115809c0a4b40938b8a109b8ea74d26c6f5d7f1" @@ -6677,6 +7035,22 @@ npm@^6.0.0: wrappy "~1.0.2" write-file-atomic "^2.3.0" +npmconf@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/npmconf/-/npmconf-2.1.3.tgz#1cbe5dd02e899d365fed7260b54055473f90a15c" + integrity sha512-iTK+HI68GceCoGOHAQiJ/ik1iDfI7S+cgyG8A+PP18IU3X83kRhQIRhAUNj4Bp2JMx6Zrt5kCiozYa9uGWTjhA== + dependencies: + config-chain "~1.1.8" + inherits "~2.0.0" + ini "^1.2.0" + mkdirp "^0.5.0" + nopt "~3.0.1" + once "~1.3.0" + osenv "^0.1.0" + safe-buffer "^5.1.1" + semver "2 || 3 || 4" + uid-number "0.0.5" + "npmlog@0 || 1 || 2 || 3 || 4", "npmlog@2 || ^3.1.0 || ^4.0.0", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@~4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -6686,6 +7060,15 @@ npm@^6.0.0: gauge "~2.7.3" set-blocking "~2.0.0" +npmlog@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-1.2.1.tgz#28e7be619609b53f7ad1dd300a10d64d716268b6" + integrity sha1-KOe+YZYJtT960d0wChDWTXFiaLY= + dependencies: + ansi "~0.3.0" + are-we-there-yet "~1.0.0" + gauge "~1.2.0" + nth-check@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" @@ -6809,6 +7192,13 @@ once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0: dependencies: wrappy "1" +once@~1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + integrity sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA= + dependencies: + wrappy "1" + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -6881,13 +7271,18 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -osenv@0, osenv@^0.1.4, osenv@^0.1.5: +osenv@0, osenv@^0.1.0, osenv@^0.1.4, osenv@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +ot-json0@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ot-json0/-/ot-json0-1.1.0.tgz#f5edeff162673b62f0f136bb64724c40ac5e590d" + integrity sha512-wf5fci7GGpMYRDnbbdIFQymvhsbFACMHtxjivQo5KgvAHlxekyfJ9aPsRr6YfFQthQkk4bmsl5yESrZwC/oMYQ== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -6998,6 +7393,11 @@ parse-asn1@^5.0.0: evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" +parse-github-repo-url@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" + integrity sha1-nn2LslKmy2ukJZUGC3v23z28H1A= + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -7076,6 +7476,14 @@ path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" +path-object@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/path-object/-/path-object-2.3.0.tgz#03e46653e5c375c60af1cabdd94bc6448a5d9110" + integrity sha1-A+RmU+XDdcYK8cq92UvGRIpdkRA= + dependencies: + core-util-is "^1.0.1" + lodash.assign "^3.0.0" + path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" @@ -7110,7 +7518,7 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" -pause-stream@0.0.11: +pause-stream@0.0.11, pause-stream@^0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" dependencies: @@ -8147,7 +8555,7 @@ read@1, read@~1.0.1, read@~1.0.7: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: @@ -8209,6 +8617,11 @@ realpath-native@^1.0.0: dependencies: util.promisify "^1.0.0" +reconnecting-websocket@^3.0.3: + version "3.2.2" + resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-3.2.2.tgz#8097514e926e9855e03c39e76efa2e3d1f371bee" + integrity sha512-SWSfoXiaHVOqXuPWFgGWeUxKnb5HIY7I/Fh5C/hy4wUOgeOh7YIMXEiv5/eHBlNs4tNzCrO5YDR9AH62NWle0Q== + recursive-readdir@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99" @@ -8259,6 +8672,11 @@ regenerate@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" +regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg= + regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" @@ -8369,6 +8787,13 @@ request-promise-core@1.1.1: dependencies: lodash "^4.13.1" +request-promise-core@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" + integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== + dependencies: + lodash "^4.17.11" + request-promise-native@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" @@ -8377,6 +8802,16 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" +request-promise@^4.1.1: + version "4.2.4" + resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.4.tgz#1c5ed0d71441e38ad58c7ce4ea4ea5b06d54b310" + integrity sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg== + dependencies: + bluebird "^3.5.0" + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + request@2.81.0, "request@>=2.9.0 <2.82.0": version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" @@ -8429,6 +8864,31 @@ request@^2.74.0, request@^2.79.0, request@^2.86.0: tunnel-agent "^0.6.0" uuid "^3.1.0" +request@^2.78.0, request@^2.87.0, request@^2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + request@^2.83.0: version "2.85.0" resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" @@ -8456,31 +8916,6 @@ request@^2.83.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -request@^2.87.0, request@^2.88.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -8493,6 +8928,11 @@ require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" +require-relative@^0.8.7: + version "0.8.7" + resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" + integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4= + requires-port@1.0.x, requires-port@1.x.x, requires-port@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -8600,6 +9040,13 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" +run-auto@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/run-auto/-/run-auto-2.0.3.tgz#d5caaa61b458dbd08f49b913a1ad64f5b5db4952" + integrity sha512-kmEkpcJFly4+IWOG9BvwroclzdYUKFSoi92h1FVK41zat+38epu8xjqBDz9eC0sWbPnB3ONYDbtTMnQeNdYhAw== + dependencies: + dezalgo "^1.0.1" + run-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" @@ -8610,6 +9057,11 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +run-series@^1.1.3: + version "1.1.8" + resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.8.tgz#2c4558f49221e01cd6371ff4e0a1e203e460fc36" + integrity sha512-+GztYEPRpIsQoCSraWHDBs9WVy4eVME16zhOtDB4H9J4xN0XRhknnmLOl+4gRgZtu8dpp9N/utSPjKH/xmDzXg== + rx-lite-aggregates@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" @@ -8702,6 +9154,30 @@ selfsigned@^1.9.1: dependencies: node-forge "0.7.1" +semantic-release@^6.3.6: + version "6.3.6" + resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-6.3.6.tgz#629d0aec90b38a2957a57a4a9ee1214af51928c7" + integrity sha1-Yp0K7JCziilXpXpKnuEhSvUZKMc= + dependencies: + "@semantic-release/commit-analyzer" "^2.0.0" + "@semantic-release/condition-travis" "^5.0.2" + "@semantic-release/error" "^1.0.0" + "@semantic-release/last-release-npm" "^1.2.1" + "@semantic-release/release-notes-generator" "^2.0.0" + git-head "^1.2.1" + github "^8.0.0" + lodash "^4.0.0" + nerf-dart "^1.0.0" + nopt "^4.0.0" + normalize-package-data "^2.3.4" + npmconf "^2.1.2" + npmlog "^4.0.0" + parse-github-repo-url "^1.3.0" + require-relative "^0.8.7" + run-auto "^2.0.0" + run-series "^1.1.3" + semver "^5.2.0" + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -8717,6 +9193,21 @@ semver-diff@^2.0.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" +"semver@2 || 3 || 4": + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= + +semver@^5.2.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== + +semver@~5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" + integrity sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no= + semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -8820,6 +9311,29 @@ sha@~2.0.1: graceful-fs "^4.1.2" readable-stream "^2.0.2" +sharedb-ace@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sharedb-ace/-/sharedb-ace-1.0.5.tgz#fcc7397f27816afe8a266a6356dbea45669b57d3" + integrity sha1-/Mc5fyeBav6KJmpjVtvqRWabV9M= + dependencies: + event-emitter-es6 "^1.1.5" + logdown "^2.0.3" + reconnecting-websocket "^3.0.3" + semantic-release "^6.3.6" + sharedb "^1.0.0-beta.7" + +sharedb@^1.0.0-beta.7: + version "1.0.0-beta.18" + resolved "https://registry.yarnpkg.com/sharedb/-/sharedb-1.0.0-beta.18.tgz#adfcb779ad150a09fdb5d01290f3865eef7b55bd" + integrity sha512-IK7xyx+BMxOl9vhng885/k05SzZWbcFDSSKHpMdujYQStex+leOQg3ZNiuxpUBWt33jA1GmqSpdR8ngIQ9O6bg== + dependencies: + arraydiff "^0.1.1" + async "^1.4.2" + deep-is "^0.1.3" + hat "0.0.3" + make-error "^1.1.1" + ot-json0 "^1.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -9085,6 +9599,13 @@ split@0.3: dependencies: through "2" +split@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -9138,9 +9659,10 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -stealthy-require@^1.1.0: +stealthy-require@^1.1.0, stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= stream-browserify@^2.0.1: version "2.0.1" @@ -9149,12 +9671,25 @@ stream-browserify@^2.0.1: inherits "~2.0.1" readable-stream "^2.0.2" +stream-combiner@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" + integrity sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg= + dependencies: + duplexer "~0.1.1" + through "~2.3.4" + stream-combiner@~0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" dependencies: duplexer "~0.1.1" +stream-consume@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.1.tgz#d3bdb598c2bd0ae82b8cac7ac50b1107a7996c48" + integrity sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg== + stream-each@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" @@ -9436,7 +9971,7 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1: +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -9529,6 +10064,27 @@ tr46@^1.0.0: dependencies: punycode "^2.1.0" +travis-ci@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/travis-ci/-/travis-ci-2.2.0.tgz#0efdfdc1c2572a1294f2637c6a55a3394697604e" + integrity sha512-6m+VoKD/va53D4O8I1SLtoLXKLHVUoEL2GksMKLUR0yUqYqco2kj5QB4gdBdFAMw3XL0VBozFsGw8jb6MrrIEQ== + dependencies: + github "~0.1.10" + lodash "~1.3.1" + request "^2.87.0" + underscore.string "~2.2.0rc" + +travis-deploy-once@1.0.0-node-0.10-support: + version "1.0.0-node-0.10-support" + resolved "https://registry.yarnpkg.com/travis-deploy-once/-/travis-deploy-once-1.0.0-node-0.10-support.tgz#98ecce7d95b2f4ba5dcdeeebf54b9df87713d5e6" + integrity sha1-mOzOfZWy9Lpdze7r9Uud+HcT1eY= + dependencies: + babel-polyfill "^6.16.0" + bluebird "^3.4.6" + request "^2.78.0" + request-promise "^4.1.1" + travis-ci "^2.1.1" + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -9716,6 +10272,11 @@ uglifyjs-webpack-plugin@^1.1.8: webpack-sources "^1.1.0" worker-farm "^1.5.2" +uid-number@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.5.tgz#5a3db23ef5dbd55b81fce0ec9a2ac6fccdebb81e" + integrity sha1-Wj2yPvXb1VuB/ODsmirG/M3ruB4= + uid-number@0.0.6, uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -9724,6 +10285,11 @@ umask@^1.1.0, umask@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" +underscore.string@~2.2.0rc: + version "2.2.1" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.2.1.tgz#d7c0fa2af5d5a1a67f4253daee98132e733f0f19" + integrity sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk= + underscore@~1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" @@ -9824,6 +10390,7 @@ uri-js@^3.0.2: uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== dependencies: punycode "^2.1.0" @@ -9970,6 +10537,13 @@ w3c-hr-time@^1.0.1: dependencies: browser-process-hrtime "^0.1.2" +walk@^2.3.9: + version "2.3.14" + resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.14.tgz#60ec8631cfd23276ae1e7363ce11d626452e1ef3" + integrity sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg== + dependencies: + foreachasync "^3.0.0" + walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" From f660dec87d26430076df6725436de616aa9e9dfd Mon Sep 17 00:00:00 2001 From: zephyr Date: Thu, 14 Mar 2019 14:39:20 +0800 Subject: [PATCH 02/15] Allow multiple sessions by implementing session id --- src/actions/actionTypes.ts | 1 + src/actions/workspaces.ts | 11 ++++ src/components/Playground.tsx | 7 +++ src/components/__tests__/Playground.tsx | 2 + .../academy/grading/GradingWorkspace.tsx | 2 + .../assessment/AssessmentWorkspace.tsx | 2 + src/components/commons/controlButton.tsx | 5 +- src/components/workspace/ControlBar.tsx | 63 +++++++++++++++++++ src/components/workspace/Editor.tsx | 43 ++++++------- src/components/workspace/index.tsx | 3 +- src/containers/PlaygroundContainer.ts | 4 ++ src/reducers/states.ts | 2 + src/reducers/workspaces.ts | 9 +++ 13 files changed, 129 insertions(+), 25 deletions(-) diff --git a/src/actions/actionTypes.ts b/src/actions/actionTypes.ts index b30b29db39..e7d4fdbc04 100644 --- a/src/actions/actionTypes.ts +++ b/src/actions/actionTypes.ts @@ -39,6 +39,7 @@ export const EVAL_REPL = 'EVAL_REPL'; export const PLAYGROUND_EXTERNAL_SELECT = 'PLAYGROUND_EXTERNAL_SELECT '; export const RESET_WORKSPACE = 'RESET_WORKSPACE'; export const SEND_REPL_INPUT_TO_OUTPUT = 'SEND_REPL_INPUT_TO_OUTPUT'; +export const SET_EDITOR_SESSION_ID = 'SET_EDITOR_SESSION_ID'; export const TOGGLE_EDITOR_AUTORUN = 'TOGGLE_EDITOR_AUTORUN'; export const UPDATE_CURRENT_ASSESSMENT_ID = 'UPDATE_CURRENT_ASSESSMENT_ID'; export const UPDATE_CURRENT_SUBMISSION_ID = 'UPDATE_CURRENT_SUBMISSION_ID'; diff --git a/src/actions/workspaces.ts b/src/actions/workspaces.ts index 615ab560e7..335b8aae78 100755 --- a/src/actions/workspaces.ts +++ b/src/actions/workspaces.ts @@ -209,6 +209,17 @@ export const resetWorkspace = ( } }); +export const setEditorSessionId: ActionCreator = ( + workspaceLocation: WorkspaceLocation, + editorSessionId: string +) => ({ + type: actionTypes.SET_EDITOR_SESSION_ID, + payload: { + workspaceLocation, + editorSessionId + } +}); + export const updateCurrentAssessmentId = (assessmentId: number, questionId: number) => ({ type: actionTypes.UPDATE_CURRENT_ASSESSMENT_ID, payload: { diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index 99c409c761..a7350800cf 100755 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -34,6 +34,7 @@ export interface IPlaygroundProps extends IDispatchProps, IStateProps, RouteComp export interface IStateProps { activeTab: number; + editorSessionId: string; editorValue: string; editorWidth: string; isEditorAutorun: boolean; @@ -60,6 +61,7 @@ export interface IDispatchProps { handleReplEval: () => void; handleReplOutputClear: () => void; handleReplValueChange: (newValue: string) => void; + handleSetEditorSessionId: (editorSessionId: string) => void; handleSideContentHeightChange: (heightChange: number) => void; handleToggleEditorAutorun: () => void; } @@ -82,6 +84,7 @@ class Playground extends React.Component { public render() { const workspaceProps: WorkspaceProps = { controlBarProps: { + editorSessionId: this.props.editorSessionId, externalLibraryName: this.props.externalLibraryName, handleChapterSelect: ({ chapter }: { chapter: number }, e: any) => this.props.handleChapterSelect(chapter), @@ -92,9 +95,12 @@ class Playground extends React.Component { handleInterruptEval: this.props.handleInterruptEval, handleReplEval: this.props.handleReplEval, handleReplOutputClear: this.props.handleReplOutputClear, + handleSetEditorSessionId: this.props.handleSetEditorSessionId, handleToggleEditorAutorun: this.props.handleToggleEditorAutorun, hasChapterSelect: true, hasEditorAutorunButton: true, + hasInviteButton: true, + hasJoinButton: true, hasSaveButton: false, hasShareButton: true, isEditorAutorun: this.props.isEditorAutorun, @@ -105,6 +111,7 @@ class Playground extends React.Component { }, editorProps: { editorValue: this.props.editorValue, + editorSessionId: this.props.editorSessionId, handleEditorEval: this.props.handleEditorEval, handleEditorValueChange: this.props.handleEditorValueChange, isEditorAutorun: this.props.isEditorAutorun diff --git a/src/components/__tests__/Playground.tsx b/src/components/__tests__/Playground.tsx index 100b60c377..e991c26e2c 100644 --- a/src/components/__tests__/Playground.tsx +++ b/src/components/__tests__/Playground.tsx @@ -9,6 +9,7 @@ const baseProps = { editorValue: '', isRunning: false, activeTab: 0, + editorSessionId: '', editorWidth: '50%', isEditorAutorun: false, sideContentHeight: 40, @@ -29,6 +30,7 @@ const baseProps = { handleReplEval: () => {}, handleReplOutputClear: () => {}, handleReplValueChange: (code: string) => {}, + handleSetEditorSessionId: (editorSessionId: string) => {}, handleSideContentHeightChange: (h: number) => {}, handleToggleEditorAutorun: () => {} }; diff --git a/src/components/academy/grading/GradingWorkspace.tsx b/src/components/academy/grading/GradingWorkspace.tsx index 0c1b413255..faf270c753 100755 --- a/src/components/academy/grading/GradingWorkspace.tsx +++ b/src/components/academy/grading/GradingWorkspace.tsx @@ -221,6 +221,8 @@ class GradingWorkspace extends React.Component { handleReplOutputClear: this.props.handleReplOutputClear, hasChapterSelect: false, hasEditorAutorunButton: false, + hasInviteButton: false, + hasJoinButton: false, hasSaveButton: false, hasShareButton: false, isRunning: this.props.isRunning, diff --git a/src/components/assessment/AssessmentWorkspace.tsx b/src/components/assessment/AssessmentWorkspace.tsx index ddc16ce912..d6f237f99b 100755 --- a/src/components/assessment/AssessmentWorkspace.tsx +++ b/src/components/assessment/AssessmentWorkspace.tsx @@ -272,6 +272,8 @@ class AssessmentWorkspace extends React.Component< handleReplValueChange: this.props.handleReplValueChange, hasChapterSelect: false, hasEditorAutorunButton: false, + hasInviteButton: false, + hasJoinButton: false, hasSaveButton: !beforeNow(this.props.closeDate) && this.props.assessment!.questions[questionId].type !== QuestionTypes.mcq, diff --git a/src/components/commons/controlButton.tsx b/src/components/commons/controlButton.tsx index 07cd95800f..d925bc0b32 100644 --- a/src/components/commons/controlButton.tsx +++ b/src/components/commons/controlButton.tsx @@ -7,6 +7,7 @@ type controlButtonOptionals = { iconOnRight?: boolean; intent?: Intent; minimal?: boolean; + type?: string; }; const defaultOptions = { @@ -14,7 +15,8 @@ const defaultOptions = { fullWidth: false, iconOnRight: false, intent: Intent.NONE, - minimal: true + minimal: true, + type: '' }; export function controlButton( @@ -30,6 +32,7 @@ export function controlButton( props.intent = opts.intent === undefined ? Intent.NONE : opts.intent; props.minimal = opts.minimal !== undefined && opts.minimal; props.className = opts.className; + props.type = opts.type; if (icon) { opts.iconOnRight ? (props.rightIcon = icon) : (props.icon = icon); } diff --git a/src/components/workspace/ControlBar.tsx b/src/components/workspace/ControlBar.tsx index 5bf80be90e..31068d1e02 100755 --- a/src/components/workspace/ControlBar.tsx +++ b/src/components/workspace/ControlBar.tsx @@ -17,6 +17,7 @@ export type ControlBarProps = { queryString?: string; questionProgress: [number, number] | null; sourceChapter: number; + editorSessionId?: string; externalLibraryName?: string; handleChapterSelect?: (i: IChapter, e: React.ChangeEvent) => void; handleEditorEval: () => void; @@ -25,9 +26,12 @@ export type ControlBarProps = { handleInterruptEval: () => void; handleReplEval: () => void; handleReplOutputClear: () => void; + handleSetEditorSessionId?: (editorSessionId: string) => void; handleToggleEditorAutorun?: () => void; hasChapterSelect: boolean; hasEditorAutorunButton: boolean; + hasInviteButton: boolean; + hasJoinButton: boolean; hasSaveButton: boolean; hasShareButton: boolean; hasUnsavedChanges?: boolean; @@ -66,10 +70,14 @@ class ControlBar extends React.PureComponent { }; private shareInputElem: HTMLInputElement; + private joinInputElem: React.RefObject; + private inviteInputElem: HTMLInputElement; constructor(props: ControlBarProps) { super(props); this.selectShareInputText = this.selectShareInputText.bind(this); + this.selectInviteInputText = this.selectInviteInputText.bind(this); + this.joinInputElem = React.createRef(); } public render() { @@ -83,6 +91,23 @@ class ControlBar extends React.PureComponent { } private editorControl() { + const handleStartInvite = () => { + if (this.props.editorSessionId === '') { + const xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = () => { + if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { + const id = JSON.parse(xmlhttp.responseText).id; + this.props.handleSetEditorSessionId!(id); + } + }; + xmlhttp.open('GET', 'https://13.250.109.61/gists/latest', true); + xmlhttp.send(); + } + }; + const handleStartJoining = (e: React.FormEvent) => { + this.props.handleSetEditorSessionId!(this.joinInputElem.current!.value); + e.preventDefault(); + }; const runButton = ( {controlButton('Run', IconNames.PLAY, this.props.handleEditorEval)} @@ -98,6 +123,7 @@ class ControlBar extends React.PureComponent { const shareUrl = `${window.location.protocol}//${window.location.hostname}/playground#${ this.props.queryString }`; + const shareButton = this.props.hasShareButton ? ( {controlButton('Share', IconNames.SHARE, this.props.handleGenerateLz)} @@ -123,6 +149,37 @@ class ControlBar extends React.PureComponent { ) : ( undefined ); + const inviteButton = this.props.hasInviteButton ? ( + + {controlButton('Invite', IconNames.SHARE, handleStartInvite)} + <> + (this.inviteInputElem = e!)} + onFocus={this.selectInviteInputText} + /> + + {controlButton('', IconNames.DUPLICATE, this.selectInviteInputText)} + + + + ) : ( + undefined + ); + const joinButton = this.props.hasJoinButton ? ( + + {controlButton('Join', IconNames.CHAT)} + <> +
+ + {controlButton('', IconNames.KEY_ENTER, null, { type: 'submit' })} +
+ +
+ ) : ( + undefined + ); const chapterSelectButton = this.props.hasChapterSelect ? chapterSelect(this.props.sourceChapter, this.props.handleChapterSelect) : undefined; @@ -142,6 +199,7 @@ class ControlBar extends React.PureComponent { {saveButton} {shareButton} {chapterSelectButton} {externalSelectButton} {this.props.isEditorAutorun ? stopAutorunButton : startAutorunButton} + {inviteButton} {joinButton} ); } @@ -194,6 +252,11 @@ class ControlBar extends React.PureComponent { this.shareInputElem.select(); } + private selectInviteInputText() { + this.inviteInputElem.focus(); + this.inviteInputElem.select(); + } + private hasNextButton() { return ( this.props.questionProgress && this.props.questionProgress[0] < this.props.questionProgress[1] diff --git a/src/components/workspace/Editor.tsx b/src/components/workspace/Editor.tsx index 086d2a7a5d..2265dd451b 100644 --- a/src/components/workspace/Editor.tsx +++ b/src/components/workspace/Editor.tsx @@ -1,7 +1,8 @@ +// tslint:disable:no-console import * as React from 'react'; import AceEditor, { Annotation } from 'react-ace'; import { HotKeys } from 'react-hotkeys'; -import sharedbAce from "sharedb-ace"; +import sharedbAce from 'sharedb-ace'; import 'brace/ext/searchbox'; import 'brace/mode/javascript'; @@ -16,6 +17,7 @@ import 'brace/theme/cobalt'; */ export interface IEditorProps { isEditorAutorun?: boolean; + editorSessionId?: string; editorValue: string; handleEditorEval: () => void; handleEditorValueChange: (newCode: string) => void; @@ -51,22 +53,29 @@ class Editor extends React.PureComponent { // this editor is the same as the one in line 5 of index.js of sharedb-ace-example const editor = this.ace.current.editor; // const session = editor.getSession(); - this.get("http://localhost:4000/gists/latest", (data: IJSONData) => { - const ShareAce = new sharedbAce(data.id, { - WsUrl: "ws://localhost:4000/ws", - pluginWsUrl: "ws://localhost:3108/ws", - namespace: "codepad", + if (this.props.editorSessionId !== '') { + console.log('Component mounted with id = ' + this.props.editorSessionId); + const ShareAce = new sharedbAce(this.props.editorSessionId, { + WsUrl: 'wss://13.250.109.61/ws', + pluginWsUrl: 'ws://localhost:3108/ws', + namespace: 'codepad' }); ShareAce.on('ready', () => { - ShareAce.add(editor, ["code"], [ - // SharedbAceRWControl, - // SharedbAceMultipleCursors - ]); + ShareAce.add( + editor, + ['code'], + [ + // SharedbAceRWControl, + // SharedbAceMultipleCursors + ] + ); }); - }); + } } public render() { + console.log('Starting render: editorSessionId = ' + this.props.editorSessionId); + console.log('Starting render: key = ' + this.props.editorSessionId); return (
@@ -100,18 +109,6 @@ class Editor extends React.PureComponent { ); } - - private get(url: string, callback: (data: IJSONData) => void){ - const xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = () => { - if (xmlhttp.readyState === 4 && xmlhttp.status === 200){ - callback(JSON.parse(xmlhttp.responseText)); - } - }; - xmlhttp.open("GET", url, true); - xmlhttp.send(); - } - } /* Override handler, so does not trigger when focus is in editor */ diff --git a/src/components/workspace/index.tsx b/src/components/workspace/index.tsx index 1cbb2756b1..21c1eb780c 100644 --- a/src/components/workspace/index.tsx +++ b/src/components/workspace/index.tsx @@ -153,7 +153,8 @@ class Workspace extends React.Component { */ private createWorkspaceInput = (props: WorkspaceProps) => { if (props.editorProps) { - return ; + // Set key to force remount of Editor component when session id changes + return ; } else { return ; } diff --git a/src/containers/PlaygroundContainer.ts b/src/containers/PlaygroundContainer.ts index 8e5de66cc5..fd58ac8dff 100644 --- a/src/containers/PlaygroundContainer.ts +++ b/src/containers/PlaygroundContainer.ts @@ -15,6 +15,7 @@ import { evalRepl, generateLzString, playgroundExternalSelect, + setEditorSessionId, toggleEditorAutorun, updateEditorValue, updateReplValue, @@ -26,6 +27,7 @@ import { IState } from '../reducers/states'; const mapStateToProps: MapStateToProps = state => ({ activeTab: state.workspaces.playground.sideContentActiveTab, + editorSessionId: state.workspaces.playground.editorSessionId, editorWidth: state.workspaces.playground.editorWidth, editorValue: state.workspaces.playground.editorValue!, isEditorAutorun: state.workspaces.playground.isEditorAutorun, @@ -57,6 +59,8 @@ const mapDispatchToProps: MapDispatchToProps = (dispatch: Di handleReplEval: () => evalRepl(location), handleReplOutputClear: () => clearReplOutput(location), handleReplValueChange: (newValue: string) => updateReplValue(newValue, location), + handleSetEditorSessionId: (editorSessionId: string) => + setEditorSessionId(location, editorSessionId), handleSideContentHeightChange: (heightChange: number) => changeSideContentHeight(heightChange, location), handleToggleEditorAutorun: () => toggleEditorAutorun(location) diff --git a/src/reducers/states.ts b/src/reducers/states.ts index 97bef0603c..517639b3d9 100644 --- a/src/reducers/states.ts +++ b/src/reducers/states.ts @@ -58,6 +58,7 @@ export interface IWorkspaceManagerState { export interface IWorkspaceState { readonly context: Context; + readonly editorSessionId: string; readonly editorValue: string | null; readonly editorWidth: string; readonly isEditorAutorun: boolean; @@ -197,6 +198,7 @@ export const defaultEditorValue = '// Type your program in here!'; */ export const createDefaultWorkspace = (location: WorkspaceLocation): IWorkspaceState => ({ context: createContext(latestSourceChapter, [], location), + editorSessionId: '', editorValue: location === WorkspaceLocations.playground ? defaultEditorValue : null, editorWidth: '50%', output: [], diff --git a/src/reducers/workspaces.ts b/src/reducers/workspaces.ts index b248f2ca7c..e1200a8c28 100644 --- a/src/reducers/workspaces.ts +++ b/src/reducers/workspaces.ts @@ -20,6 +20,7 @@ import { LOG_OUT, RESET_WORKSPACE, SEND_REPL_INPUT_TO_OUTPUT, + SET_EDITOR_SESSION_ID, TOGGLE_EDITOR_AUTORUN, UPDATE_CURRENT_ASSESSMENT_ID, UPDATE_CURRENT_SUBMISSION_ID, @@ -356,6 +357,14 @@ export const reducer: Reducer = ( ...action.payload.workspaceOptions } }; + case SET_EDITOR_SESSION_ID: + return { + ...state, + [location]: { + ...state[location], + editorSessionId: action.payload.editorSessionId + } + }; case TOGGLE_EDITOR_AUTORUN: return { ...state, From 5284c7b3d90fc1f5659d2cce053212ff9b64cf84 Mon Sep 17 00:00:00 2001 From: zephyr Date: Thu, 28 Mar 2019 14:29:41 +0800 Subject: [PATCH 03/15] Add auto-completion, and change info annotation to error --- src/components/workspace/Editor.tsx | 82 +++++++++++++++++++++++++++-- tsconfig.json | 2 +- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/components/workspace/Editor.tsx b/src/components/workspace/Editor.tsx index 2265dd451b..4902644e72 100644 --- a/src/components/workspace/Editor.tsx +++ b/src/components/workspace/Editor.tsx @@ -1,11 +1,14 @@ // tslint:disable:no-console +import * as ace from 'brace'; import * as React from 'react'; import AceEditor, { Annotation } from 'react-ace'; import { HotKeys } from 'react-hotkeys'; import sharedbAce from 'sharedb-ace'; +import 'brace/ext/language_tools'; import 'brace/ext/searchbox'; import 'brace/mode/javascript'; +import 'brace/snippets/javascript'; import 'brace/theme/cobalt'; /** @@ -24,10 +27,6 @@ export interface IEditorProps { handleUpdateHasUnsavedChanges?: (hasUnsavedChanges: boolean) => void; } -export interface IJSONData { - id: number; -} - class Editor extends React.PureComponent { private onChangeMethod: (newCode: string) => void; private onValidateMethod: (annotations: Annotation[]) => void; @@ -52,7 +51,80 @@ class Editor extends React.PureComponent { public componentDidMount() { // this editor is the same as the one in line 5 of index.js of sharedb-ace-example const editor = this.ace.current.editor; - // const session = editor.getSession(); + const session = editor.getSession(); + const sourceCompleter = { + getCompletions(editors, sessions, pos, prefix, callback) { + const wordList1 = ["function", "return", "const", "let", "display", + "null", "while", "for", "break", "continue", "if", "else", + "true", "false", "array_length"]; + const completerList1 = wordList1.map(word => { + return { + caption: word, + value: word, + meta: "Source" + }; + }); + const wordList2 = ["show", "heart_bb", "sail_bb", "blank_bb", "black_bb"]; + const completerList2 = wordList2.map(word => { + return { + caption: word, + value: word, + meta: "Rune" + }; + }); + const wordList3 = ["pair", "is_pair", "head", "tail", "is_empty_list", "is_list", + "list", "draw_list", "equal", "length", "map", "build_list", + "for_each", "list_to_string", "reverse", "append", "member", + "remove", "remove_all", "filter", "enum_list", "list_ref", + "accumulate", "set_head", "set_tail"]; + const completerList3 = wordList3.map(word => { + return { + caption: word, + value: word, + meta: "List Support" + }; + }); + const wordList4 = ["stream_tail", "is_stream", "stream", "list_to_stream", + "stream_to_list", "stream_length", "stream_map", "build_stream", + "stream_for_each", "stream_reverse", "stream_append", "stream_member", + "stream_remove", "stream_remove_all", "stream_filter", "enum_stream", + "integers_from", "eval_stream", "stream_ref"]; + const completerList4 = wordList4.map(word => { + return { + caption: word, + value: word, + meta: "Stream Support" + }; + }); + const completerList = completerList1.concat(completerList2) + .concat(completerList3).concat(completerList4); + callback(null, completerList); + } + }; + const langTools = ace.acequire("ace/ext/language_tools"); + // Alternative: + langTools.setCompleters([langTools.textCompleter, sourceCompleter]); + // langTools.addCompleter([sourceCompleter]); + // editor.completers = [sourceCompleter]; + editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true + }); + session.on('changeAnnotation', () => { + const annotations = session.getAnnotations(); + let count = 0; + for (const anno of annotations) { + if (anno.type === "info") { + anno.type = "error"; + anno.className = "ace_error"; + count++; + } + } + if (count !== 0) { + session.setAnnotations(annotations); + } + }); if (this.props.editorSessionId !== '') { console.log('Component mounted with id = ' + this.props.editorSessionId); const ShareAce = new sharedbAce(this.props.editorSessionId, { diff --git a/tsconfig.json b/tsconfig.json index aad155ae1c..738932b49f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "forceConsistentCasingInFileNames": true, "noImplicitReturns": true, "noImplicitThis": true, - "noImplicitAny": true, + "noImplicitAny": false, "strictNullChecks": true, "suppressImplicitAnyIndexErrors": true, "noUnusedLocals": true From 12a0623bb4e006de42fb347f4fc54fa289183718 Mon Sep 17 00:00:00 2001 From: Chee Yuan Date: Wed, 3 Apr 2019 02:39:28 +0800 Subject: [PATCH 04/15] Add notifications for autorun and websocket connection --- src/actions/actionTypes.ts | 1 + src/actions/workspaces.ts | 21 + src/components/Playground.tsx | 6 +- src/components/__tests__/Playground.tsx | 2 + src/components/workspace/ControlBar.tsx | 16 +- .../workspace/CustomJavaScriptMode.js | 364 ++++++++++++++++++ src/components/workspace/Editor.tsx | 152 ++++++-- .../workspace/sharedb-ace-binding.js | 243 ++++++++++++ src/components/workspace/sharedb-ace.js | 104 +++++ src/containers/PlaygroundContainer.ts | 4 + src/reducers/states.ts | 4 +- src/reducers/workspaces.ts | 11 + src/sagas/index.ts | 8 + 13 files changed, 902 insertions(+), 34 deletions(-) mode change 100644 => 100755 src/actions/actionTypes.ts mode change 100644 => 100755 src/components/__tests__/Playground.tsx create mode 100755 src/components/workspace/CustomJavaScriptMode.js mode change 100644 => 100755 src/components/workspace/Editor.tsx create mode 100755 src/components/workspace/sharedb-ace-binding.js create mode 100755 src/components/workspace/sharedb-ace.js mode change 100644 => 100755 src/containers/PlaygroundContainer.ts mode change 100644 => 100755 src/reducers/states.ts mode change 100644 => 100755 src/reducers/workspaces.ts diff --git a/src/actions/actionTypes.ts b/src/actions/actionTypes.ts old mode 100644 new mode 100755 index e7d4fdbc04..0311f2c311 --- a/src/actions/actionTypes.ts +++ b/src/actions/actionTypes.ts @@ -40,6 +40,7 @@ export const PLAYGROUND_EXTERNAL_SELECT = 'PLAYGROUND_EXTERNAL_SELECT '; export const RESET_WORKSPACE = 'RESET_WORKSPACE'; export const SEND_REPL_INPUT_TO_OUTPUT = 'SEND_REPL_INPUT_TO_OUTPUT'; export const SET_EDITOR_SESSION_ID = 'SET_EDITOR_SESSION_ID'; +export const SET_WEBSOCKET_STATUS = 'SET_WEBSOCKET_STATUS'; export const TOGGLE_EDITOR_AUTORUN = 'TOGGLE_EDITOR_AUTORUN'; export const UPDATE_CURRENT_ASSESSMENT_ID = 'UPDATE_CURRENT_ASSESSMENT_ID'; export const UPDATE_CURRENT_SUBMISSION_ID = 'UPDATE_CURRENT_SUBMISSION_ID'; diff --git a/src/actions/workspaces.ts b/src/actions/workspaces.ts index 335b8aae78..85f1dde662 100755 --- a/src/actions/workspaces.ts +++ b/src/actions/workspaces.ts @@ -1,3 +1,4 @@ +// tslint:disable:no-console import { ActionCreator } from 'redux'; import { ExternalLibraryName, Library } from '../components/assessment/assessmentShape'; @@ -220,6 +221,26 @@ export const setEditorSessionId: ActionCreator = ( } }); +/** + * Sets sharedb websocket status. + * + * @param workspaceLocation the workspace to be reset + * @param websocketStatus 0: CLOSED 1: OPEN + */ +export const setWebsocketStatus: ActionCreator = ( + workspaceLocation: WorkspaceLocations, + websocketStatus: number +) => { + console.log('In workspace action, WS status: ' + websocketStatus); + return { + type: actionTypes.SET_WEBSOCKET_STATUS, + payload: { + workspaceLocation, + websocketStatus + } + }; +}; + export const updateCurrentAssessmentId = (assessmentId: number, questionId: number) => ({ type: actionTypes.UPDATE_CURRENT_ASSESSMENT_ID, payload: { diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index a7350800cf..2c4e2bc603 100755 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -44,6 +44,7 @@ export interface IStateProps { replValue: string; sideContentHeight?: number; sourceChapter: number; + websocketStatus: number; externalLibraryName: string; } @@ -62,6 +63,7 @@ export interface IDispatchProps { handleReplOutputClear: () => void; handleReplValueChange: (newValue: string) => void; handleSetEditorSessionId: (editorSessionId: string) => void; + handleSetWebsocketStatus: (websocketStatus: number) => void; handleSideContentHeightChange: (heightChange: number) => void; handleToggleEditorAutorun: () => void; } @@ -107,13 +109,15 @@ class Playground extends React.Component { isRunning: this.props.isRunning, queryString: this.props.queryString, questionProgress: null, - sourceChapter: this.props.sourceChapter + sourceChapter: this.props.sourceChapter, + websocketStatus: this.props.websocketStatus }, editorProps: { editorValue: this.props.editorValue, editorSessionId: this.props.editorSessionId, handleEditorEval: this.props.handleEditorEval, handleEditorValueChange: this.props.handleEditorValueChange, + handleSetWebsocketStatus: this.props.handleSetWebsocketStatus, isEditorAutorun: this.props.isEditorAutorun }, editorWidth: this.props.editorWidth, diff --git a/src/components/__tests__/Playground.tsx b/src/components/__tests__/Playground.tsx old mode 100644 new mode 100755 index e991c26e2c..45b2989557 --- a/src/components/__tests__/Playground.tsx +++ b/src/components/__tests__/Playground.tsx @@ -17,6 +17,7 @@ const baseProps = { externalLibraryName: ExternalLibraryNames.NONE, output: [], replValue: '', + websocketStatus: 0, handleBrowseHistoryDown: () => {}, handleBrowseHistoryUp: () => {}, handleChangeActiveTab: (n: number) => {}, @@ -31,6 +32,7 @@ const baseProps = { handleReplOutputClear: () => {}, handleReplValueChange: (code: string) => {}, handleSetEditorSessionId: (editorSessionId: string) => {}, + handleSetWebsocketStatus: (websocketStatus: number) => {}, handleSideContentHeightChange: (h: number) => {}, handleToggleEditorAutorun: () => {} }; diff --git a/src/components/workspace/ControlBar.tsx b/src/components/workspace/ControlBar.tsx index 31068d1e02..f286ca29f9 100755 --- a/src/components/workspace/ControlBar.tsx +++ b/src/components/workspace/ControlBar.tsx @@ -1,4 +1,5 @@ -import { Button, Intent, MenuItem, Popover, Text, Tooltip } from '@blueprintjs/core'; +// tslint:disable:no-console +import { Button, Classes, Intent, MenuItem, Popover, Text, Tooltip } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { ItemRenderer, Select } from '@blueprintjs/select'; import * as React from 'react'; @@ -37,6 +38,7 @@ export type ControlBarProps = { hasUnsavedChanges?: boolean; isEditorAutorun?: boolean; isRunning: boolean; + websocketStatus?: number; onClickNext?(): any; onClickPrevious?(): any; onClickReturn?(): any; @@ -173,7 +175,9 @@ class ControlBar extends React.PureComponent { <>
- {controlButton('', IconNames.KEY_ENTER, null, { type: 'submit' })} + + {controlButton('', IconNames.KEY_ENTER, null, { type: 'submit' })} +
@@ -193,13 +197,19 @@ class ControlBar extends React.PureComponent { const stopAutorunButton = this.props.hasEditorAutorunButton ? controlButton('Autorun', IconNames.STOP, this.props.handleToggleEditorAutorun) : undefined; + const indicatorButton = controlButton( + this.props.websocketStatus === 1 ? 'Connected' : 'Disconnected', + IconNames.AIRPLANE, + null + ); + console.log('Controbar rendering, ws status: ' + this.props.websocketStatus); return (
{this.props.isEditorAutorun ? undefined : this.props.isRunning ? stopButton : runButton} {saveButton} {shareButton} {chapterSelectButton} {externalSelectButton} {this.props.isEditorAutorun ? stopAutorunButton : startAutorunButton} - {inviteButton} {joinButton} + {inviteButton} {joinButton} {indicatorButton}
); } diff --git a/src/components/workspace/CustomJavaScriptMode.js b/src/components/workspace/CustomJavaScriptMode.js new file mode 100755 index 0000000000..cf4e2669cb --- /dev/null +++ b/src/components/workspace/CustomJavaScriptMode.js @@ -0,0 +1,364 @@ +/* tslint:disable */ + +import "brace/mode/javascript"; + +export class CustomHighlightRules extends window.ace.acequire( + "ace/mode/text_highlight_rules" +).TextHighlightRules { + + + constructor() { + super(); + // see: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects + // TODO: Unicode escape sequences + var DocCommentHighlightRules = window.ace.acequire("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules; + var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*"; + function comments(next) { + return [ + { + token : "comment", // multi line comment + regex : /\/\*/, + next: [ + DocCommentHighlightRules.getTagRule(), + {token : "comment", regex : "\\*\\/", next : next || "pop"}, + {defaultToken : "comment", caseInsensitive: true} + ] + }, { + token : "comment", + regex : "\\/\\/", + next: [ + DocCommentHighlightRules.getTagRule(), + {token : "comment", regex : "$|^", next : next || "pop"}, + {defaultToken : "comment", caseInsensitive: true} + ] + } + ]; + } + var keywordMapper = this.createKeywordMapper({ + "variable.language": + "Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|" + // Constructors + "Namespace|QName|XML|XMLList|" + // E4X + "ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|" + + "Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|" + + "Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|" + // Errors + "SyntaxError|TypeError|URIError|" + + "decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|" + // Non-constructor functions + "isNaN|parseFloat|parseInt|" + + "JSON|Math|" + // Other + "this|arguments|prototype|window|document" , // Pseudo + "keyword": + "const|yield|import|get|set|async|await|" + + "break|case|catch|continue|default|delete|do|else|finally|for|function|" + + "if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|" + + // invalid or reserved + "__parent__|__count__|escape|unescape|with|__proto__|" + + "class|enum|extends|super|export|implements|private|public|interface|package|protected|static", + "storage.type": + "const|let|var|function", + "constant.language": + "null|Infinity|NaN|undefined", + "support.function": + "alert", + "constant.language.boolean": "true|false" + }, "identifier"); + + // keywords which can be followed by regular expressions + var kwBeforeRe = "case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void"; + + var escapedRe = "\\\\(?:x[0-9a-fA-F]{2}|" + // hex + "u[0-9a-fA-F]{4}|" + // unicode + "u{[0-9a-fA-F]{1,6}}|" + // es6 unicode + "[0-2][0-7]{0,2}|" + // oct + "3[0-7][0-7]?|" + // oct + "[4-7][0-7]?|" + //oct + ".)"; + // regexp must not have capturing parentheses. Use (?:) instead. + // regexps are ordered -> the first match is used + + this.$rules = { + "no_regex" : [ + DocCommentHighlightRules.getStartRule("doc-start"), + comments("no_regex"), + { + token : "string", + regex : "'(?=.)", + next : "qstring" + }, { + token : "string", + regex : '"(?=.)', + next : "qqstring" + }, { + token : "constant.numeric", // hexadecimal, octal and binary + regex : /0(?:[xX][0-9a-fA-F]+|[oO][0-7]+|[bB][01]+)\b/ + }, { + token : "constant.numeric", // decimal integers and floats + regex : /(?:\d\d*(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+\b)?/ + }, { + // Sound.prototype.play = + token : [ + "storage.type", "punctuation.operator", "support.function", + "punctuation.operator", "entity.name.function", "text","keyword.operator" + ], + regex : "(" + identifierRe + ")(\\.)(prototype)(\\.)(" + identifierRe +")(\\s*)(=)", + next: "function_arguments" + }, { + // Sound.play = function() { } + token : [ + "storage.type", "punctuation.operator", "entity.name.function", "text", + "keyword.operator", "text", "storage.type", "text", "paren.lparen" + ], + regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()", + next: "function_arguments" + }, { + // play = function() { } + token : [ + "entity.name.function", "text", "keyword.operator", "text", "storage.type", + "text", "paren.lparen" + ], + regex : "(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()", + next: "function_arguments" + }, { + // Sound.play = function play() { } + token : [ + "storage.type", "punctuation.operator", "entity.name.function", "text", + "keyword.operator", "text", + "storage.type", "text", "entity.name.function", "text", "paren.lparen" + ], + regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()", + next: "function_arguments" + }, { + // function myFunc(arg) { } + token : [ + "storage.type", "text", "entity.name.function", "text", "paren.lparen" + ], + regex : "(function)(\\s+)(" + identifierRe + ")(\\s*)(\\()", + next: "function_arguments" + }, { + // foobar: function() { } + token : [ + "entity.name.function", "text", "punctuation.operator", + "text", "storage.type", "text", "paren.lparen" + ], + regex : "(" + identifierRe + ")(\\s*)(:)(\\s*)(function)(\\s*)(\\()", + next: "function_arguments" + }, { + // : function() { } (this is for issues with 'foo': function() { }) + token : [ + "text", "text", "storage.type", "text", "paren.lparen" + ], + regex : "(:)(\\s*)(function)(\\s*)(\\()", + next: "function_arguments" + }, { + // from "module-path" (this is the only case where 'from' should be a keyword) + token : "keyword", + regex : "from(?=\\s*('|\"))" + }, { + token : "keyword", + regex : "(?:" + kwBeforeRe + ")\\b", + next : "start" + }, { + token : ["support.constant"], + regex : /that\b/ + }, { + token : ["storage.type", "punctuation.operator", "support.function.firebug"], + regex : /(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/ + }, { + token : keywordMapper, + regex : identifierRe + }, { + token : "punctuation.operator", + regex : /[.](?![.])/, + next : "property" + }, { + token : "storage.type", + regex : /=>/, + next : "start" + }, { + token : "keyword.operator", + regex : /--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/, + next : "start" + }, { + token : "punctuation.operator", + regex : /[?:,;.]/, + next : "start" + }, { + token : "paren.lparen", + regex : /[\[({]/, + next : "start" + }, { + token : "paren.rparen", + regex : /[\])}]/ + }, { + token: "comment", + regex: "(//).*$" + } + ], + property: [{ + token : "text", + regex : "\\s+" + }, { + // Sound.play = function play() { } + token : [ + "storage.type", "punctuation.operator", "entity.name.function", "text", + "keyword.operator", "text", + "storage.type", "text", "entity.name.function", "text", "paren.lparen" + ], + regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()", + next: "function_arguments" + }, { + token : "punctuation.operator", + regex : /[.](?![.])/ + }, { + token : "support.function", + regex : /(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/ + }, { + token : "support.function.dom", + regex : /(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/ + }, { + token : "support.constant", + regex : /(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/ + }, { + token : "identifier", + regex : identifierRe + }, { + regex: "", + token: "empty", + next: "no_regex" + } + ], + // regular expressions are only allowed after certain tokens. This + // makes sure we don't mix up regexps with the divison operator + "start": [ + DocCommentHighlightRules.getStartRule("doc-start"), + comments("start"), + { + token: "string.regexp", + regex: "\\/", + next: "regex" + }, { + token : "text", + regex : "\\s+|^$", + next : "start" + }, { + // immediately return to the start mode without matching + // anything + token: "empty", + regex: "", + next: "no_regex" + } + ], + "regex": [ + { + // escapes + token: "regexp.keyword.operator", + regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)" + }, { + // flag + token: "string.regexp", + regex: "/[sxngimy]*", + next: "no_regex" + }, { + // invalid operators + token : "invalid", + regex: /\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/ + }, { + // operators + token : "constant.language.escape", + regex: /\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/ + }, { + token : "constant.language.delimiter", + regex: /\|/ + }, { + token: "constant.language.escape", + regex: /\[\^?/, + next: "regex_character_class" + }, { + token: "empty", + regex: "$", + next: "no_regex" + }, { + defaultToken: "string.regexp" + } + ], + "regex_character_class": [ + { + token: "regexp.charclass.keyword.operator", + regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)" + }, { + token: "constant.language.escape", + regex: "]", + next: "regex" + }, { + token: "constant.language.escape", + regex: "-" + }, { + token: "empty", + regex: "$", + next: "no_regex" + }, { + defaultToken: "string.regexp.charachterclass" + } + ], + "function_arguments": [ + { + token: "variable.parameter", + regex: identifierRe + }, { + token: "punctuation.operator", + regex: "[, ]+" + }, { + token: "punctuation.operator", + regex: "$" + }, { + token: "empty", + regex: "", + next: "no_regex" + } + ], + "qqstring" : [ + { + token : "constant.language.escape", + regex : escapedRe + }, { + token : "string", + regex : "\\\\$", + consumeLineEnd : true + }, { + token : "string", + regex : '"|$', + next : "no_regex" + }, { + defaultToken: "string" + } + ], + "qstring" : [ + { + token : "constant.language.escape", + regex : escapedRe + }, { + token : "string", + regex : "\\\\$", + consumeLineEnd : true + }, { + token : "string", + regex : "'|$", + next : "no_regex" + }, { + defaultToken: "string" + } + ] + }; + this.embedRules(DocCommentHighlightRules, "doc-", + [ DocCommentHighlightRules.getEndRule("no_regex") ]); + + this.normalizeRules(); + } +} + +export default class CustomJavaScriptMode extends window.ace.acequire("ace/mode/text") + .Mode { + constructor() { + super(); + this.HighlightRules = CustomHighlightRules; + } +} \ No newline at end of file diff --git a/src/components/workspace/Editor.tsx b/src/components/workspace/Editor.tsx old mode 100644 new mode 100755 index 4902644e72..91e2d6f7ab --- a/src/components/workspace/Editor.tsx +++ b/src/components/workspace/Editor.tsx @@ -1,9 +1,9 @@ -// tslint:disable:no-console +/* tslint:disable */ import * as ace from 'brace'; import * as React from 'react'; import AceEditor, { Annotation } from 'react-ace'; import { HotKeys } from 'react-hotkeys'; -import sharedbAce from 'sharedb-ace'; +import sharedbAce from './sharedb-ace'; import 'brace/ext/language_tools'; import 'brace/ext/searchbox'; @@ -24,6 +24,7 @@ export interface IEditorProps { editorValue: string; handleEditorEval: () => void; handleEditorValueChange: (newCode: string) => void; + handleSetWebsocketStatus?: (websocketStatus: number) => void; handleUpdateHasUnsavedChanges?: (hasUnsavedChanges: boolean) => void; } @@ -31,10 +32,12 @@ class Editor extends React.PureComponent { private onChangeMethod: (newCode: string) => void; private onValidateMethod: (annotations: Annotation[]) => void; private ace: any; + private ShareAce: any; constructor(props: IEditorProps) { super(props); this.ace = React.createRef(); + this.ShareAce = null; this.onChangeMethod = (newCode: string) => { if (this.props.handleUpdateHasUnsavedChanges) { this.props.handleUpdateHasUnsavedChanges(true); @@ -54,54 +57,108 @@ class Editor extends React.PureComponent { const session = editor.getSession(); const sourceCompleter = { getCompletions(editors, sessions, pos, prefix, callback) { - const wordList1 = ["function", "return", "const", "let", "display", - "null", "while", "for", "break", "continue", "if", "else", - "true", "false", "array_length"]; + const wordList1 = [ + 'function', + 'return', + 'const', + 'let', + 'display', + 'null', + 'while', + 'for', + 'break', + 'continue', + 'if', + 'else', + 'true', + 'false', + 'array_length' + ]; const completerList1 = wordList1.map(word => { return { caption: word, value: word, - meta: "Source" + meta: 'Source' }; }); - const wordList2 = ["show", "heart_bb", "sail_bb", "blank_bb", "black_bb"]; + const wordList2 = ['show', 'heart_bb', 'sail_bb', 'blank_bb', 'black_bb']; const completerList2 = wordList2.map(word => { return { caption: word, value: word, - meta: "Rune" + meta: 'Rune' }; }); - const wordList3 = ["pair", "is_pair", "head", "tail", "is_empty_list", "is_list", - "list", "draw_list", "equal", "length", "map", "build_list", - "for_each", "list_to_string", "reverse", "append", "member", - "remove", "remove_all", "filter", "enum_list", "list_ref", - "accumulate", "set_head", "set_tail"]; + const wordList3 = [ + 'pair', + 'is_pair', + 'head', + 'tail', + 'is_empty_list', + 'is_list', + 'list', + 'draw_list', + 'equal', + 'length', + 'map', + 'build_list', + 'for_each', + 'list_to_string', + 'reverse', + 'append', + 'member', + 'remove', + 'remove_all', + 'filter', + 'enum_list', + 'list_ref', + 'accumulate', + 'set_head', + 'set_tail' + ]; const completerList3 = wordList3.map(word => { return { caption: word, value: word, - meta: "List Support" + meta: 'List Support' }; }); - const wordList4 = ["stream_tail", "is_stream", "stream", "list_to_stream", - "stream_to_list", "stream_length", "stream_map", "build_stream", - "stream_for_each", "stream_reverse", "stream_append", "stream_member", - "stream_remove", "stream_remove_all", "stream_filter", "enum_stream", - "integers_from", "eval_stream", "stream_ref"]; + const wordList4 = [ + 'stream_tail', + 'is_stream', + 'stream', + 'list_to_stream', + 'stream_to_list', + 'stream_length', + 'stream_map', + 'build_stream', + 'stream_for_each', + 'stream_reverse', + 'stream_append', + 'stream_member', + 'stream_remove', + 'stream_remove_all', + 'stream_filter', + 'enum_stream', + 'integers_from', + 'eval_stream', + 'stream_ref' + ]; const completerList4 = wordList4.map(word => { return { caption: word, value: word, - meta: "Stream Support" + meta: 'Stream Support' }; }); - const completerList = completerList1.concat(completerList2) - .concat(completerList3).concat(completerList4); + const completerList = completerList1 + .concat(completerList2) + .concat(completerList3) + .concat(completerList4); callback(null, completerList); } }; - const langTools = ace.acequire("ace/ext/language_tools"); + const langTools = ace.acequire('ace/ext/language_tools'); // Alternative: langTools.setCompleters([langTools.textCompleter, sourceCompleter]); // langTools.addCompleter([sourceCompleter]); @@ -115,9 +172,9 @@ class Editor extends React.PureComponent { const annotations = session.getAnnotations(); let count = 0; for (const anno of annotations) { - if (anno.type === "info") { - anno.type = "error"; - anno.className = "ace_error"; + if (anno.type === 'info') { + anno.type = 'error'; + anno.className = 'ace_error'; count++; } } @@ -127,12 +184,23 @@ class Editor extends React.PureComponent { }); if (this.props.editorSessionId !== '') { console.log('Component mounted with id = ' + this.props.editorSessionId); - const ShareAce = new sharedbAce(this.props.editorSessionId, { + const xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = () => { + if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { + const state = JSON.parse(xmlhttp.responseText).state; + console.log(state ? 'Valid Id' : 'Invalid Id'); + } + }; + xmlhttp.open('GET', 'https://13.250.109.61/gists/' + this.props.editorSessionId, true); + xmlhttp.send(); + + const ShareAce = new sharedbAce(this.props.editorSessionId!, { WsUrl: 'wss://13.250.109.61/ws', - pluginWsUrl: 'ws://localhost:3108/ws', + pluginWsUrl: null, namespace: 'codepad' }); - ShareAce.on('ready', () => { + this.ShareAce = ShareAce; + (ShareAce as any).on('ready', () => { ShareAce.add( editor, ['code'], @@ -142,6 +210,32 @@ class Editor extends React.PureComponent { ] ); }); + ShareAce.WS.onopen = (event: any) => this.props.handleSetWebsocketStatus!(1); + ShareAce.WS.onclose = (event: any) => this.props.handleSetWebsocketStatus!(0); + // const checkAlive = () => { + // setTimeout(() => { + // const xmlhttp = new XMLHttpRequest(); + // xmlhttp.onreadystatechange = () => { + // if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { + // console.log("Server alive"); + // } else if(xmlhttp.readyState === 4 && xmlhttp.status !== 200) { + // console.log("Error connecting to server"); + // } + // }; + // xmlhttp.open('GET', 'https://13.250.109.61/ping/', true); + // xmlhttp.send(); + // if(ShareAce.WS.readyState === 1 && this.props.editorSessionId !== '') { + // checkAlive(); + // } + // }, 5000); + // }; + // checkAlive(); + } + } + + public componentWillUnmount() { + if (this.ShareAce !== null) { + this.ShareAce.WS.close(); } } diff --git a/src/components/workspace/sharedb-ace-binding.js b/src/components/workspace/sharedb-ace-binding.js new file mode 100755 index 0000000000..5325b650a6 --- /dev/null +++ b/src/components/workspace/sharedb-ace-binding.js @@ -0,0 +1,243 @@ +/* tslint:disable */ + +/** + * @fileOverview + * @name sharedb-ace-binding.js + * @author Jethro Kuan + * @license MIT + */ + +/* global process.env */ +import Logdown from 'logdown'; + +class SharedbAceBinding { + /** + * Constructs the binding object. + * + * Initializes the Ace document initial value to that of the + * ShareDB document. Also , sets up the local and remote event + * listeners, and begins listening to local and remote change events + * + * @param {Object} options - contains all parameters + * @param {Object} options.ace - ace editor instance + * @param {Object} options.doc - ShareDB document + * @param {Object} options.pluginWS - WebSocket connection for + * sharedb-ace plugins + * @param {string[]} options.path - A lens, describing the nesting + * to the JSON document. It should point to a string. + * @param {Object[]} options.plugins - array of sharedb-ace plugins + * @example + * const binding = new SharedbAceBinding({ + * ace: aceInstance, + * doc: sharedbDoc, + * path: ["path"], + * plugins: [ SharedbAceMultipleCursors ], + * pluginWS: "http://localhost:3108/ws", + * }) + */ + constructor(options) { + this.editor = options.ace; + this.editor.id = `${options.id}-${options.path}`; + this.editor.$blockScrolling = Infinity; + this.session = this.editor.getSession(); + this.newline = this.session.getDocument().getNewLineCharacter(); + this.path = options.path; + this.doc = options.doc; + this.pluginWS = options.pluginWS; + this.plugins = options.plugins || []; + this.logger = new Logdown({ prefix: 'shareace' }); + + if (process.env.NODE_ENV === 'production') { + Logdown.disable('*'); + } + + // Initialize plugins + this.plugins.forEach((plugin) => { + plugin(this.pluginWS, this.editor); + }); + + // When ops are applied to sharedb, ace emits edit events. + // This events need to be suppressed to prevent infinite looping + this.suppress = false; + + // Set value of ace document to ShareDB document value + this.setInitialValue(); + + // Event Listeners + this.$onLocalChange = this.onLocalChange.bind(this); + this.$onRemoteChange = this.onRemoteChange.bind(this); + + this.listen(); + } + + /** + * Sets the ace document value to the ShareDB document value + */ + setInitialValue() { + this.suppress = true; + // TODO: fix this + // This doesn't work for nested JSON sharedb documents > 1 + this.session.setValue(this.doc.data[this.path[0]]); + this.suppress = false; + } + + /** + * Listens to the changes + */ + listen() { + this.session.on('change', this.$onLocalChange); + this.doc.on('op', this.$onRemoteChange); + } + + /** + * Stop listening to changes + */ + unlisten() { + this.session.removeListener('change', this.$onLocalChange); + this.doc.on('op', this.$onRemoteChange); + } + + /** + * Delta (Ace Editor) -> Op (ShareDB) + * + * @param {Object} delta - delta created by ace editor + * @returns {Object} op - op compliant with ShareDB + * @throws {Error} throws error if delta is malformed + */ + deltaTransform(delta) { + const aceDoc = this.session.getDocument(); + const op = {}; + const start = aceDoc.positionToIndex(delta.start); + const end = aceDoc.positionToIndex(delta.end); + op.p = this.path.concat(start); + this.logger.log(`start: ${start} end: ${end}`); + let action; + if (delta.action === 'insert') { + action = 'si'; + } else if (delta.action === 'remove') { + action = 'sd'; + } else { + throw new Error(`action ${action} not supported`); + } + + const str = delta.lines.join('\n'); + + op[action] = str; + return op; + } + + /** + * + * @param {Object[]} ops - array of ShareDB ops + * @returns {Object[]} deltas - array of Ace Editor compliant deltas + * @throws {Error} throws error on malformed op + */ + opTransform(ops) { + const self = this; + function opToDelta(op) { + const index = op.p[op.p.length - 1]; + const pos = self.session.doc.indexToPosition(index, 0); + const start = pos; + let action; + let lines; + let end; + + if ('sd' in op) { + action = 'remove'; + lines = op.sd.split('\n'); + const count = lines.reduce((total, line) => total + line.length, lines.length - 1); + end = self.session.doc.indexToPosition(index + count, 0); + } else if ('si' in op) { + action = 'insert'; + lines = op.si.split('\n'); + if (lines.length === 1) { + end = { + row: start.row, + column: start.column + op.si.length, + }; + } else { + end = { + row: start.row + (lines.length - 1), + column: lines[lines.length - 1].length, + }; + } + } else { + throw new Error(`Invalid Operation: ${JSON.stringify(op)}`); + } + + const delta = { + start, + end, + action, + lines, + }; + + return delta; + } + const deltas = ops.map(opToDelta); + return deltas; + } + + /** + * Event listener for local changes (ace editor) + * + * transforms delta into ShareDB op and sends it to the server. + * + * @param {} delta - ace editor op (compliant with + * ace editor event listener spec) + */ + onLocalChange(delta) { + this.logger.log(`*local*: fired ${Date.now()}`); + this.logger.log(`*local*: delta received: ${JSON.stringify(delta)}`); + + if (this.suppress) { + this.logger.log('*local*: local delta, _skipping_'); + return; + } + const op = this.deltaTransform(delta); + this.logger.log(`*local*: transformed op: ${JSON.stringify(op)}`); + + const docSubmitted = (err) => { + if (err) throw err; + this.logger.log('*local*: op submitted'); + }; + + this.doc.submitOp(op, { source: this }, docSubmitted); + } + + /** + * Event Listener for remote events (ShareDB) + * + * @param {Object[]} ops - array of ShareDB ops + * @param {Object} source - which sharedb-ace-binding instance + * created the op. If self, don't apply the op. + */ + onRemoteChange(ops, source) { + this.logger.log(`*remote*: fired ${Date.now()}`); + const self = this; + + const opsPath = ops[0].p.slice(0, ops[0].p.length - 1).toString(); + this.logger.log(opsPath); + if (source === self) { + this.logger.log('*remote*: op origin is self; _skipping_'); + return; + } else if (opsPath !== this.path.toString()) { + this.logger.log('*remote*: not from my path; _skipping_'); + return; + } + + const deltas = this.opTransform(ops); + this.logger.log(`*remote*: op received: ${JSON.stringify(ops)}`); + this.logger.log(`*remote*: transformed delta: ${JSON.stringify(deltas)}`); + + self.suppress = true; + self.session.getDocument().applyDeltas(deltas); + self.suppress = false; + + this.logger.log('*remote*: session value'); + this.logger.log(JSON.stringify(this.session.getValue())); + this.logger.log('*remote*: delta applied'); + } +} + +export default SharedbAceBinding; diff --git a/src/components/workspace/sharedb-ace.js b/src/components/workspace/sharedb-ace.js new file mode 100755 index 0000000000..c41426dc33 --- /dev/null +++ b/src/components/workspace/sharedb-ace.js @@ -0,0 +1,104 @@ +/* tslint:disable */ + +/** + * @fileOverview + * @name sharedb-ace.js + * @author Jethro Kuan + * @license MIT + */ + +import WebSocket from 'reconnecting-websocket'; +import EventEmitter from 'event-emitter-es6'; +import sharedb from 'sharedb/lib/client'; +import SharedbAceBinding from './sharedb-ace-binding'; + +function IllegalArgumentException(message) { + this.message = message; + this.name = 'IllegalArgumentException'; +} + +class SharedbAce extends EventEmitter { + /** + * creating an instance connects to sharedb via websockets + * and initializes the document with no connections + * + * Assumes that the document is already initialized + * + * The "ready" event is fired once the ShareDB document has been initialized + * + * @param {string} id - id of the ShareDB document + * @param {Object} options - options object containing various + * required configurations + * @param {string} options.namespace - namespace of document within + * ShareDB, to be equal to that on the server + * @param {string} options.WsUrl - Websocket URL for ShareDB + * @param {string | null} options.pluginWsUrl - Websocket URL for extra plugins + * (different port from options.WsUrl) + */ + constructor(id, options) { + super(); + this.id = id; + if (options.pluginWsUrl !== null) { + this.pluginWS = new WebSocket(options.pluginWsUrl); + } + + if (options.WsUrl === null) { + throw new IllegalArgumentException('wsUrl not provided.'); + } + + this.WS = new WebSocket(options.WsUrl); + + const connection = new sharedb.Connection(this.WS); + if (options.namespace === null) { + throw new IllegalArgumentException('namespace not provided.'); + } + const namespace = options.namespace; + const doc = connection.get(namespace, id); + + // Fetches once from the server, and fires events + // on subsequent document changes + + const docSubscribed = (err) => { + if (err) throw err; + + if (doc.type === null) { + throw new Error('ShareDB document uninitialized. Please check if you' + + ' have the correct id or that you have initialized ' + + 'the document in the server.'); + } + + this.emit('ready'); + }; + + doc.subscribe(docSubscribed.bind(doc)); + + this.doc = doc; + this.connections = {}; + } + + /** + * Creates a two-way binding between the ace instance and the document + * + * adds the binding to the instance's "connections" property + * + * @param {Object} ace - ace editor instance + * @param {string[]} path - A lens, describing the nesting to the JSON document. + * It should point to a string. + * @param {Object[]} plugins - list of plugins to add to this particular + * ace instance + */ + add(ace, path, plugins) { + const sharePath = path || []; + const binding = new SharedbAceBinding({ + ace, + doc: this.doc, + path: sharePath, + pluginWS: this.pluginWS, + id: this.id, + plugins, + }); + this.connections[path.join('-')] = binding; + } +} + +export default SharedbAce; diff --git a/src/containers/PlaygroundContainer.ts b/src/containers/PlaygroundContainer.ts old mode 100644 new mode 100755 index fd58ac8dff..c56c51e5e9 --- a/src/containers/PlaygroundContainer.ts +++ b/src/containers/PlaygroundContainer.ts @@ -16,6 +16,7 @@ import { generateLzString, playgroundExternalSelect, setEditorSessionId, + setWebsocketStatus, toggleEditorAutorun, updateEditorValue, updateReplValue, @@ -37,6 +38,7 @@ const mapStateToProps: MapStateToProps = state => ({ replValue: state.workspaces.playground.replValue, sideContentHeight: state.workspaces.playground.sideContentHeight, sourceChapter: state.workspaces.playground.context.chapter, + websocketStatus: state.workspaces.playground.websocketStatus, externalLibraryName: state.workspaces.playground.playgroundExternal }); @@ -61,6 +63,8 @@ const mapDispatchToProps: MapDispatchToProps = (dispatch: Di handleReplValueChange: (newValue: string) => updateReplValue(newValue, location), handleSetEditorSessionId: (editorSessionId: string) => setEditorSessionId(location, editorSessionId), + handleSetWebsocketStatus: (websocketStatus: number) => + setWebsocketStatus(location, websocketStatus), handleSideContentHeightChange: (heightChange: number) => changeSideContentHeight(heightChange, location), handleToggleEditorAutorun: () => toggleEditorAutorun(location) diff --git a/src/reducers/states.ts b/src/reducers/states.ts old mode 100644 new mode 100755 index 517639b3d9..0aa5165b24 --- a/src/reducers/states.ts +++ b/src/reducers/states.ts @@ -48,6 +48,7 @@ interface IGradingWorkspace extends IWorkspaceState { export interface IPlaygroundWorkspace extends IWorkspaceState { readonly playgroundExternal: ExternalLibraryName; + readonly websocketStatus: number; } export interface IWorkspaceManagerState { @@ -230,7 +231,8 @@ export const defaultWorkspaceManager: IWorkspaceManagerState = { }, playground: { ...createDefaultWorkspace(WorkspaceLocations.playground), - playgroundExternal: ExternalLibraryNames.NONE + playgroundExternal: ExternalLibraryNames.NONE, + websocketStatus: 0 } }; diff --git a/src/reducers/workspaces.ts b/src/reducers/workspaces.ts old mode 100644 new mode 100755 index e1200a8c28..5d0d69b1a8 --- a/src/reducers/workspaces.ts +++ b/src/reducers/workspaces.ts @@ -1,3 +1,4 @@ +// tslint:disable:no-console import { Reducer } from 'redux'; import { @@ -21,6 +22,7 @@ import { RESET_WORKSPACE, SEND_REPL_INPUT_TO_OUTPUT, SET_EDITOR_SESSION_ID, + SET_WEBSOCKET_STATUS, TOGGLE_EDITOR_AUTORUN, UPDATE_CURRENT_ASSESSMENT_ID, UPDATE_CURRENT_SUBMISSION_ID, @@ -365,6 +367,15 @@ export const reducer: Reducer = ( editorSessionId: action.payload.editorSessionId } }; + case SET_WEBSOCKET_STATUS: + console.log('In reducer: ' + action.payload.websocketStatus); + return { + ...state, + [location]: { + ...state[location], + websocketStatus: action.payload.websocketStatus + } + }; case TOGGLE_EDITOR_AUTORUN: return { ...state, diff --git a/src/sagas/index.ts b/src/sagas/index.ts index 779321f5b1..303027a506 100644 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -59,6 +59,14 @@ function* workspaceSaga(): SagaIterator { yield* evalCode(code, context, location); }); + yield takeEvery(actionTypes.TOGGLE_EDITOR_AUTORUN, function*(action) { + const location = (action as actionTypes.IAction).payload.workspaceLocation; + const isEditorAutorun = yield select( + (state: IState) => (state.workspaces[location] as IWorkspaceState).isEditorAutorun + ); + yield call(showWarningMessage, 'Autorun ' + (isEditorAutorun ? 'Started' : 'Stopped'), 750); + }); + yield takeEvery(actionTypes.EVAL_REPL, function*(action) { const location = (action as actionTypes.IAction).payload.workspaceLocation; const code: string = yield select( From 5c2106659e7445e930fb015cc991932529b70d55 Mon Sep 17 00:00:00 2001 From: Chee Yuan Date: Thu, 4 Apr 2019 01:26:54 +0800 Subject: [PATCH 05/15] Allow detection on invalid session id --- src/actions/actionTypes.ts | 1 + src/actions/workspaces.ts | 4 ++++ src/components/Playground.tsx | 2 ++ src/components/__tests__/Playground.tsx | 1 + src/components/workspace/ControlBar.tsx | 22 +++++++++++++++++++++- src/components/workspace/Editor.tsx | 9 --------- src/containers/PlaygroundContainer.ts | 2 ++ src/sagas/index.ts | 4 ++++ 8 files changed, 35 insertions(+), 10 deletions(-) mode change 100644 => 100755 src/sagas/index.ts diff --git a/src/actions/actionTypes.ts b/src/actions/actionTypes.ts index 0311f2c311..d865ace327 100755 --- a/src/actions/actionTypes.ts +++ b/src/actions/actionTypes.ts @@ -36,6 +36,7 @@ export const END_CLEAR_CONTEXT = 'END_CLEAR_CONTEXT'; export const ENSURE_LIBRARIES_LOADED = 'ENSURE_LIBRARIES_LOADED'; export const EVAL_EDITOR = 'EVAL_EDITOR'; export const EVAL_REPL = 'EVAL_REPL'; +export const INVALID_EDITOR_SESSION_ID = 'INVALID_EDITOR_SESSION_ID'; export const PLAYGROUND_EXTERNAL_SELECT = 'PLAYGROUND_EXTERNAL_SELECT '; export const RESET_WORKSPACE = 'RESET_WORKSPACE'; export const SEND_REPL_INPUT_TO_OUTPUT = 'SEND_REPL_INPUT_TO_OUTPUT'; diff --git a/src/actions/workspaces.ts b/src/actions/workspaces.ts index 85f1dde662..39013fd4b8 100755 --- a/src/actions/workspaces.ts +++ b/src/actions/workspaces.ts @@ -162,6 +162,10 @@ export const evalRepl = (workspaceLocation: WorkspaceLocation) => ({ payload: { workspaceLocation } }); +export const invalidEditorSessionId = () => ({ + type: actionTypes.INVALID_EDITOR_SESSION_ID +}); + export const updateEditorValue: ActionCreator = ( newEditorValue: string, workspaceLocation: WorkspaceLocation diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index 2c4e2bc603..4e9d89cec2 100755 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -58,6 +58,7 @@ export interface IDispatchProps { handleEditorWidthChange: (widthChange: number) => void; handleGenerateLz: () => void; handleInterruptEval: () => void; + handleInvalidEditorSessionId: () => void; handleExternalSelect: (externalLibraryName: ExternalLibraryName) => void; handleReplEval: () => void; handleReplOutputClear: () => void; @@ -95,6 +96,7 @@ class Playground extends React.Component { handleEditorEval: this.props.handleEditorEval, handleGenerateLz: this.props.handleGenerateLz, handleInterruptEval: this.props.handleInterruptEval, + handleInvalidEditorSessionId: this.props.handleInvalidEditorSessionId, handleReplEval: this.props.handleReplEval, handleReplOutputClear: this.props.handleReplOutputClear, handleSetEditorSessionId: this.props.handleSetEditorSessionId, diff --git a/src/components/__tests__/Playground.tsx b/src/components/__tests__/Playground.tsx index 45b2989557..1fcf802a88 100755 --- a/src/components/__tests__/Playground.tsx +++ b/src/components/__tests__/Playground.tsx @@ -28,6 +28,7 @@ const baseProps = { handleExternalSelect: (externalLibraryName: ExternalLibraryName) => {}, handleGenerateLz: () => {}, handleInterruptEval: () => {}, + handleInvalidEditorSessionId: () => {}, handleReplEval: () => {}, handleReplOutputClear: () => {}, handleReplValueChange: (code: string) => {}, diff --git a/src/components/workspace/ControlBar.tsx b/src/components/workspace/ControlBar.tsx index f286ca29f9..b79e21a046 100755 --- a/src/components/workspace/ControlBar.tsx +++ b/src/components/workspace/ControlBar.tsx @@ -25,6 +25,7 @@ export type ControlBarProps = { handleExternalSelect?: (i: IExternal, e: React.ChangeEvent) => void; handleGenerateLz?: () => void; handleInterruptEval: () => void; + handleInvalidEditorSessionId?: () => void; handleReplEval: () => void; handleReplOutputClear: () => void; handleSetEditorSessionId?: (editorSessionId: string) => void; @@ -107,7 +108,26 @@ class ControlBar extends React.PureComponent { } }; const handleStartJoining = (e: React.FormEvent) => { - this.props.handleSetEditorSessionId!(this.joinInputElem.current!.value); + const xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = () => { + if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { + console.log('Reached server to verify ID'); + const state = JSON.parse(xmlhttp.responseText).state; + if (state === true) { + console.log('ID true'); + this.props.handleSetEditorSessionId!(this.joinInputElem.current!.value); + } else { + this.props.handleInvalidEditorSessionId!(); + this.props.handleSetEditorSessionId!(''); + } + } else if (xmlhttp.readyState === 4 && xmlhttp.status !== 200) { + console.log('Cannot reach server'); + this.props.handleSetEditorSessionId!(''); + } + }; + xmlhttp.open('GET', 'https://13.250.109.61/gists/' + this.joinInputElem.current!.value, true); + xmlhttp.send(); + e.preventDefault(); }; const runButton = ( diff --git a/src/components/workspace/Editor.tsx b/src/components/workspace/Editor.tsx index 91e2d6f7ab..38d3c732c1 100755 --- a/src/components/workspace/Editor.tsx +++ b/src/components/workspace/Editor.tsx @@ -184,15 +184,6 @@ class Editor extends React.PureComponent { }); if (this.props.editorSessionId !== '') { console.log('Component mounted with id = ' + this.props.editorSessionId); - const xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = () => { - if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { - const state = JSON.parse(xmlhttp.responseText).state; - console.log(state ? 'Valid Id' : 'Invalid Id'); - } - }; - xmlhttp.open('GET', 'https://13.250.109.61/gists/' + this.props.editorSessionId, true); - xmlhttp.send(); const ShareAce = new sharedbAce(this.props.editorSessionId!, { WsUrl: 'wss://13.250.109.61/ws', diff --git a/src/containers/PlaygroundContainer.ts b/src/containers/PlaygroundContainer.ts index c56c51e5e9..0f8ed3cfa6 100755 --- a/src/containers/PlaygroundContainer.ts +++ b/src/containers/PlaygroundContainer.ts @@ -14,6 +14,7 @@ import { evalEditor, evalRepl, generateLzString, + invalidEditorSessionId, playgroundExternalSelect, setEditorSessionId, setWebsocketStatus, @@ -56,6 +57,7 @@ const mapDispatchToProps: MapDispatchToProps = (dispatch: Di handleEditorWidthChange: (widthChange: number) => changeEditorWidth(widthChange, location), handleGenerateLz: generateLzString, handleInterruptEval: () => beginInterruptExecution(location), + handleInvalidEditorSessionId: () => invalidEditorSessionId(), handleExternalSelect: (externalLibraryName: ExternalLibraryName) => playgroundExternalSelect(externalLibraryName, location), handleReplEval: () => evalRepl(location), diff --git a/src/sagas/index.ts b/src/sagas/index.ts old mode 100644 new mode 100755 index 303027a506..c7fbb6e323 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -67,6 +67,10 @@ function* workspaceSaga(): SagaIterator { yield call(showWarningMessage, 'Autorun ' + (isEditorAutorun ? 'Started' : 'Stopped'), 750); }); + yield takeEvery(actionTypes.INVALID_EDITOR_SESSION_ID, function*(action) { + yield call(showWarningMessage, 'Invalid ID Input', 1000); + }); + yield takeEvery(actionTypes.EVAL_REPL, function*(action) { const location = (action as actionTypes.IAction).payload.workspaceLocation; const code: string = yield select( From b0db3cef158a65829eb2f190bd58dadecc16bf7f Mon Sep 17 00:00:00 2001 From: zephyr Date: Thu, 4 Apr 2019 20:00:46 +0800 Subject: [PATCH 06/15] Update snapshot and pass test --- .../__tests__/__snapshots__/index.tsx.snap | 52 +++++++++---------- src/components/workspace/__tests__/Editor.tsx | 5 ++ 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/components/assessment/__tests__/__snapshots__/index.tsx.snap b/src/components/assessment/__tests__/__snapshots__/index.tsx.snap index 0aac3badf0..778ef00785 100644 --- a/src/components/assessment/__tests__/__snapshots__/index.tsx.snap +++ b/src/components/assessment/__tests__/__snapshots__/index.tsx.snap @@ -53,8 +53,8 @@ exports[`Assessment page does not show attempt Button for upcoming assessments f
- -