fixed merge conflict with develop
This commit is contained in:
commit
e93d7e4ccc
24 changed files with 845 additions and 458 deletions
BIN
app/assets/images/junto.gif
Normal file
BIN
app/assets/images/junto.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
|
@ -1,144 +1,259 @@
|
||||||
/* Map Cards */
|
/* Map Cards */
|
||||||
|
|
||||||
.map {
|
.map {
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
width:220px;
|
width:220px;
|
||||||
height:340px;
|
height:340px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
background: #e8e8e8;
|
background: #e8e8e8;
|
||||||
border-radius:2px;
|
border-radius:2px;
|
||||||
margin:16px;
|
margin:16px;
|
||||||
box-shadow: 0px 3px 3px rgba(0,0,0,0.23), 0 3px 3px rgba(0,0,0,0.16);
|
box-shadow: 0px 3px 3px rgba(0,0,0,0.23), 0 3px 3px rgba(0,0,0,0.16);
|
||||||
}
|
|
||||||
.map.newMap {
|
|
||||||
float: left;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.map.newMap:hover {
|
|
||||||
background: #dcdcdc;
|
|
||||||
}
|
|
||||||
.map.newMap a {
|
|
||||||
height: 340px;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.newMap .newMapImage {
|
|
||||||
display: block;
|
|
||||||
width: 72px;
|
|
||||||
height: 72px;
|
|
||||||
background-image: url("<%= asset_data_uri('newmap_sprite.png') %>");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 0 0;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -36px;
|
|
||||||
top: 50%;
|
|
||||||
margin-top: -36px;
|
|
||||||
}
|
|
||||||
.map:hover .newMapImage {
|
|
||||||
background-position: 0 -72px;
|
|
||||||
}
|
|
||||||
.newMap span {
|
|
||||||
font-family: 'din-regular', sans-serif;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 22px;
|
|
||||||
text-align: center;
|
|
||||||
display: block;
|
|
||||||
padding-top: 220px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapCard {
|
&.newMap {
|
||||||
position:relative;
|
float: left;
|
||||||
width:100%;
|
position: relative;
|
||||||
height:308px;
|
|
||||||
padding: 0 0 16px 0;
|
|
||||||
color: #424242;
|
|
||||||
|
|
||||||
.mapScreenshot {
|
&:hover {
|
||||||
width: 100%;
|
background: #dcdcdc;
|
||||||
height: 220px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapScreenshot img {
|
.newMapImage {
|
||||||
width: 100%;
|
background-position: 0 -72px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
a {
|
||||||
word-wrap: break-word;
|
height: 340px;
|
||||||
font-size:18px;
|
display: block;
|
||||||
line-height:22px;
|
position: relative;
|
||||||
height: 71px;
|
}
|
||||||
display:table;
|
|
||||||
padding: 0 16px;
|
.newMapImage {
|
||||||
font-family: 'din-regular', sans-serif;
|
display: block;
|
||||||
margin: 0 auto;
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
background-image: url("<%= asset_data_uri('newmap_sprite.png') %>");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 0 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -36px;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-family: 'din-regular', sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
padding-top: 220px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapCard {
|
||||||
|
position:relative;
|
||||||
|
width:100%;
|
||||||
|
height:308px;
|
||||||
|
padding: 0 0 16px 0;
|
||||||
|
color: #424242;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.dropdownMenu .menuToggle .circle {
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
.dropdownMenu .menuToggle:hover .circle {
|
||||||
|
background-color: #DDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainContent {
|
||||||
|
filter: blur(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapMetadata {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapHasMapper, .mapHasConversation {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 8px;
|
||||||
|
min-width: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #FFF;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
.mapperList {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapperList {
|
||||||
|
display: none;
|
||||||
|
padding: 8px;
|
||||||
|
list-style-type: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
&.live {
|
||||||
|
height: 32px;
|
||||||
|
padding-left: 32px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
padding-left: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mapHasMapper {
|
||||||
|
background: url('<%= asset_path('junto.png') %>') no-repeat 4px 0;
|
||||||
|
}
|
||||||
|
.mapHasConversation {
|
||||||
|
background: url('<%= asset_path('junto.gif') %>') no-repeat 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownMenu {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.menuToggle {
|
||||||
|
width: 30px;
|
||||||
|
height: 10px;
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #454545;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .circle {
|
||||||
|
background-color: #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuItems {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
right: 0px;
|
||||||
|
background: #FFF;
|
||||||
|
border-radius: 2px;
|
||||||
|
list-style-type: none;
|
||||||
|
color: #454545;
|
||||||
|
|
||||||
|
li {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 6px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #DDD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapScreenshot {
|
||||||
|
width: 100%;
|
||||||
|
height: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapScreenshot img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
word-wrap: break-word;
|
||||||
|
font-size:18px;
|
||||||
|
line-height:22px;
|
||||||
|
height: 71px;
|
||||||
|
display:table;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-family: 'din-regular', sans-serif;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
.innerTitle {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.creatorAndPerm {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creatorImage {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.creatorName {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardViewOnly {
|
||||||
|
float: right;
|
||||||
|
line-height: 32px;
|
||||||
|
padding-right: 10px;
|
||||||
|
color: #454545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll {
|
||||||
|
display:block;
|
||||||
|
font-family: helvetica, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.mapMetadata {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 40px 20px 0;
|
||||||
|
height: 300px;
|
||||||
|
font-family: 'din-regular', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #FFF;
|
||||||
|
background: -moz-linear-gradient(top, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.43) 81%, rgba(0,0,0,0) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(0,0,0,0.65) 0%,rgba(0,0,0,0.43) 81%,rgba(0,0,0,0) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(0,0,0,0.65) 0%,rgba(0,0,0,0.43) 81%,rgba(0,0,0,0) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a6000000', endColorstr='#00000000',GradientType=0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadataSection {
|
||||||
|
padding: 16px 0;
|
||||||
|
width: 90px;
|
||||||
|
float: left;
|
||||||
|
font-family: 'din-medium', sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.innerTitle {
|
|
||||||
display: table-cell;
|
|
||||||
vertical-align: middle;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.creatorAndPerm {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.creatorImage {
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 16px;
|
|
||||||
vertical-align: middle;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.creatorName {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.scroll {
|
|
||||||
display:block;
|
|
||||||
font-family: helvetica, sans-serif;
|
|
||||||
font-size: 12px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .mainContent {
|
|
||||||
filter: blur(2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .mapMetadata {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapMetadata {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
padding: 40px 20px 0;
|
|
||||||
height: 300px;
|
|
||||||
font-family: 'din-regular', sans-serif;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #FFF;
|
|
||||||
background: -moz-linear-gradient(top, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.43) 81%, rgba(0,0,0,0) 100%);
|
|
||||||
background: -webkit-linear-gradient(top, rgba(0,0,0,0.65) 0%,rgba(0,0,0,0.43) 81%,rgba(0,0,0,0) 100%);
|
|
||||||
background: linear-gradient(to bottom, rgba(0,0,0,0.65) 0%,rgba(0,0,0,0.43) 81%,rgba(0,0,0,0) 100%);
|
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a6000000', endColorstr='#00000000',GradientType=0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
.metadataSection {
|
|
||||||
padding: 16px 0;
|
|
||||||
width: 90px;
|
|
||||||
float: left;
|
|
||||||
font-family: 'din-medium', sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
#%>
|
#%>
|
||||||
|
|
||||||
<h3>HELP</h3>
|
<h3>HELP</h3>
|
||||||
|
|
||||||
<div id="cheatSheet">
|
<div id="cheatSheet">
|
||||||
<ul id="helpWrapper">
|
<ul id="helpWrapper">
|
||||||
<li><a href="#quickReference">QUICK REFERENCE</a></li>
|
<li><a href="#quickReference">QUICK REFERENCE</a></li>
|
||||||
<li><a href="#tutorials">TUTORIALS</a></li>
|
<li><a href="#tutorials">TUTORIAL</a></li>
|
||||||
<li><a href="#moreResources">MORE RESOURCES</a></li>
|
<li><a href="#moreResources">MORE RESOURCES</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="quickReference">
|
<div id="quickReference">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#csCreatingTopics">Creating Topics</a></li>
|
<li><a href="#csCreatingTopics">Creating Topics</a></li>
|
||||||
|
@ -25,13 +25,13 @@
|
||||||
<li><a href="#csKeyboardShortcuts">Keyboard Shortcuts</a></li>
|
<li><a href="#csKeyboardShortcuts">Keyboard Shortcuts</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="csTopicView">
|
<div id="csTopicView">
|
||||||
<div class="csItem"><span class="csTitle">Recenter Topics around chosen Topic:</span> Alt + click on the topic OR Alt + E</div>
|
<div class="csItem"><span class="csTitle">Recenter Topics around chosen Topic:</span> Alt + click on the topic OR Alt + E</div>
|
||||||
<div class="csItem"><span class="csTitle">Reveal the siblings for a Topic:</span> Right-click and choose 'Reveal siblings' OR Alt + R</div>
|
<div class="csItem"><span class="csTitle">Reveal the siblings for a Topic:</span> Right-click and choose 'Reveal siblings' OR Alt + R</div>
|
||||||
<div class="csItem"><span class="csTitle">Center topic and reveal siblings:</span> Alt + T</div>
|
<div class="csItem"><span class="csTitle">Center topic and reveal siblings:</span> Alt + T</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csCreatingTopics">
|
<div id="csCreatingTopics">
|
||||||
<div class="csItem"><span class="csTitle">Double-click on canvas:</span> Bring up the metacode spinner</div>
|
<div class="csItem"><span class="csTitle">Double-click on canvas:</span> Bring up the metacode spinner</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Scroll:</span> change metacode spinner selection</div>
|
<div class="csItem indented"><span class="csTitle">Scroll:</span> change metacode spinner selection</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Tab:</span> rotate spinner counter-clockwise</div>
|
<div class="csItem indented"><span class="csTitle">Tab:</span> rotate spinner counter-clockwise</div>
|
||||||
|
@ -39,10 +39,10 @@
|
||||||
<div class="csItem indented"><span class="csTitle">Esc:</span> Hides auto-suggestion results</div>
|
<div class="csItem indented"><span class="csTitle">Esc:</span> Hides auto-suggestion results</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Enter:</span> create a new topic</div>
|
<div class="csItem indented"><span class="csTitle">Enter:</span> create a new topic</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Gear Icon:</span> open up metacode settings</div>
|
<div class="csItem indented"><span class="csTitle">Gear Icon:</span> open up metacode settings</div>
|
||||||
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_topics.html" target= "_blank">Learn More</a></div>
|
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_topics.html" target= "_blank">Learn More</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csEditingTopics">
|
<div id="csEditingTopics">
|
||||||
<div class="csItem">
|
<div class="csItem">
|
||||||
<span class="csTitle">Open 'Topic' card:</span> Double-click on topic icon
|
<span class="csTitle">Open 'Topic' card:</span> Double-click on topic icon
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,11 +71,11 @@
|
||||||
<span class="csTitle">Open 'Context Menu':</span> Right-click/alt+click on topic icon or synapse
|
<span class="csTitle">Open 'Context Menu':</span> Right-click/alt+click on topic icon or synapse
|
||||||
</div>
|
</div>
|
||||||
<div class="csItem indented">*Hide/Remove/Delete topic within context menu</div>
|
<div class="csItem indented">*Hide/Remove/Delete topic within context menu</div>
|
||||||
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_topics.html" target= "_blank">Learn More</a></div>
|
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_topics.html" target= "_blank">Learn More</a></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csCreatingSynapses">
|
<div id="csCreatingSynapses">
|
||||||
<div class="csItem"><span class="csTitle">Open 'Create Synapse' prompt:</span> Right-click & drag from one topic to another</div>
|
<div class="csItem"><span class="csTitle">Open 'Create Synapse' prompt:</span> Right-click & drag from one topic to another</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Enter or Tab:</span> Create synapse</div>
|
<div class="csItem indented"><span class="csTitle">Enter or Tab:</span> Create synapse</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Esc or Delete:</span> Cancel synapse creation</div>
|
<div class="csItem indented"><span class="csTitle">Esc or Delete:</span> Cancel synapse creation</div>
|
||||||
|
@ -83,11 +83,11 @@
|
||||||
<div class="csItem"><span class="csTitle">Create new Topic with Synapse:</span> Right-click + drag from topic to open canvas</div>
|
<div class="csItem"><span class="csTitle">Create new Topic with Synapse:</span> Right-click + drag from topic to open canvas</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Enter:</span> Create topic</div>
|
<div class="csItem indented"><span class="csTitle">Enter:</span> Create topic</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Enter:</span> Create synapse</div>
|
<div class="csItem indented"><span class="csTitle">Enter:</span> Create synapse</div>
|
||||||
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_synapses.html" target= "_blank">Learn More</a></div>
|
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_synapses.html" target= "_blank">Learn More</a></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csEditingSynapses">
|
<div id="csEditingSynapses">
|
||||||
<div class="csItem"><span class="csTitle">Open 'Synapse' card:</span> Double-click on Synapse </div>
|
<div class="csItem"><span class="csTitle">Open 'Synapse' card:</span> Double-click on Synapse </div>
|
||||||
<div class="csItem indented"><span class="csTitle">Edit Synapse description:</span> Click on current description text</div>
|
<div class="csItem indented"><span class="csTitle">Edit Synapse description:</span> Click on current description text</div>
|
||||||
<div class="csItem indented"><span class="csTitle">Save Synapse description:</span> Hit enter</div>
|
<div class="csItem indented"><span class="csTitle">Save Synapse description:</span> Hit enter</div>
|
||||||
|
@ -96,19 +96,19 @@
|
||||||
<div class="csItem indented"><span class="csTitle">Browse synapses / change visible synapse</span> click on arrow icon and select desired synapse</div>
|
<div class="csItem indented"><span class="csTitle">Browse synapses / change visible synapse</span> click on arrow icon and select desired synapse</div>
|
||||||
<div class="csItem"><span class="csTitle">Open 'Context Menu':</span> Right-click/alt-click on Synapse</div>
|
<div class="csItem"><span class="csTitle">Open 'Context Menu':</span> Right-click/alt-click on Synapse</div>
|
||||||
<div class="csItem indented">*Hide/Remove/Delete synapse within context menu</div>
|
<div class="csItem indented">*Hide/Remove/Delete synapse within context menu</div>
|
||||||
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_synapses.html" target= "_blank">Learn More</a></div>
|
<div class="csItem"><br><a href="https://docs.metamaps.cc/creating_synapses.html" target= "_blank">Learn More</a></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csNavigation">
|
<div id="csNavigation">
|
||||||
<div class="csItem"><span class="csTitle">Move around Canvas:</span> Click and drag</div>
|
<div class="csItem"><span class="csTitle">Move around Canvas:</span> Click and drag</div>
|
||||||
<div class="csItem"><span class="csTitle">Zoom in/out:</span> Scroll OR click on <div id="zoomIn"> </div> & <div id="zoomOut"> </div></div>
|
<div class="csItem"><span class="csTitle">Zoom in/out:</span> Scroll OR click on <div id="zoomIn"> </div> & <div id="zoomOut"> </div></div>
|
||||||
<div class="csItem"><span class="csTitle">Zoom to see all:</span> Click <div id="centerMap"></div> OR Ctrl + E</div>
|
<div class="csItem"><span class="csTitle">Zoom to see all:</span> Click <div id="centerMap"></div> OR Ctrl + E</div>
|
||||||
<div class="csItem"><br><a href="https://docs.metamaps.cc/exploring_maps.html" target= "_blank">Learn More</a></div>
|
<div class="csItem"><br><a href="https://docs.metamaps.cc/exploring_maps.html" target= "_blank">Learn More</a></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csSelection">
|
<div id="csSelection">
|
||||||
<div class="csItem"><span class="csTitle">Select/Deselect Topic:</span> Click on topic icon</div>
|
<div class="csItem"><span class="csTitle">Select/Deselect Topic:</span> Click on topic icon</div>
|
||||||
<div class="csItem"><span class="csTitle">Select/Deselect Synapse:</span> Click on synapse</div>
|
<div class="csItem"><span class="csTitle">Select/Deselect Synapse:</span> Click on synapse</div>
|
||||||
<div class="csItem"><span class="csTitle">Select multiple Topics/Synapses:</span> Shift + click</div>
|
<div class="csItem"><span class="csTitle">Select multiple Topics/Synapses:</span> Shift + click</div>
|
||||||
|
@ -120,9 +120,9 @@
|
||||||
<div class="csItem"><span class="csTitle">Deselect all topics & Synapses:</span> Click on background or Esc</div>
|
<div class="csItem"><span class="csTitle">Deselect all topics & Synapses:</span> Click on background or Esc</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csSearch">
|
<div id="csSearch">
|
||||||
<div class="csItem"><span class="csTitle">Open 'Search' prompt:</span> Ctrl + /</div>
|
<div class="csItem"><span class="csTitle">Open 'Search' prompt:</span> Ctrl + /</div>
|
||||||
<div class="csItem"><span class="csTitle">Close 'Search' prompt:</span> Esc</div>
|
<div class="csItem"><span class="csTitle">Close 'Search' prompt:</span> Esc</div>
|
||||||
<% if controller_name == "maps" && action_name == "show" %>
|
<% if controller_name == "maps" && action_name == "show" %>
|
||||||
<div class="csItem"><span class="csTitle">Add to current Map:</span> Click "+" on a topic result</div>
|
<div class="csItem"><span class="csTitle">Add to current Map:</span> Click "+" on a topic result</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -131,9 +131,9 @@
|
||||||
<div class="csItem"><span class="csTitle">Search for mapper:</span> type "mapper:", then your search query. i.e. mapper:Robert</div>
|
<div class="csItem"><span class="csTitle">Search for mapper:</span> type "mapper:", then your search query. i.e. mapper:Robert</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="csKeyboardShortcuts">
|
<div id="csKeyboardShortcuts">
|
||||||
<div class="csItem"><span class="csTitle">Ctrl + /:</span> Open 'Search' prompt</div>
|
<div class="csItem"><span class="csTitle">Ctrl + /:</span> Open 'Search' prompt</div>
|
||||||
<div class="csItem"><span class="csTitle">Ctrl + H:</span> Hide selection on map</div>
|
<div class="csItem"><span class="csTitle">Ctrl + H:</span> Hide selection on map</div>
|
||||||
<div class="csItem"><span class="csTitle">Ctrl + M:</span> Remove selection from map</div>
|
<div class="csItem"><span class="csTitle">Ctrl + M:</span> Remove selection from map</div>
|
||||||
<div class="csItem"><span class="csTitle">Ctrl + D:</span> Delete selection</div>
|
<div class="csItem"><span class="csTitle">Ctrl + D:</span> Delete selection</div>
|
||||||
<div class="csItem"><span class="csTitle">Ctrl + A:</span> Select all topics</div>
|
<div class="csItem"><span class="csTitle">Ctrl + A:</span> Select all topics</div>
|
||||||
|
@ -145,9 +145,6 @@
|
||||||
|
|
||||||
<div id="tutorials">
|
<div id="tutorials">
|
||||||
<iframe id="tutorialVideo" src="//player.vimeo.com/video/88334167" width="552" height="320" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
<iframe id="tutorialVideo" src="//player.vimeo.com/video/88334167" width="552" height="320" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||||
<div class="tutorialItem active" id="gettingStarted">1. GETTING STARTED</div>
|
|
||||||
<div class="tutorialItem disable" id="upYourSkillz">2. UP YOUR SKILLZ</div>
|
|
||||||
<div class="tutorialItem disable" id="advancedMapping">3. ADVANCED MAPPING</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="moreResources">
|
<div id="moreResources">
|
||||||
|
|
|
@ -37,6 +37,10 @@ const JIT = {
|
||||||
tempInit: false,
|
tempInit: false,
|
||||||
tempNode: null,
|
tempNode: null,
|
||||||
tempNode2: null,
|
tempNode2: null,
|
||||||
|
mouseDownPix: {},
|
||||||
|
dragFlag : 0,
|
||||||
|
dragTolerance: 0,
|
||||||
|
virtualPointer: {},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
topicDrag: 'Metamaps:JIT:events:topicDrag',
|
topicDrag: 'Metamaps:JIT:events:topicDrag',
|
||||||
|
@ -754,77 +758,127 @@ const JIT = {
|
||||||
Control.deselectAllNodes()
|
Control.deselectAllNodes()
|
||||||
}, // escKeyHandler
|
}, // escKeyHandler
|
||||||
onDragMoveTopicHandler: function (node, eventInfo, e) {
|
onDragMoveTopicHandler: function (node, eventInfo, e) {
|
||||||
const self = JIT
|
var self = JIT
|
||||||
|
|
||||||
// this is used to send nodes that are moving to
|
var authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper)
|
||||||
// other realtime collaborators on the same map
|
|
||||||
const positionsToSend = {}
|
|
||||||
let topic
|
|
||||||
|
|
||||||
const authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper)
|
|
||||||
|
|
||||||
if (node && !node.nodeFrom) {
|
if (node && !node.nodeFrom) {
|
||||||
const pos = eventInfo.getPos()
|
var pos = eventInfo.getPos(),
|
||||||
|
EDGE_THICKNESS = 30 /** Metamaps.Visualize.mGraph.canvas.scaleOffsetX*/,
|
||||||
|
SHIFT = 2 / Metamaps.Visualize.mGraph.canvas.scaleOffsetX,
|
||||||
|
PERIOD = 5;
|
||||||
|
|
||||||
|
//self.virtualPointer = pos;
|
||||||
// if it's a left click, or a touch, move the node
|
// if it's a left click, or a touch, move the node
|
||||||
if (e.touches || (e.button === 0 && !e.altKey && !e.ctrlKey && !e.shiftKey && (e.buttons === 0 || e.buttons === 1 || e.buttons === undefined))) {
|
if (e.touches || (e.button === 0 && !e.altKey && !e.ctrlKey && (e.buttons === 0 || e.buttons === 1 || e.buttons === undefined))) {
|
||||||
|
|
||||||
|
var width = Metamaps.Visualize.mGraph.canvas.getSize().width,
|
||||||
|
height = Metamaps.Visualize.mGraph.canvas.getSize().height,
|
||||||
|
xPix = Metamaps.Util.coordsToPixels(pos).x,
|
||||||
|
yPix = Metamaps.Util.coordsToPixels(pos).y;
|
||||||
|
|
||||||
|
if(self.dragFlag === 0){
|
||||||
|
self.mouseDownPix = Metamaps.Util.coordsToPixels(eventInfo.getPos());
|
||||||
|
self.dragFlag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Metamaps.Util.getDistance(Metamaps.Util.coordsToPixels(pos),self.mouseDownPix) > 2 && !self.dragTolerance){
|
||||||
|
self.dragTolerance = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(xPix < EDGE_THICKNESS && self.dragTolerance ){
|
||||||
|
clearInterval(self.dragLeftEdge);
|
||||||
|
clearInterval(self.dragRightEdge);
|
||||||
|
clearInterval(self.dragTopEdge);
|
||||||
|
clearInterval(self.dragBottomEdge);
|
||||||
|
self.virtualPointer = {x:Metamaps.Util.pixelsToCoords({x:EDGE_THICKNESS,y:yPix}).x - SHIFT,y:pos.y};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(SHIFT,0);
|
||||||
|
self.updateTopicPositions(node, self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
|
||||||
|
self.dragLeftEdge = setInterval( function(){
|
||||||
|
self.virtualPointer = {x:Metamaps.Util.pixelsToCoords({x:EDGE_THICKNESS,y:yPix}).x - SHIFT,y:pos.y};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(SHIFT,0);
|
||||||
|
self.updateTopicPositions(node,self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
} , PERIOD);
|
||||||
|
|
||||||
|
}
|
||||||
|
if(width - xPix < EDGE_THICKNESS && self.dragTolerance){
|
||||||
|
clearInterval(self.dragLeftEdge);
|
||||||
|
clearInterval(self.dragRightEdge);
|
||||||
|
clearInterval(self.dragTopEdge);
|
||||||
|
clearInterval(self.dragBottomEdge);
|
||||||
|
self.virtualPointer = {x:Metamaps.Util.pixelsToCoords({x:width - EDGE_THICKNESS,y:yPix}).x + SHIFT,y:pos.y};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(-SHIFT,0);
|
||||||
|
self.updateTopicPositions(node, self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
|
||||||
|
self.dragRightEdge = setInterval( function(){
|
||||||
|
self.virtualPointer = {x:Metamaps.Util.pixelsToCoords({x:width - EDGE_THICKNESS,y:yPix}).x + SHIFT,y:pos.y};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(-SHIFT,0);
|
||||||
|
self.updateTopicPositions(node, self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
} , PERIOD);
|
||||||
|
}
|
||||||
|
if(yPix < EDGE_THICKNESS && self.dragTolerance){
|
||||||
|
clearInterval(self.dragLeftEdge);
|
||||||
|
clearInterval(self.dragRightEdge);
|
||||||
|
clearInterval(self.dragTopEdge);
|
||||||
|
clearInterval(self.dragBottomEdge);
|
||||||
|
self.virtualPointer = {x:pos.x,y:Metamaps.Util.pixelsToCoords({x:xPix,y:EDGE_THICKNESS}).y - SHIFT};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(0,SHIFT);
|
||||||
|
self.updateTopicPositions(node, self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
|
||||||
|
self.dragTopEdge = setInterval( function(){
|
||||||
|
self.virtualPointer = {x:pos.x,y:Metamaps.Util.pixelsToCoords({x:xPix,y:EDGE_THICKNESS}).y - SHIFT};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(0,SHIFT);
|
||||||
|
self.updateTopicPositions(node, self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
} , PERIOD);
|
||||||
|
}
|
||||||
|
if(height - yPix < EDGE_THICKNESS && self.dragTolerance){
|
||||||
|
clearInterval(self.dragLeftEdge);
|
||||||
|
clearInterval(self.dragRightEdge);
|
||||||
|
clearInterval(self.dragTopEdge);
|
||||||
|
clearInterval(self.dragBottomEdge);
|
||||||
|
self.virtualPointer = {x:pos.x,y:Metamaps.Util.pixelsToCoords({x:xPix,y:height - EDGE_THICKNESS}).y + SHIFT};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(0,-SHIFT);
|
||||||
|
self.updateTopicPositions(node, self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
|
||||||
|
self.dragBottomEdge = setInterval( function(){
|
||||||
|
self.virtualPointer = {x:pos.x,y:Metamaps.Util.pixelsToCoords({x:xPix,y:height - EDGE_THICKNESS}).y + SHIFT};
|
||||||
|
Metamaps.Visualize.mGraph.canvas.translate(0,-SHIFT);
|
||||||
|
self.updateTopicPositions(node, self.virtualPointer);
|
||||||
|
Visualize.mGraph.plot();
|
||||||
|
} , PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(xPix >= EDGE_THICKNESS && width - xPix >= EDGE_THICKNESS && yPix >= EDGE_THICKNESS && height - yPix >= EDGE_THICKNESS){
|
||||||
|
clearInterval(self.dragLeftEdge);
|
||||||
|
clearInterval(self.dragRightEdge);
|
||||||
|
clearInterval(self.dragTopEdge);
|
||||||
|
clearInterval(self.dragBottomEdge);
|
||||||
|
|
||||||
|
self.updateTopicPositions(node,pos);
|
||||||
|
Visualize.mGraph.plot()
|
||||||
|
}
|
||||||
|
|
||||||
// if the node dragged isn't already selected, select it
|
// if the node dragged isn't already selected, select it
|
||||||
const whatToDo = self.handleSelectionBeforeDragging(node, e)
|
var whatToDo = self.handleSelectionBeforeDragging(node, e)
|
||||||
if (node.pos.rho || node.pos.rho === 0) {
|
if (node.pos.rho || node.pos.rho === 0) {
|
||||||
// this means we're in topic view
|
// this means we're in topic view
|
||||||
const rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y)
|
var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y)
|
||||||
const theta = Math.atan2(pos.y, pos.x)
|
var theta = Math.atan2(pos.y, pos.x)
|
||||||
node.pos.setp(theta, rho)
|
node.pos.setp(theta, rho)
|
||||||
} else if (whatToDo === 'only-drag-this-one') {
|
|
||||||
node.pos.setc(pos.x, pos.y)
|
|
||||||
|
|
||||||
if (Active.Map) {
|
|
||||||
topic = node.getData('topic')
|
|
||||||
// we use the topic ID not the node id
|
|
||||||
// because we can't depend on the node id
|
|
||||||
// to be the same as on other collaborators
|
|
||||||
// maps
|
|
||||||
positionsToSend[topic.id] = pos
|
|
||||||
$(document).trigger(JIT.events.topicDrag, [positionsToSend])
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const len = Selected.Nodes.length
|
//self.updateTopicPositions(node,pos);
|
||||||
|
|
||||||
// first define offset for each node
|
|
||||||
const xOffset = []
|
|
||||||
const yOffset = []
|
|
||||||
for (let i = 0; i < len; i += 1) {
|
|
||||||
const n = Selected.Nodes[i]
|
|
||||||
xOffset[i] = n.pos.x - node.pos.x
|
|
||||||
yOffset[i] = n.pos.y - node.pos.y
|
|
||||||
} // for
|
|
||||||
|
|
||||||
for (let i = 0; i < len; i += 1) {
|
|
||||||
const n = Selected.Nodes[i]
|
|
||||||
const x = pos.x + xOffset[i]
|
|
||||||
const y = pos.y + yOffset[i]
|
|
||||||
n.pos.setc(x, y)
|
|
||||||
|
|
||||||
if (Active.Map) {
|
|
||||||
topic = n.getData('topic')
|
|
||||||
// we use the topic ID not the node id
|
|
||||||
// because we can't depend on the node id
|
|
||||||
// to be the same as on other collaborators
|
|
||||||
// maps
|
|
||||||
positionsToSend[topic.id] = n.pos
|
|
||||||
}
|
|
||||||
} // for
|
|
||||||
|
|
||||||
if (Active.Map) {
|
|
||||||
$(document).trigger(JIT.events.topicDrag, [positionsToSend])
|
|
||||||
}
|
|
||||||
} // if
|
|
||||||
|
|
||||||
if (whatToDo === 'deselect') {
|
|
||||||
Control.deselectNode(node)
|
|
||||||
}
|
}
|
||||||
Visualize.mGraph.plot()
|
}
|
||||||
} else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && authorized) {
|
// if it's a right click or holding down alt, start synapse creation ->third option is for firefox
|
||||||
// if it's a right click or holding down alt, start synapse creation ->third option is for firefox
|
else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && authorized) {
|
||||||
if (JIT.tempInit === false) {
|
if (JIT.tempInit === false) {
|
||||||
JIT.tempNode = node
|
JIT.tempNode = node
|
||||||
JIT.tempInit = true
|
JIT.tempInit = true
|
||||||
|
@ -832,7 +886,7 @@ const JIT = {
|
||||||
Create.newTopic.hide()
|
Create.newTopic.hide()
|
||||||
Create.newSynapse.hide()
|
Create.newSynapse.hide()
|
||||||
// set the draw synapse start positions
|
// set the draw synapse start positions
|
||||||
const l = Selected.Nodes.length
|
var l = Selected.Nodes.length
|
||||||
if (l > 0) {
|
if (l > 0) {
|
||||||
for (let i = l - 1; i >= 0; i -= 1) {
|
for (let i = l - 1; i >= 0; i -= 1) {
|
||||||
const n = Selected.Nodes[i]
|
const n = Selected.Nodes[i]
|
||||||
|
@ -874,8 +928,8 @@ const JIT = {
|
||||||
n.setData('dim', 25, 'current')
|
n.setData('dim', 25, 'current')
|
||||||
})
|
})
|
||||||
// pop up node creation :)
|
// pop up node creation :)
|
||||||
const myX = e.clientX - 110
|
var myX = e.clientX - 110
|
||||||
const myY = e.clientY - 30
|
var myY = e.clientY - 30
|
||||||
$('#new_topic').css('left', myX + 'px')
|
$('#new_topic').css('left', myX + 'px')
|
||||||
$('#new_topic').css('top', myY + 'px')
|
$('#new_topic').css('top', myY + 'px')
|
||||||
Create.newTopic.x = eventInfo.getPos().x
|
Create.newTopic.x = eventInfo.getPos().x
|
||||||
|
@ -887,9 +941,11 @@ const JIT = {
|
||||||
y: pos.y
|
y: pos.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && Active.Topic) {
|
}
|
||||||
|
else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && Active.Topic) {
|
||||||
GlobalUI.notifyUser('Cannot create in Topic view.')
|
GlobalUI.notifyUser('Cannot create in Topic view.')
|
||||||
} else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && !authorized) {
|
}
|
||||||
|
else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && !authorized) {
|
||||||
GlobalUI.notifyUser('Cannot edit Public map.')
|
GlobalUI.notifyUser('Cannot edit Public map.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -905,9 +961,21 @@ const JIT = {
|
||||||
Visualize.mGraph.plot()
|
Visualize.mGraph.plot()
|
||||||
}, // onDragCancelHandler
|
}, // onDragCancelHandler
|
||||||
onDragEndTopicHandler: function (node, eventInfo, e) {
|
onDragEndTopicHandler: function (node, eventInfo, e) {
|
||||||
let midpoint = {}
|
var self = JIT;
|
||||||
let pixelPos
|
var midpoint = {}, pixelPos, mapping
|
||||||
let mapping
|
|
||||||
|
clearInterval(self.dragLeftEdge);
|
||||||
|
clearInterval(self.dragRightEdge);
|
||||||
|
clearInterval(self.dragTopEdge);
|
||||||
|
clearInterval(self.dragBottomEdge);
|
||||||
|
|
||||||
|
delete self.dragLeftEdge;
|
||||||
|
delete self.dragRightEdge;
|
||||||
|
delete self.dragTopEdge;
|
||||||
|
delete self.dragBottomEdge;
|
||||||
|
|
||||||
|
self.dragFlag = 0;
|
||||||
|
self.dragTolerance = 0;
|
||||||
|
|
||||||
if (JIT.tempInit && JIT.tempNode2 === null) {
|
if (JIT.tempInit && JIT.tempNode2 === null) {
|
||||||
// this means you want to add a new topic, and then a synapse
|
// this means you want to add a new topic, and then a synapse
|
||||||
|
@ -1002,7 +1070,44 @@ const JIT = {
|
||||||
Control.deselectAllNodes()
|
Control.deselectAllNodes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, // canvasClickHandler
|
}, // canvasClickHandler
|
||||||
|
updateTopicPositions: function (node, pos){
|
||||||
|
var len = Selected.Nodes.length;
|
||||||
|
var topic;
|
||||||
|
// this is used to send nodes that are moving to
|
||||||
|
// other realtime collaborators on the same map
|
||||||
|
var positionsToSend = {};
|
||||||
|
|
||||||
|
// first define offset for each node
|
||||||
|
var xOffset = []
|
||||||
|
var yOffset = []
|
||||||
|
for (var i = 0; i < len; i += 1) {
|
||||||
|
var n = Selected.Nodes[i]
|
||||||
|
xOffset[i] = n.pos.x - node.pos.x
|
||||||
|
yOffset[i] = n.pos.y - node.pos.y
|
||||||
|
} // for
|
||||||
|
|
||||||
|
for (var i = 0; i < len; i += 1) {
|
||||||
|
var n = Selected.Nodes[i]
|
||||||
|
var x = pos.x + xOffset[i]
|
||||||
|
var y = pos.y + yOffset[i]
|
||||||
|
n.pos.setc(x, y)
|
||||||
|
|
||||||
|
if (Active.Map) {
|
||||||
|
topic = n.getData('topic')
|
||||||
|
// we use the topic ID not the node id
|
||||||
|
// because we can't depend on the node id
|
||||||
|
// to be the same as on other collaborators
|
||||||
|
// maps
|
||||||
|
positionsToSend[topic.id] = n.pos
|
||||||
|
}
|
||||||
|
} // for
|
||||||
|
|
||||||
|
if (Active.Map) {
|
||||||
|
$(document).trigger(JIT.events.topicDrag, [positionsToSend])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
nodeDoubleClickHandler: function (node, e) {
|
nodeDoubleClickHandler: function (node, e) {
|
||||||
TopicCard.showCard(node)
|
TopicCard.showCard(node)
|
||||||
}, // nodeDoubleClickHandler
|
}, // nodeDoubleClickHandler
|
||||||
|
@ -1027,19 +1132,23 @@ const JIT = {
|
||||||
// 2 others are selected only and shift, so additionally select this one
|
// 2 others are selected only and shift, so additionally select this one
|
||||||
// 3 others are selected only, no shift: drag only this one
|
// 3 others are selected only, no shift: drag only this one
|
||||||
// 4 this node and others were selected, so drag them (just return false)
|
// 4 this node and others were selected, so drag them (just return false)
|
||||||
// return value: deselect node again after?
|
|
||||||
if (Selected.Nodes.length === 0) {
|
if (Selected.Nodes.length === 0) {
|
||||||
|
Control.selectNode(node, e)
|
||||||
return 'only-drag-this-one'
|
return 'only-drag-this-one'
|
||||||
}
|
}
|
||||||
if (Selected.Nodes.indexOf(node) === -1) {
|
if (Selected.Nodes.indexOf(node) === -1) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
Control.selectNode(node, e)
|
Control.selectNode(node, e)
|
||||||
return 'nothing'
|
return 'move-all-incuding-this-one'
|
||||||
} else {
|
} else {
|
||||||
|
Control.deselectAllEdges()
|
||||||
|
Control.deselectAllNodes()
|
||||||
|
Control.selectNode(node, e)
|
||||||
return 'only-drag-this-one'
|
return 'only-drag-this-one'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'nothing' // case 4?
|
return 'move-all'; // case 4?
|
||||||
|
|
||||||
}, // handleSelectionBeforeDragging
|
}, // handleSelectionBeforeDragging
|
||||||
getNodeXY: function (node) {
|
getNodeXY: function (node) {
|
||||||
if (typeof node.pos.x === 'number' && typeof node.pos.y === 'number') {
|
if (typeof node.pos.x === 'number' && typeof node.pos.y === 'number') {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Mobile from './Mobile'
|
||||||
import Realtime from './Realtime'
|
import Realtime from './Realtime'
|
||||||
import Selected from './Selected'
|
import Selected from './Selected'
|
||||||
import Topic from './Topic'
|
import Topic from './Topic'
|
||||||
|
import Util from './Util'
|
||||||
import Visualize from './Visualize'
|
import Visualize from './Visualize'
|
||||||
import { Search } from './GlobalUI'
|
import { Search } from './GlobalUI'
|
||||||
|
|
||||||
|
@ -108,16 +109,19 @@ const Listeners = {
|
||||||
})
|
})
|
||||||
|
|
||||||
$(window).resize(function () {
|
$(window).resize(function () {
|
||||||
if (Visualize && Visualize.mGraph) Visualize.mGraph.canvas.resize($(window).width(), $(window).height())
|
if (Visualize && Visualize.mGraph) {
|
||||||
|
Util.resizeCanvas(Visualize.mGraph.canvas)
|
||||||
|
}
|
||||||
|
|
||||||
if (Active.Map && Realtime.inConversation) Realtime.positionVideos()
|
if (Active.Map && Realtime.inConversation) Realtime.positionVideos()
|
||||||
Mobile.resizeTitle()
|
Mobile.resizeTitle()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
centerAndReveal: function(nodes, opts) {
|
centerAndReveal: function (nodes, opts) {
|
||||||
if (nodes.length < 1) return
|
if (nodes.length < 1) return
|
||||||
var node = nodes[nodes.length - 1]
|
var node = nodes[nodes.length - 1]
|
||||||
if (opts.center && opts.reveal) {
|
if (opts.center && opts.reveal) {
|
||||||
Topic.centerOn(node.id, function() {
|
Topic.centerOn(node.id, function () {
|
||||||
Topic.fetchRelatives(nodes)
|
Topic.fetchRelatives(nodes)
|
||||||
})
|
})
|
||||||
} else if (opts.center) {
|
} else if (opts.center) {
|
||||||
|
|
|
@ -1,54 +1,53 @@
|
||||||
/* EVENTS SENDABLE */
|
/* EVENTS SENDABLE */
|
||||||
export const REQUEST_LIVE_MAPS = 'REQUEST_LIVE_MAPS'
|
module.exports = {
|
||||||
export const JOIN_MAP = 'JOIN_MAP'
|
JOIN_MAP: 'JOIN_MAP',
|
||||||
export const LEAVE_MAP = 'LEAVE_MAP'
|
CHECK_FOR_CALL: 'CHECK_FOR_CALL',
|
||||||
export const CHECK_FOR_CALL = 'CHECK_FOR_CALL'
|
LEAVE_MAP: 'LEAVE_MAP',
|
||||||
export const ACCEPT_CALL = 'ACCEPT_CALL'
|
ACCEPT_CALL: 'ACCEPT_CALL',
|
||||||
export const DENY_CALL = 'DENY_CALL'
|
DENY_CALL: 'DENY_CALL',
|
||||||
export const DENY_INVITE = 'DENY_INVITE'
|
DENY_INVITE: 'DENY_INVITE',
|
||||||
export const INVITE_TO_JOIN = 'INVITE_TO_JOIN'
|
INVITE_TO_JOIN: 'INVITE_TO_JOIN',
|
||||||
export const INVITE_A_CALL = 'INVITE_A_CALL'
|
INVITE_A_CALL: 'INVITE_A_CALL',
|
||||||
export const JOIN_CALL = 'JOIN_CALL'
|
JOIN_CALL: 'JOIN_CALL',
|
||||||
export const LEAVE_CALL = 'LEAVE_CALL'
|
LEAVE_CALL: 'LEAVE_CALL',
|
||||||
export const SEND_MAPPER_INFO = 'SEND_MAPPER_INFO'
|
SEND_MAPPER_INFO: 'SEND_MAPPER_INFO',
|
||||||
export const SEND_COORDS = 'SEND_COORDS'
|
SEND_COORDS: 'SEND_COORDS',
|
||||||
export const CREATE_MESSAGE = 'CREATE_MESSAGE'
|
CREATE_MESSAGE: 'CREATE_MESSAGE',
|
||||||
export const DRAG_TOPIC = 'DRAG_TOPIC'
|
DRAG_TOPIC: 'DRAG_TOPIC',
|
||||||
export const CREATE_TOPIC = 'CREATE_TOPIC'
|
CREATE_TOPIC: 'CREATE_TOPIC',
|
||||||
export const UPDATE_TOPIC = 'UPDATE_TOPIC'
|
UPDATE_TOPIC: 'UPDATE_TOPIC',
|
||||||
export const REMOVE_TOPIC = 'REMOVE_TOPIC'
|
REMOVE_TOPIC: 'REMOVE_TOPIC',
|
||||||
export const DELETE_TOPIC = 'DELETE_TOPIC'
|
DELETE_TOPIC: 'DELETE_TOPIC',
|
||||||
export const CREATE_SYNAPSE = 'CREATE_SYNAPSE'
|
CREATE_SYNAPSE: 'CREATE_SYNAPSE',
|
||||||
export const UPDATE_SYNAPSE = 'UPDATE_SYNAPSE'
|
UPDATE_SYNAPSE: 'UPDATE_SYNAPSE',
|
||||||
export const REMOVE_SYNAPSE = 'REMOVE_SYNAPSE'
|
REMOVE_SYNAPSE: 'REMOVE_SYNAPSE',
|
||||||
export const DELETE_SYNAPSE = 'DELETE_SYNAPSE'
|
DELETE_SYNAPSE: 'DELETE_SYNAPSE',
|
||||||
export const UPDATE_MAP = 'UPDATE_MAP'
|
UPDATE_MAP: 'UPDATE_MAP',
|
||||||
|
|
||||||
/* EVENTS RECEIVABLE */
|
/* EVENTS RECEIVABLE */
|
||||||
export const INVITED_TO_CALL = 'INVITED_TO_CALL'
|
JUNTO_UPDATED: 'JUNTO_UPDATED',
|
||||||
export const INVITED_TO_JOIN = 'INVITED_TO_JOIN'
|
INVITED_TO_CALL: 'INVITED_TO_CALL',
|
||||||
export const CALL_ACCEPTED = 'CALL_ACCEPTED'
|
INVITED_TO_JOIN: 'INVITED_TO_JOIN',
|
||||||
export const CALL_DENIED = 'CALL_DENIED'
|
CALL_ACCEPTED: 'CALL_ACCEPTED',
|
||||||
export const INVITE_DENIED = 'INVITE_DENIED'
|
CALL_DENIED: 'CALL_DENIED',
|
||||||
export const CALL_IN_PROGRESS = 'CALL_IN_PROGRESS'
|
INVITE_DENIED: 'INVITE_DENIED',
|
||||||
export const CALL_STARTED = 'CALL_STARTED'
|
CALL_IN_PROGRESS: 'CALL_IN_PROGRESS',
|
||||||
export const MAPPER_JOINED_CALL = 'MAPPER_JOINED_CALL'
|
CALL_STARTED: 'CALL_STARTED',
|
||||||
export const MAPPER_LEFT_CALL = 'MAPPER_LEFT_CALL'
|
MAPPER_JOINED_CALL: 'MAPPER_JOINED_CALL',
|
||||||
export const MAPPER_LIST_UPDATED = 'MAPPER_LIST_UPDATED'
|
MAPPER_LEFT_CALL: 'MAPPER_LEFT_CALL',
|
||||||
export const NEW_MAPPER = 'NEW_MAPPER'
|
MAPPER_LIST_UPDATED: 'MAPPER_LIST_UPDATED',
|
||||||
export const LOST_MAPPER = 'LOST_MAPPER'
|
NEW_MAPPER: 'NEW_MAPPER',
|
||||||
export const MESSAGE_CREATED = 'MESSAGE_CREATED'
|
LOST_MAPPER: 'LOST_MAPPER',
|
||||||
export const TOPIC_DRAGGED = 'TOPIC_DRAGGED'
|
MESSAGE_CREATED: 'MESSAGE_CREATED',
|
||||||
export const TOPIC_CREATED = 'TOPIC_CREATED'
|
TOPIC_DRAGGED: 'TOPIC_DRAGGED',
|
||||||
export const TOPIC_UPDATED = 'TOPIC_UPDATED'
|
TOPIC_CREATED: 'TOPIC_CREATED',
|
||||||
export const TOPIC_REMOVED = 'TOPIC_REMOVED'
|
TOPIC_UPDATED: 'TOPIC_UPDATED',
|
||||||
export const TOPIC_DELETED = 'TOPIC_DELETED'
|
TOPIC_REMOVED: 'TOPIC_REMOVED',
|
||||||
export const SYNAPSE_CREATED = 'SYNAPSE_CREATED'
|
TOPIC_DELETED: 'TOPIC_DELETED',
|
||||||
export const SYNAPSE_UPDATED = 'SYNAPSE_UPDATED'
|
SYNAPSE_CREATED: 'SYNAPSE_CREATED',
|
||||||
export const SYNAPSE_REMOVED = 'SYNAPSE_REMOVED'
|
SYNAPSE_UPDATED: 'SYNAPSE_UPDATED',
|
||||||
export const SYNAPSE_DELETED = 'SYNAPSE_DELETED'
|
SYNAPSE_REMOVED: 'SYNAPSE_REMOVED',
|
||||||
export const PEER_COORDS_UPDATED = 'PEER_COORDS_UPDATED'
|
SYNAPSE_DELETED: 'SYNAPSE_DELETED',
|
||||||
export const MAP_UPDATED = 'MAP_UPDATED'
|
PEER_COORDS_UPDATED: 'PEER_COORDS_UPDATED',
|
||||||
export const LIVE_MAPS_RECEIVED = 'LIVE_MAPS_RECEIVED'
|
MAP_UPDATED: 'MAP_UPDATED'
|
||||||
export const MAP_WENT_LIVE = 'MAP_WENT_LIVE'
|
}
|
||||||
export const MAP_CEASED_LIVE = 'MAP_CEASED_LIVE'
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import Views from '../Views'
|
||||||
import Visualize from '../Visualize'
|
import Visualize from '../Visualize'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
JUNTO_UPDATED,
|
||||||
INVITED_TO_CALL,
|
INVITED_TO_CALL,
|
||||||
INVITED_TO_JOIN,
|
INVITED_TO_JOIN,
|
||||||
CALL_ACCEPTED,
|
CALL_ACCEPTED,
|
||||||
|
@ -34,9 +35,9 @@ import {
|
||||||
INVITE_DENIED,
|
INVITE_DENIED,
|
||||||
CALL_IN_PROGRESS,
|
CALL_IN_PROGRESS,
|
||||||
CALL_STARTED,
|
CALL_STARTED,
|
||||||
|
MAPPER_LIST_UPDATED,
|
||||||
MAPPER_JOINED_CALL,
|
MAPPER_JOINED_CALL,
|
||||||
MAPPER_LEFT_CALL,
|
MAPPER_LEFT_CALL,
|
||||||
MAPPER_LIST_UPDATED,
|
|
||||||
NEW_MAPPER,
|
NEW_MAPPER,
|
||||||
LOST_MAPPER,
|
LOST_MAPPER,
|
||||||
MESSAGE_CREATED,
|
MESSAGE_CREATED,
|
||||||
|
@ -50,13 +51,11 @@ import {
|
||||||
SYNAPSE_REMOVED,
|
SYNAPSE_REMOVED,
|
||||||
SYNAPSE_DELETED,
|
SYNAPSE_DELETED,
|
||||||
PEER_COORDS_UPDATED,
|
PEER_COORDS_UPDATED,
|
||||||
LIVE_MAPS_RECEIVED,
|
|
||||||
MAP_WENT_LIVE,
|
|
||||||
MAP_CEASED_LIVE,
|
|
||||||
MAP_UPDATED
|
MAP_UPDATED
|
||||||
} from './events'
|
} from './events'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
juntoUpdated,
|
||||||
invitedToCall,
|
invitedToCall,
|
||||||
invitedToJoin,
|
invitedToJoin,
|
||||||
callAccepted,
|
callAccepted,
|
||||||
|
@ -64,9 +63,9 @@ import {
|
||||||
inviteDenied,
|
inviteDenied,
|
||||||
callInProgress,
|
callInProgress,
|
||||||
callStarted,
|
callStarted,
|
||||||
|
mapperListUpdated,
|
||||||
mapperJoinedCall,
|
mapperJoinedCall,
|
||||||
mapperLeftCall,
|
mapperLeftCall,
|
||||||
mapperListUpdated,
|
|
||||||
peerCoordsUpdated,
|
peerCoordsUpdated,
|
||||||
newMapper,
|
newMapper,
|
||||||
lostMapper,
|
lostMapper,
|
||||||
|
@ -81,13 +80,9 @@ import {
|
||||||
synapseRemoved,
|
synapseRemoved,
|
||||||
synapseDeleted,
|
synapseDeleted,
|
||||||
mapUpdated,
|
mapUpdated,
|
||||||
liveMapsReceived,
|
|
||||||
mapWentLive,
|
|
||||||
mapCeasedLive
|
|
||||||
} from './receivable'
|
} from './receivable'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
requestLiveMaps,
|
|
||||||
joinMap,
|
joinMap,
|
||||||
leaveMap,
|
leaveMap,
|
||||||
checkForCall,
|
checkForCall,
|
||||||
|
@ -98,8 +93,8 @@ import {
|
||||||
inviteACall,
|
inviteACall,
|
||||||
joinCall,
|
joinCall,
|
||||||
leaveCall,
|
leaveCall,
|
||||||
sendMapperInfo,
|
|
||||||
sendCoords,
|
sendCoords,
|
||||||
|
sendMapperInfo,
|
||||||
createMessage,
|
createMessage,
|
||||||
dragTopic,
|
dragTopic,
|
||||||
createTopic,
|
createTopic,
|
||||||
|
@ -114,6 +109,7 @@ import {
|
||||||
} from './sendable'
|
} from './sendable'
|
||||||
|
|
||||||
let Realtime = {
|
let Realtime = {
|
||||||
|
juntoState: { connectedPeople: {}, liveMaps: {} },
|
||||||
videoId: 'video-wrapper',
|
videoId: 'video-wrapper',
|
||||||
socket: null,
|
socket: null,
|
||||||
webrtc: null,
|
webrtc: null,
|
||||||
|
@ -499,12 +495,11 @@ let Realtime = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendables = [
|
const sendables = [
|
||||||
['requestLiveMaps',requestLiveMaps],
|
|
||||||
['joinMap',joinMap],
|
['joinMap',joinMap],
|
||||||
['leaveMap',leaveMap],
|
['leaveMap',leaveMap],
|
||||||
['checkForCall',checkForCall],
|
['checkForCall',checkForCall],
|
||||||
['acceptCall',acceptCall],
|
['acceptCall',acceptCall],
|
||||||
['denyAll',denyCall],
|
['denyCall',denyCall],
|
||||||
['denyInvite',denyInvite],
|
['denyInvite',denyInvite],
|
||||||
['inviteToJoin',inviteToJoin],
|
['inviteToJoin',inviteToJoin],
|
||||||
['inviteACall',inviteACall],
|
['inviteACall',inviteACall],
|
||||||
|
@ -529,6 +524,7 @@ sendables.forEach(sendable => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const subscribeToEvents = (Realtime, socket) => {
|
const subscribeToEvents = (Realtime, socket) => {
|
||||||
|
socket.on(JUNTO_UPDATED, juntoUpdated(Realtime))
|
||||||
socket.on(INVITED_TO_CALL, invitedToCall(Realtime))
|
socket.on(INVITED_TO_CALL, invitedToCall(Realtime))
|
||||||
socket.on(INVITED_TO_JOIN, invitedToJoin(Realtime))
|
socket.on(INVITED_TO_JOIN, invitedToJoin(Realtime))
|
||||||
socket.on(CALL_ACCEPTED, callAccepted(Realtime))
|
socket.on(CALL_ACCEPTED, callAccepted(Realtime))
|
||||||
|
@ -536,9 +532,9 @@ const subscribeToEvents = (Realtime, socket) => {
|
||||||
socket.on(INVITE_DENIED, inviteDenied(Realtime))
|
socket.on(INVITE_DENIED, inviteDenied(Realtime))
|
||||||
socket.on(CALL_IN_PROGRESS, callInProgress(Realtime))
|
socket.on(CALL_IN_PROGRESS, callInProgress(Realtime))
|
||||||
socket.on(CALL_STARTED, callStarted(Realtime))
|
socket.on(CALL_STARTED, callStarted(Realtime))
|
||||||
|
socket.on(MAPPER_LIST_UPDATED, mapperListUpdated(Realtime))
|
||||||
socket.on(MAPPER_JOINED_CALL, mapperJoinedCall(Realtime))
|
socket.on(MAPPER_JOINED_CALL, mapperJoinedCall(Realtime))
|
||||||
socket.on(MAPPER_LEFT_CALL, mapperLeftCall(Realtime))
|
socket.on(MAPPER_LEFT_CALL, mapperLeftCall(Realtime))
|
||||||
socket.on(MAPPER_LIST_UPDATED, mapperListUpdated(Realtime))
|
|
||||||
socket.on(PEER_COORDS_UPDATED, peerCoordsUpdated(Realtime))
|
socket.on(PEER_COORDS_UPDATED, peerCoordsUpdated(Realtime))
|
||||||
socket.on(NEW_MAPPER, newMapper(Realtime))
|
socket.on(NEW_MAPPER, newMapper(Realtime))
|
||||||
socket.on(LOST_MAPPER, lostMapper(Realtime))
|
socket.on(LOST_MAPPER, lostMapper(Realtime))
|
||||||
|
@ -553,9 +549,6 @@ const subscribeToEvents = (Realtime, socket) => {
|
||||||
socket.on(SYNAPSE_REMOVED, synapseRemoved(Realtime))
|
socket.on(SYNAPSE_REMOVED, synapseRemoved(Realtime))
|
||||||
socket.on(SYNAPSE_DELETED, synapseDeleted(Realtime))
|
socket.on(SYNAPSE_DELETED, synapseDeleted(Realtime))
|
||||||
socket.on(MAP_UPDATED, mapUpdated(Realtime))
|
socket.on(MAP_UPDATED, mapUpdated(Realtime))
|
||||||
socket.on(LIVE_MAPS_RECEIVED, liveMapsReceived(Realtime))
|
|
||||||
socket.on(MAP_WENT_LIVE, mapWentLive(Realtime))
|
|
||||||
socket.on(MAP_CEASED_LIVE, mapCeasedLive(Realtime))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Realtime
|
export default Realtime
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
everthing in this file happens as a result of websocket events
|
everthing in this file happens as a result of websocket events
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { JUNTO_UPDATED } from './events'
|
||||||
|
|
||||||
import Active from '../Active'
|
import Active from '../Active'
|
||||||
import GlobalUI from '../GlobalUI'
|
import GlobalUI from '../GlobalUI'
|
||||||
import Control from '../Control'
|
import Control from '../Control'
|
||||||
|
@ -12,6 +16,11 @@ import Synapse from '../Synapse'
|
||||||
import Util from '../Util'
|
import Util from '../Util'
|
||||||
import Visualize from '../Visualize'
|
import Visualize from '../Visualize'
|
||||||
|
|
||||||
|
export const juntoUpdated = self => state => {
|
||||||
|
self.juntoState = state
|
||||||
|
$(document).trigger(JUNTO_UPDATED)
|
||||||
|
}
|
||||||
|
|
||||||
export const synapseRemoved = self => data => {
|
export const synapseRemoved = self => data => {
|
||||||
var synapse = Metamaps.Synapses.get(data.mappableid)
|
var synapse = Metamaps.Synapses.get(data.mappableid)
|
||||||
if (synapse) {
|
if (synapse) {
|
||||||
|
@ -239,13 +248,13 @@ export const lostMapper = self => data => {
|
||||||
export const mapperListUpdated = self => data => {
|
export const mapperListUpdated = self => data => {
|
||||||
// data.userid
|
// data.userid
|
||||||
// data.username
|
// data.username
|
||||||
// data.userimage
|
// data.avatar
|
||||||
|
|
||||||
self.mappersOnMap[data.userid] = {
|
self.mappersOnMap[data.userid] = {
|
||||||
id: data.userid,
|
id: data.userid,
|
||||||
name: data.username,
|
name: data.username,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
image: data.userimage,
|
image: data.avatar,
|
||||||
color: Util.getPastelColor(),
|
color: Util.getPastelColor(),
|
||||||
inConversation: data.userinconversation,
|
inConversation: data.userinconversation,
|
||||||
coords: {
|
coords: {
|
||||||
|
@ -259,14 +268,14 @@ export const mapperListUpdated = self => data => {
|
||||||
if (data.userinconversation) self.room.chat.mapperJoinedCall(data.userid)
|
if (data.userinconversation) self.room.chat.mapperJoinedCall(data.userid)
|
||||||
|
|
||||||
// create a div for the collaborators compass
|
// create a div for the collaborators compass
|
||||||
self.createCompass(data.username, data.userid, data.userimage, self.mappersOnMap[data.userid].color)
|
self.createCompass(data.username, data.userid, data.avatar, self.mappersOnMap[data.userid].color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const newMapper = self => data => {
|
export const newMapper = self => data => {
|
||||||
// data.userid
|
// data.userid
|
||||||
// data.username
|
// data.username
|
||||||
// data.userimage
|
// data.avatar
|
||||||
// data.coords
|
// data.coords
|
||||||
var firstOtherPerson = Object.keys(self.mappersOnMap).length === 0
|
var firstOtherPerson = Object.keys(self.mappersOnMap).length === 0
|
||||||
|
|
||||||
|
@ -274,13 +283,12 @@ export const newMapper = self => data => {
|
||||||
id: data.userid,
|
id: data.userid,
|
||||||
name: data.username,
|
name: data.username,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
image: data.userimage,
|
image: data.avatar,
|
||||||
color: Util.getPastelColor(),
|
color: Util.getPastelColor(),
|
||||||
realtime: true,
|
|
||||||
coords: {
|
coords: {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0
|
y: 0
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create an item for them in the realtime box
|
// create an item for them in the realtime box
|
||||||
|
@ -289,7 +297,7 @@ export const newMapper = self => data => {
|
||||||
self.room.chat.addParticipant(self.mappersOnMap[data.userid])
|
self.room.chat.addParticipant(self.mappersOnMap[data.userid])
|
||||||
|
|
||||||
// create a div for the collaborators compass
|
// create a div for the collaborators compass
|
||||||
self.createCompass(data.username, data.userid, data.userimage, self.mappersOnMap[data.userid].color)
|
self.createCompass(data.username, data.userid, data.avatar, self.mappersOnMap[data.userid].color)
|
||||||
|
|
||||||
var notifyMessage = data.username + ' just joined the map'
|
var notifyMessage = data.username + ' just joined the map'
|
||||||
if (firstOtherPerson) {
|
if (firstOtherPerson) {
|
||||||
|
@ -324,7 +332,7 @@ export const invitedToCall = self => inviter => {
|
||||||
self.soundId = self.room.chat.sound.play('sessioninvite')
|
self.soundId = self.room.chat.sound.play('sessioninvite')
|
||||||
|
|
||||||
var username = self.mappersOnMap[inviter].name
|
var username = self.mappersOnMap[inviter].name
|
||||||
var notifyText = '<img src="' + Metamaps.Erb['junto_spinner_darkgrey.gif'] + '" style="display: inline-block; margin-top: -12px; vertical-align: top;" />'
|
var notifyText = '<img src="' + Metamaps.Erb['junto_spinner_darkgrey.gif'] + '" style="display: inline-block; margin-top: -12px; margin-bottom: -6px; vertical-align: top;" />'
|
||||||
notifyText += username + ' is inviting you to a conversation. Join live?'
|
notifyText += username + ' is inviting you to a conversation. Join live?'
|
||||||
notifyText += ' <button type="button" class="toast-button button" onclick="Metamaps.Realtime.acceptCall(' + inviter + ')">Yes</button>'
|
notifyText += ' <button type="button" class="toast-button button" onclick="Metamaps.Realtime.acceptCall(' + inviter + ')">Yes</button>'
|
||||||
notifyText += ' <button type="button" class="toast-button button btn-no" onclick="Metamaps.Realtime.denyCall(' + inviter + ')">No</button>'
|
notifyText += ' <button type="button" class="toast-button button btn-no" onclick="Metamaps.Realtime.denyCall(' + inviter + ')">No</button>'
|
||||||
|
@ -391,6 +399,3 @@ export const callStarted = self => () => {
|
||||||
self.room.conversationInProgress()
|
self.room.conversationInProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const liveMapsReceived = self => () => {}
|
|
||||||
export const mapWentLive = self => () => {}
|
|
||||||
export const mapCeasedLive = self => () => {}
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import Active from '../Active'
|
||||||
import GlobalUI from '../GlobalUI'
|
import GlobalUI from '../GlobalUI'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REQUEST_LIVE_MAPS,
|
|
||||||
JOIN_MAP,
|
JOIN_MAP,
|
||||||
LEAVE_MAP,
|
LEAVE_MAP,
|
||||||
CHECK_FOR_CALL,
|
CHECK_FOR_CALL,
|
||||||
|
@ -28,15 +27,11 @@ import {
|
||||||
UPDATE_MAP
|
UPDATE_MAP
|
||||||
} from './events'
|
} from './events'
|
||||||
|
|
||||||
export const requestLiveMaps = self => () => {
|
|
||||||
self.socket.emit(REQUEST_LIVE_MAPS)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const joinMap = self => () => {
|
export const joinMap = self => () => {
|
||||||
self.socket.emit(JOIN_MAP, {
|
self.socket.emit(JOIN_MAP, {
|
||||||
userid: Active.Mapper.id,
|
userid: Active.Mapper.id,
|
||||||
username: Active.Mapper.get('name'),
|
username: Active.Mapper.get('name'),
|
||||||
userimage: Active.Mapper.get('image'),
|
avatar: Active.Mapper.get('image'),
|
||||||
mapid: Active.Map.id,
|
mapid: Active.Map.id,
|
||||||
map: Active.Map.attributes
|
map: Active.Map.attributes
|
||||||
})
|
})
|
||||||
|
@ -55,7 +50,7 @@ export const sendMapperInfo = self => userid => {
|
||||||
var update = {
|
var update = {
|
||||||
userToNotify: userid,
|
userToNotify: userid,
|
||||||
username: Active.Mapper.get('name'),
|
username: Active.Mapper.get('name'),
|
||||||
userimage: Active.Mapper.get('image'),
|
avatar: Active.Mapper.get('image'),
|
||||||
userid: Active.Mapper.id,
|
userid: Active.Mapper.id,
|
||||||
userinconversation: self.inConversation,
|
userinconversation: self.inConversation,
|
||||||
mapid: Active.Map.id
|
mapid: Active.Map.id
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
import { Parser, HtmlRenderer } from 'commonmark'
|
import { Parser, HtmlRenderer } from 'commonmark'
|
||||||
|
|
||||||
import Visualize from './Visualize'
|
import Visualize from './Visualize'
|
||||||
|
@ -138,6 +140,25 @@ const Util = {
|
||||||
// use safe: true to filter xss
|
// use safe: true to filter xss
|
||||||
return new HtmlRenderer({ safe: true })
|
return new HtmlRenderer({ safe: true })
|
||||||
.render(new Parser().parse(text))
|
.render(new Parser().parse(text))
|
||||||
|
},
|
||||||
|
logCanvasAttributes: function(canvas){
|
||||||
|
return {
|
||||||
|
scaleX: canvas.scaleOffsetX,
|
||||||
|
scaleY: canvas.scaleOffsetY,
|
||||||
|
centreCoords: Util.pixelsToCoords({ x: canvas.canvases[0].size.width / 2, y: canvas.canvases[0].size.height / 2 }),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
resizeCanvas: function(canvas){
|
||||||
|
// Store the current canvas attributes, i.e. scale and map-coordinate at the centre of the user's screen
|
||||||
|
const oldAttr = Util.logCanvasAttributes(canvas);
|
||||||
|
|
||||||
|
// Resize the canvas to fill the new window size. Based on how JIT works, this also resets the map back to scale 1 and tranlations = 0
|
||||||
|
canvas.resize($(window).width(), $(window).height())
|
||||||
|
|
||||||
|
// Return the map to the original scale, and then put the previous central map-coordinate back to the centre of user's newly resized screen
|
||||||
|
canvas.scale(oldAttr.scaleX, oldAttr.scaleY)
|
||||||
|
const newAttr = Util.logCanvasAttributes(canvas);
|
||||||
|
canvas.translate(newAttr.centreCoords.x - oldAttr.centreCoords.x, newAttr.centreCoords.y - oldAttr.centreCoords.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ var Private = {
|
||||||
var date = (m.timestamp.getMonth() + 1) + '/' + m.timestamp.getDate()
|
var date = (m.timestamp.getMonth() + 1) + '/' + m.timestamp.getDate()
|
||||||
date += ' ' + addZero(m.timestamp.getHours()) + ':' + addZero(m.timestamp.getMinutes())
|
date += ' ' + addZero(m.timestamp.getHours()) + ':' + addZero(m.timestamp.getMinutes())
|
||||||
m.timestamp = date
|
m.timestamp = date
|
||||||
m.image = m.user_image || 'http://www.hotpepper.ca/wp-content/uploads/2014/11/default_profile_1_200x200.png' // TODO: remove
|
m.image = m.user_image
|
||||||
m.message = linker.link(m.message)
|
m.message = linker.link(m.message)
|
||||||
var $html = $(this.messageTemplate(m))
|
var $html = $(this.messageTemplate(m))
|
||||||
this.$messages.append($html)
|
this.$messages.append($html)
|
||||||
|
|
|
@ -4,6 +4,8 @@ import React from 'react'
|
||||||
import ReactDOM from 'react-dom' // TODO ensure this isn't a double import
|
import ReactDOM from 'react-dom' // TODO ensure this isn't a double import
|
||||||
|
|
||||||
import Active from '../Active'
|
import Active from '../Active'
|
||||||
|
import GlobalUI from '../GlobalUI'
|
||||||
|
import Realtime from '../Realtime'
|
||||||
import Maps from '../../components/Maps'
|
import Maps from '../../components/Maps'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -11,6 +13,7 @@ import Maps from '../../components/Maps'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const ExploreMaps = {
|
const ExploreMaps = {
|
||||||
|
pending: false,
|
||||||
setCollection: function (collection) {
|
setCollection: function (collection) {
|
||||||
var self = ExploreMaps
|
var self = ExploreMaps
|
||||||
|
|
||||||
|
@ -27,58 +30,79 @@ const ExploreMaps = {
|
||||||
render: function (mapperObj, cb) {
|
render: function (mapperObj, cb) {
|
||||||
var self = ExploreMaps
|
var self = ExploreMaps
|
||||||
|
|
||||||
|
if (!self.collection) return
|
||||||
|
|
||||||
if (typeof mapperObj === 'function') {
|
if (typeof mapperObj === 'function') {
|
||||||
cb = mapperObj
|
cb = mapperObj
|
||||||
mapperObj = null
|
mapperObj = null
|
||||||
}
|
}
|
||||||
|
|
||||||
var exploreObj = {
|
var exploreObj = {
|
||||||
currentUser: Active.Mapper,
|
currentUser: Active.Mapper,
|
||||||
section: self.collection.id,
|
section: self.collection.id,
|
||||||
maps: self.collection,
|
maps: self.collection,
|
||||||
|
juntoState: Realtime.juntoState,
|
||||||
moreToLoad: self.collection.page != 'loadedAll',
|
moreToLoad: self.collection.page != 'loadedAll',
|
||||||
user: mapperObj,
|
user: mapperObj,
|
||||||
loadMore: self.loadMore
|
loadMore: self.loadMore,
|
||||||
|
pending: self.pending,
|
||||||
|
onStar: function (map) {
|
||||||
|
$.post('/maps/' + map.id + '/star')
|
||||||
|
map.set('star_count', map.get('star_count') + 1)
|
||||||
|
if (Metamaps.Stars) Metamaps.Stars.push({ user_id: Active.Mapper.id, map_id: map.id })
|
||||||
|
Metamaps.Maps.Starred.add(map)
|
||||||
|
GlobalUI.notifyUser('Map is now starred')
|
||||||
|
self.render()
|
||||||
|
},
|
||||||
|
onRequest: function (map) {
|
||||||
|
$.post({
|
||||||
|
url: `/maps/${map.id}/access_request`
|
||||||
|
})
|
||||||
|
GlobalUI.notifyUser('You will be notified by email if request accepted')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
React.createElement(Maps, exploreObj),
|
React.createElement(Maps, exploreObj),
|
||||||
document.getElementById('explore')
|
document.getElementById('explore')
|
||||||
)
|
).resize()
|
||||||
|
|
||||||
if (cb) cb()
|
if (cb) cb()
|
||||||
Metamaps.Loading.hide()
|
|
||||||
},
|
},
|
||||||
loadMore: function () {
|
loadMore: function () {
|
||||||
var self = ExploreMaps
|
var self = ExploreMaps
|
||||||
|
|
||||||
if (self.collection.page != "loadedAll") {
|
if (self.collection.page != "loadedAll") {
|
||||||
self.collection.getMaps()
|
self.collection.getMaps()
|
||||||
|
self.pending = true
|
||||||
}
|
}
|
||||||
else self.render()
|
self.render()
|
||||||
},
|
},
|
||||||
handleSuccess: function (cb) {
|
handleSuccess: function (cb) {
|
||||||
var self = ExploreMaps
|
var self = ExploreMaps
|
||||||
|
self.pending = false
|
||||||
if (self.collection && self.collection.id === 'mapper') {
|
if (self.collection && self.collection.id === 'mapper') {
|
||||||
self.fetchUserThenRender(cb)
|
self.fetchUserThenRender(cb)
|
||||||
} else {
|
} else {
|
||||||
self.render(cb)
|
self.render(cb)
|
||||||
|
Metamaps.Loading.hide()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleError: function () {
|
handleError: function () {
|
||||||
console.log('error loading maps!') // TODO
|
console.log('error loading maps!') // TODO
|
||||||
|
Metamaps.Loading.hide()
|
||||||
},
|
},
|
||||||
fetchUserThenRender: function (cb) {
|
fetchUserThenRender: function (cb) {
|
||||||
var self = ExploreMaps
|
var self = ExploreMaps
|
||||||
|
|
||||||
// first load the mapper object and then call the render function
|
// first load the mapper object and then call the render function
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/users/' + self.collection.mapperId + '/details.json',
|
url: '/users/' + self.collection.mapperId + '/details.json',
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
self.render(response, cb)
|
self.render(response, cb)
|
||||||
|
Metamaps.Loading.hide()
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
self.render(cb)
|
self.render(cb)
|
||||||
|
Metamaps.Loading.hide()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
import ExploreMaps from './ExploreMaps'
|
import ExploreMaps from './ExploreMaps'
|
||||||
import ChatView from './ChatView'
|
import ChatView from './ChatView'
|
||||||
import VideoView from './VideoView'
|
import VideoView from './VideoView'
|
||||||
import Room from './Room'
|
import Room from './Room'
|
||||||
|
import { JUNTO_UPDATED } from '../Realtime/events'
|
||||||
|
|
||||||
const Views = { ExploreMaps, ChatView, VideoView, Room }
|
const Views = {
|
||||||
|
init: () => {
|
||||||
|
$(document).on(JUNTO_UPDATED, () => ExploreMaps.render())
|
||||||
|
},
|
||||||
|
ExploreMaps,
|
||||||
|
ChatView,
|
||||||
|
VideoView,
|
||||||
|
Room
|
||||||
|
}
|
||||||
export default Views
|
export default Views
|
||||||
|
|
|
@ -88,7 +88,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
if (Metamaps.currentSection === 'explore') {
|
if (Metamaps.currentSection === 'explore') {
|
||||||
const capitalize = Metamaps.currentPage.charAt(0).toUpperCase() + Metamaps.currentPage.slice(1)
|
const capitalize = Metamaps.currentPage.charAt(0).toUpperCase() + Metamaps.currentPage.slice(1)
|
||||||
|
|
||||||
Metamaps.Views.ExploreMaps.setCollection(Metamaps.Maps[capitalize])
|
Views.ExploreMaps.setCollection(Metamaps.Maps[capitalize])
|
||||||
if (Metamaps.currentPage === 'mapper') {
|
if (Metamaps.currentPage === 'mapper') {
|
||||||
Views.ExploreMaps.fetchUserThenRender()
|
Views.ExploreMaps.fetchUserThenRender()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,8 +1,60 @@
|
||||||
import React, { Component, PropTypes } from 'react'
|
import React, { Component, PropTypes } from 'react'
|
||||||
|
import { find, values } from 'lodash'
|
||||||
|
|
||||||
|
const IN_CONVERSATION = 1 // shared with /realtime/reducer.js
|
||||||
|
|
||||||
|
const MapperList = (props) => {
|
||||||
|
return <ul className='mapperList'>
|
||||||
|
<li className='live'>LIVE</li>
|
||||||
|
{ props.mappers.map(mapper => <li key={ mapper.id } ><img src={ mapper.avatar } /><span>{ mapper.username }</span></li>) }
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
|
||||||
|
class Menu extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = { open: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.setState({ open: !this.state.open })
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
const { currentUser, map, onStar, onRequest } = this.props
|
||||||
|
const style = { display: this.state.open ? 'block' : 'none' }
|
||||||
|
|
||||||
|
return <div className='dropdownMenu'>
|
||||||
|
<div className='menuToggle' onClick={ this.toggle }>
|
||||||
|
<div className='circle'></div>
|
||||||
|
<div className='circle'></div>
|
||||||
|
<div className='circle'></div>
|
||||||
|
</div>
|
||||||
|
<ul className='menuItems' style={ style }>
|
||||||
|
<li className='star' onClick={ () => { this.toggle() && onStar(map) }}>Star Map</li>
|
||||||
|
{ !map.authorizeToEdit(currentUser) && <li className='request' onClick={ () => { this.toggle() && onRequest(map) }}>Request Access</li> }
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Menu.propTypes = {
|
||||||
|
currentUser: PropTypes.object.isRequired,
|
||||||
|
map: PropTypes.object.isRequired,
|
||||||
|
onStar: PropTypes.func.isRequired,
|
||||||
|
onRequest: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MapCard extends Component {
|
class MapCard extends Component {
|
||||||
render = () => {
|
render = () => {
|
||||||
const { map, currentUser } = this.props
|
const { map, juntoState, currentUser, onRequest, onStar } = this.props
|
||||||
|
|
||||||
|
const hasMap = juntoState.liveMaps[map.id]
|
||||||
|
const hasConversation = hasMap && find(values(hasMap), v => v === IN_CONVERSATION)
|
||||||
|
const hasMapper = hasMap && !hasConversation
|
||||||
|
const mapperList = hasMap && Object.keys(hasMap).map(id => juntoState.connectedPeople[id])
|
||||||
|
|
||||||
function capitalize (string) {
|
function capitalize (string) {
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||||
|
@ -19,49 +71,51 @@ class MapCard extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="map" id={ map.id }>
|
<div className="map" id={ map.id }>
|
||||||
<a href={ '/maps/' + map.id } data-router="true">
|
<div className={ 'permission ' + editPermission }>
|
||||||
<div className={ 'permission ' + editPermission }>
|
<div className='mapCard'>
|
||||||
<div className='mapCard'>
|
<div className='mainContent'>
|
||||||
<div className='mainContent'>
|
<div className='mapScreenshot'>
|
||||||
<div className='mapScreenshot'>
|
<img src={ map.get('screenshot_url') } />
|
||||||
<img src={ map.get('screenshot_url') } />
|
</div>
|
||||||
</div>
|
<div className='title' title={ map.get('name') }>
|
||||||
<div className='title' title={ map.get('name') }>
|
<div className='innerTitle'>{ truncatedName }</div>
|
||||||
<div className='innerTitle'>{ truncatedName }</div>
|
</div>
|
||||||
</div>
|
<div className='creatorAndPerm'>
|
||||||
<div className='creatorAndPerm'>
|
<img className='creatorImage' src={ map.get('user_image') } />
|
||||||
<img className='creatorImage' src={ map.get('user_image') } />
|
<span className='creatorName'>{ map.get('user_name') }</span>
|
||||||
<span className='creatorName'>{ map.get('user_name') }</span>
|
{ !map.authorizeToEdit(currentUser) && <div className='cardViewOnly'>View Only</div> }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mapMetadata">
|
<a className='mapMetadata' href={ '/maps/' + map.id } data-router="true">
|
||||||
<div className="metadataSection numContributors">
|
<div className="metadataSection numContributors">
|
||||||
{ map.get('contributor_count') }<br/>
|
{ map.get('contributor_count') }<br/>
|
||||||
{ map.get('contributor_count') === 1 ? 'contributor' : 'contributors' }
|
{ map.get('contributor_count') === 1 ? 'contributor' : 'contributors' }
|
||||||
</div>
|
</div>
|
||||||
<div className="metadataSection numTopics">
|
<div className="metadataSection numTopics">
|
||||||
{ map.get('topic_count') }<br/>
|
{ map.get('topic_count') }<br/>
|
||||||
{ map.get('topic_count') === 1 ? 'topic' : 'topics' }
|
{ map.get('topic_count') === 1 ? 'topic' : 'topics' }
|
||||||
</div>
|
</div>
|
||||||
<div className="metadataSection numStars">
|
<div className="metadataSection numStars">
|
||||||
{ map.get('star_count') }<br/>
|
{ map.get('star_count') }<br/>
|
||||||
{ map.get('star_count') === 1 ? 'star' : 'stars' }
|
{ map.get('star_count') === 1 ? 'star' : 'stars' }
|
||||||
</div>
|
</div>
|
||||||
<div className="metadataSection numSynapses">
|
<div className="metadataSection numSynapses">
|
||||||
{ map.get('synapse_count') }<br/>
|
{ map.get('synapse_count') }<br/>
|
||||||
{ map.get('synapse_count') === 1 ? 'synapse' : 'synapses' }
|
{ map.get('synapse_count') === 1 ? 'synapse' : 'synapses' }
|
||||||
</div>
|
</div>
|
||||||
<div className="clearfloat"></div>
|
<div className="clearfloat"></div>
|
||||||
<div className="scroll">
|
<div className="scroll">
|
||||||
<div className="desc">
|
<div className="desc">
|
||||||
{ truncatedDesc }
|
{ truncatedDesc }
|
||||||
<div className="clearfloat"></div>
|
<div className="clearfloat"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
</div>
|
{ hasMapper && <div className='mapHasMapper'><MapperList mappers={ mapperList } /></div> }
|
||||||
</div>
|
{ hasConversation && <div className='mapHasConversation'><MapperList mappers={ mapperList } /></div> }
|
||||||
</a>
|
{ currentUser && <Menu currentUser={ currentUser } map={ map } onStar= { onStar } onRequest={ onRequest } /> }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +123,10 @@ class MapCard extends Component {
|
||||||
|
|
||||||
MapCard.propTypes = {
|
MapCard.propTypes = {
|
||||||
map: PropTypes.object.isRequired,
|
map: PropTypes.object.isRequired,
|
||||||
currentUser: PropTypes.object
|
juntoState: PropTypes.object,
|
||||||
|
currentUser: PropTypes.object,
|
||||||
|
onStar: PropTypes.func.isRequired,
|
||||||
|
onRequest: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MapCard
|
export default MapCard
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { Component, PropTypes } from 'react'
|
import React, { Component, PropTypes } from 'react'
|
||||||
|
import { throttle } from 'lodash'
|
||||||
import Header from './Header'
|
import Header from './Header'
|
||||||
import MapperCard from './MapperCard'
|
import MapperCard from './MapperCard'
|
||||||
import MapCard from './MapCard'
|
import MapCard from './MapCard'
|
||||||
|
@ -15,19 +16,10 @@ class Maps extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
window && window.addEventListener('resize', this.resize)
|
window && window.addEventListener('resize', this.resize)
|
||||||
|
this.refs.maps.addEventListener('scroll', throttle(this.scroll, 500, { leading: true, trailing: false }))
|
||||||
this.resize()
|
this.resize()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(oldProps) {
|
|
||||||
const { maps, user, currentUser } = this.props
|
|
||||||
const oldMaps = oldProps.maps
|
|
||||||
const oldUser = oldProps.user
|
|
||||||
const oldCurrentUser = oldProps.currentUser
|
|
||||||
const numCards = maps.length + (user || currentUser ? 1 : 0)
|
|
||||||
const oldNumCards = oldMaps.length + (oldUser || oldCurrentUser ? 1 : 0)
|
|
||||||
if (numCards !== oldNumCards) this.resize()
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window && window.removeEventListener('resize', this.resize)
|
window && window.removeEventListener('resize', this.resize)
|
||||||
}
|
}
|
||||||
|
@ -40,22 +32,26 @@ class Maps extends Component {
|
||||||
this.setState({ mapsWidth })
|
this.setState({ mapsWidth })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scroll = () => {
|
||||||
|
const { loadMore, moreToLoad, pending } = this.props
|
||||||
|
const { maps } = this.refs
|
||||||
|
if (moreToLoad && !pending && maps.scrollTop + maps.offsetHeight > maps.scrollHeight - 300 ) {
|
||||||
|
loadMore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
const { maps, currentUser, section, user, moreToLoad, loadMore } = this.props
|
const { maps, currentUser, juntoState, section, user, moreToLoad, loadMore, onStar, onRequest } = this.props
|
||||||
const style = { width: this.state.mapsWidth + 'px' }
|
const style = { width: this.state.mapsWidth + 'px' }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div id='exploreMaps'>
|
<div id='exploreMaps' ref='maps'>
|
||||||
<div style={ style }>
|
<div style={ style }>
|
||||||
{ user ? <MapperCard user={ user } /> : null }
|
{ user ? <MapperCard user={ user } /> : null }
|
||||||
{ currentUser && !user ? <div className="map newMap"><a href="/maps/new"><div className="newMapImage"></div><span>Create new map...</span></a></div> : null }
|
{ currentUser && !user ? <div className="map newMap"><a href="/maps/new"><div className="newMapImage"></div><span>Create new map...</span></a></div> : null }
|
||||||
{ maps.models.map(map => <MapCard key={ map.id } map={ map } currentUser={ currentUser } />) }
|
{ maps.models.map(map => <MapCard key={ map.id } map={ map } juntoState={ juntoState } currentUser={ currentUser } onStar={ onStar } onRequest={ onRequest } />) }
|
||||||
<div className='clearfloat'></div>
|
<div className='clearfloat'></div>
|
||||||
{!moreToLoad ? null : [
|
|
||||||
<button className="button loadMore" onClick={ loadMore }>load more</button>,
|
|
||||||
<div className='clearfloat'></div>
|
|
||||||
]}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Header signedIn={ !!currentUser }
|
<Header signedIn={ !!currentUser }
|
||||||
|
@ -70,10 +66,14 @@ class Maps extends Component {
|
||||||
Maps.propTypes = {
|
Maps.propTypes = {
|
||||||
section: PropTypes.string.isRequired,
|
section: PropTypes.string.isRequired,
|
||||||
maps: PropTypes.object.isRequired,
|
maps: PropTypes.object.isRequired,
|
||||||
|
juntoState: PropTypes.object.isRequired,
|
||||||
moreToLoad: PropTypes.bool.isRequired,
|
moreToLoad: PropTypes.bool.isRequired,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
currentUser: PropTypes.object,
|
currentUser: PropTypes.object,
|
||||||
loadMore: PropTypes.func
|
loadMore: PropTypes.func,
|
||||||
|
pending: PropTypes.bool.isRequired,
|
||||||
|
onStar: PropTypes.func.isRequired,
|
||||||
|
onRequest: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Maps
|
export default Maps
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"react": "15.3.2",
|
"react": "15.3.2",
|
||||||
"react-dom": "15.3.2",
|
"react-dom": "15.3.2",
|
||||||
"react-dropzone": "3.6.0",
|
"react-dropzone": "3.6.0",
|
||||||
|
"redux": "^3.6.0",
|
||||||
"simplewebrtc": "2.2.0",
|
"simplewebrtc": "2.2.0",
|
||||||
"socket.io": "1.3.7",
|
"socket.io": "1.3.7",
|
||||||
"underscore": "1.4.4",
|
"underscore": "1.4.4",
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
|
const {
|
||||||
import {
|
|
||||||
// server sendable, client receivable
|
// server sendable, client receivable
|
||||||
TOPIC_UPDATED,
|
TOPIC_UPDATED,
|
||||||
TOPIC_DELETED,
|
TOPIC_DELETED,
|
||||||
SYNAPSE_UPDATED,
|
SYNAPSE_UPDATED,
|
||||||
SYNAPSE_DELETED,
|
SYNAPSE_DELETED,
|
||||||
LIVE_MAPS_RECEIVED,
|
|
||||||
MAP_WENT_LIVE,
|
|
||||||
MAP_CEASED_LIVE,
|
|
||||||
MAP_UPDATED,
|
MAP_UPDATED,
|
||||||
|
JUNTO_UPDATED,
|
||||||
|
|
||||||
// server receivable, client sendable
|
// server receivable, client sendable
|
||||||
REQUEST_LIVE_MAPS,
|
JOIN_CALL,
|
||||||
|
LEAVE_CALL,
|
||||||
JOIN_MAP,
|
JOIN_MAP,
|
||||||
LEAVE_MAP,
|
LEAVE_MAP,
|
||||||
UPDATE_TOPIC,
|
UPDATE_TOPIC,
|
||||||
|
@ -19,43 +17,23 @@ import {
|
||||||
UPDATE_SYNAPSE,
|
UPDATE_SYNAPSE,
|
||||||
DELETE_SYNAPSE,
|
DELETE_SYNAPSE,
|
||||||
UPDATE_MAP
|
UPDATE_MAP
|
||||||
} from '../frontend/src/Metamaps/Realtime/events'
|
} = require('../frontend/src/Metamaps/Realtime/events')
|
||||||
|
|
||||||
const adjustAndBroadcast = (io, socket, state, event, data) => {
|
module.exports = function (io, store) {
|
||||||
if (event === JOIN_MAP) {
|
store.subscribe(() => {
|
||||||
if (!state.liveMaps[data.mapid]) {
|
console.log(store.getState())
|
||||||
state.liveMaps[data.mapid] = data.map // { name: '', desc: '', numTopics: '' }
|
io.sockets.emit(JUNTO_UPDATED, store.getState())
|
||||||
state.liveMaps[data.mapid].mapper_count = 1
|
})
|
||||||
io.sockets.emit(MAP_WENT_LIVE, state.liveMaps[data.mapid])
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
state.liveMaps[data.mapid].mapper_count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (event === LEAVE_MAP) {
|
|
||||||
const mapid = socket.mapid
|
|
||||||
if (state.liveMaps[mapid] && state.liveMaps[mapid].mapper_count == 1) {
|
|
||||||
delete state.liveMaps[mapid]
|
|
||||||
io.sockets.emit(MAP_CEASED_LIVE, { id: mapid })
|
|
||||||
}
|
|
||||||
else if (state.liveMaps[mapid]) {
|
|
||||||
state.liveMaps[mapid].mapper_count--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function (io, state) {
|
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
|
|
||||||
socket.on(REQUEST_LIVE_MAPS, function (activeUser) {
|
io.sockets.emit(JUNTO_UPDATED, store.getState())
|
||||||
//constrain response to maps visible to user
|
|
||||||
var maps = Object.keys(state.liveMaps).map(function(key) { return state.liveMaps[key] })
|
|
||||||
socket.emit(LIVE_MAPS_RECEIVED, maps)
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on(JOIN_MAP, data => adjustAndBroadcast(io, socket, state, JOIN_MAP, data))
|
socket.on(JOIN_MAP, data => store.dispatch({ type: JOIN_MAP, payload: data }))
|
||||||
socket.on(LEAVE_MAP, () => adjustAndBroadcast(io, socket, state, LEAVE_MAP))
|
socket.on(LEAVE_MAP, () => store.dispatch({ type: LEAVE_MAP, payload: socket }))
|
||||||
socket.on('disconnect', () => adjustAndBroadcast(io, socket, state, LEAVE_MAP))
|
socket.on(JOIN_CALL, data => store.dispatch({ type: JOIN_CALL, payload: data }))
|
||||||
|
socket.on(LEAVE_CALL, () => store.dispatch({ type: LEAVE_CALL, payload: socket }))
|
||||||
|
socket.on('disconnect', () => store.dispatch({ type: 'DISCONNECT', payload: socket }))
|
||||||
|
|
||||||
socket.on(UPDATE_TOPIC, function (data) {
|
socket.on(UPDATE_TOPIC, function (data) {
|
||||||
socket.broadcast.emit(TOPIC_UPDATED, data)
|
socket.broadcast.emit(TOPIC_UPDATED, data)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {
|
const {
|
||||||
INVITED_TO_CALL,
|
INVITED_TO_CALL,
|
||||||
INVITED_TO_JOIN,
|
INVITED_TO_JOIN,
|
||||||
CALL_ACCEPTED,
|
CALL_ACCEPTED,
|
||||||
|
@ -17,11 +17,11 @@ import {
|
||||||
INVITE_A_CALL,
|
INVITE_A_CALL,
|
||||||
JOIN_CALL,
|
JOIN_CALL,
|
||||||
LEAVE_CALL
|
LEAVE_CALL
|
||||||
} from '../frontend/src/Metamaps/Realtime/events'
|
} = require('../frontend/src/Metamaps/Realtime/events')
|
||||||
|
|
||||||
const { mapRoom, userMapRoom } = require('./rooms')
|
const { mapRoom, userMapRoom } = require('./rooms')
|
||||||
|
|
||||||
module.exports = function (io, state) {
|
module.exports = function (io, store) {
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
|
|
||||||
socket.on(CHECK_FOR_CALL, function (data) {
|
socket.on(CHECK_FOR_CALL, function (data) {
|
||||||
|
@ -39,6 +39,8 @@ module.exports = function (io, state) {
|
||||||
|
|
||||||
socket.on(ACCEPT_CALL, function (data) {
|
socket.on(ACCEPT_CALL, function (data) {
|
||||||
socket.broadcast.in(userMapRoom(data.inviter, data.mapid)).emit(CALL_ACCEPTED, data.invited)
|
socket.broadcast.in(userMapRoom(data.inviter, data.mapid)).emit(CALL_ACCEPTED, data.invited)
|
||||||
|
// convert this so that it broadcasts to all sockets and includes the map id
|
||||||
|
// and who's participating
|
||||||
socket.broadcast.in(mapRoom(data.mapid)).emit(CALL_STARTED)
|
socket.broadcast.in(mapRoom(data.mapid)).emit(CALL_STARTED)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -51,12 +53,15 @@ module.exports = function (io, state) {
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on(JOIN_CALL, function (data) {
|
socket.on(JOIN_CALL, function (data) {
|
||||||
|
// convert this so that it broadcasts to all sockets and includes the map id
|
||||||
|
// and info about who joined
|
||||||
socket.broadcast.in(mapRoom(data.mapid)).emit(MAPPER_JOINED_CALL, data.id)
|
socket.broadcast.in(mapRoom(data.mapid)).emit(MAPPER_JOINED_CALL, data.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on(LEAVE_CALL, function (data) {
|
socket.on(LEAVE_CALL, function (data) {
|
||||||
|
// convert this so that it broadcasts to all sockets and includes the map id
|
||||||
|
// and info about who joined
|
||||||
socket.broadcast.in(mapRoom(data.mapid)).emit(MAPPER_LEFT_CALL, data.id)
|
socket.broadcast.in(mapRoom(data.mapid)).emit(MAPPER_LEFT_CALL, data.id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
const {
|
||||||
import {
|
|
||||||
MAPPER_LIST_UPDATED,
|
MAPPER_LIST_UPDATED,
|
||||||
NEW_MAPPER,
|
NEW_MAPPER,
|
||||||
LOST_MAPPER,
|
LOST_MAPPER,
|
||||||
|
@ -13,19 +12,19 @@ import {
|
||||||
|
|
||||||
JOIN_MAP,
|
JOIN_MAP,
|
||||||
LEAVE_MAP,
|
LEAVE_MAP,
|
||||||
SEND_MAPPER_INFO,
|
|
||||||
SEND_COORDS,
|
SEND_COORDS,
|
||||||
|
SEND_MAPPER_INFO,
|
||||||
CREATE_MESSAGE,
|
CREATE_MESSAGE,
|
||||||
DRAG_TOPIC,
|
DRAG_TOPIC,
|
||||||
CREATE_TOPIC,
|
CREATE_TOPIC,
|
||||||
REMOVE_TOPIC,
|
REMOVE_TOPIC,
|
||||||
CREATE_SYNAPSE,
|
CREATE_SYNAPSE,
|
||||||
REMOVE_SYNAPSE
|
REMOVE_SYNAPSE
|
||||||
} from '../frontend/src/Metamaps/Realtime/events'
|
} = require('../frontend/src/Metamaps/Realtime/events')
|
||||||
|
|
||||||
const { mapRoom, userMapRoom } = require('./rooms')
|
const { mapRoom, userMapRoom } = require('./rooms')
|
||||||
|
|
||||||
module.exports = function (io, state) {
|
module.exports = function (io, store) {
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
|
|
||||||
// this will ping everyone on a map that there's a person just joined the map
|
// this will ping everyone on a map that there's a person just joined the map
|
||||||
|
@ -33,11 +32,11 @@ module.exports = function (io, state) {
|
||||||
socket.mapid = data.mapid
|
socket.mapid = data.mapid
|
||||||
socket.userid = data.userid
|
socket.userid = data.userid
|
||||||
socket.username = data.username
|
socket.username = data.username
|
||||||
socket.userimage = data.userimage
|
socket.avatar = data.avatar
|
||||||
var newUser = {
|
var newUser = {
|
||||||
userid: data.userid,
|
userid: data.userid,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
userimage: data.userimage
|
avatar: data.avatar
|
||||||
}
|
}
|
||||||
socket.join(mapRoom(data.mapid))
|
socket.join(mapRoom(data.mapid))
|
||||||
socket.join(userMapRoom(data.userid, data.mapid))
|
socket.join(userMapRoom(data.userid, data.mapid))
|
||||||
|
@ -63,7 +62,7 @@ module.exports = function (io, state) {
|
||||||
userid: data.userid,
|
userid: data.userid,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
userinconversation: data.userinconversation,
|
userinconversation: data.userinconversation,
|
||||||
userimage: data.userimage
|
avatar: data.avatar
|
||||||
}
|
}
|
||||||
socket.broadcast.in(userMapRoom(data.userToNotify, data.mapid)).emit(MAPPER_LIST_UPDATED, existingUser)
|
socket.broadcast.in(userMapRoom(data.userToNotify, data.mapid)).emit(MAPPER_LIST_UPDATED, existingUser)
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,13 +6,14 @@ map = require('./map'),
|
||||||
global = require('./global'),
|
global = require('./global'),
|
||||||
stunservers = [{"url": "stun:stun.l.google.com:19302"}]
|
stunservers = [{"url": "stun:stun.l.google.com:19302"}]
|
||||||
|
|
||||||
var state = {
|
const { createStore } = require('redux')
|
||||||
connectedPeople: {},
|
const reducer = require('./reducer')
|
||||||
liveMaps: {}
|
|
||||||
}
|
|
||||||
signalling(io, stunservers, state)
|
|
||||||
junto(io, state)
|
|
||||||
map(io, state)
|
|
||||||
global(io, state)
|
|
||||||
io.listen(5001)
|
|
||||||
|
|
||||||
|
let store = createStore(reducer)
|
||||||
|
|
||||||
|
global(io, store)
|
||||||
|
signalling(io, stunservers, store)
|
||||||
|
junto(io, store)
|
||||||
|
map(io, store)
|
||||||
|
|
||||||
|
io.listen(5001)
|
||||||
|
|
75
realtime/reducer.js
Normal file
75
realtime/reducer.js
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
const { omit, omitBy, isNil, mapValues } = require('lodash')
|
||||||
|
const {
|
||||||
|
JOIN_MAP,
|
||||||
|
LEAVE_MAP,
|
||||||
|
JOIN_CALL,
|
||||||
|
LEAVE_CALL
|
||||||
|
} = require('../frontend/src/Metamaps/Realtime/events')
|
||||||
|
|
||||||
|
const NOT_IN_CONVERSATION = 0
|
||||||
|
const IN_CONVERSATION = 1
|
||||||
|
|
||||||
|
const addMapperToMap = (map, userId) => { return Object.assign({}, map, { [userId]: NOT_IN_CONVERSATION })}
|
||||||
|
|
||||||
|
const reducer = (state = { connectedPeople: {}, liveMaps: {} }, action) => {
|
||||||
|
const { type, payload } = action
|
||||||
|
const { connectedPeople, liveMaps } = state
|
||||||
|
const map = payload && liveMaps[payload.mapid]
|
||||||
|
const mapWillEmpty = map && Object.keys(map).length === 1
|
||||||
|
const callWillFinish = map && (type === LEAVE_CALL || type === 'DISCONNECT') && Object.keys(map).length === 2
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case JOIN_MAP:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
connectedPeople: Object.assign({}, connectedPeople, {
|
||||||
|
[payload.userid]: {
|
||||||
|
id: payload.userid,
|
||||||
|
username: payload.username,
|
||||||
|
avatar: payload.avatar
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
liveMaps: Object.assign({}, liveMaps, {
|
||||||
|
[payload.mapid]: addMapperToMap(map || {}, payload.userid)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
case LEAVE_MAP:
|
||||||
|
// if the map will empty, remove it from liveMaps, if the map will not empty, just remove the mapper
|
||||||
|
const newLiveMaps = mapWillEmpty
|
||||||
|
? omit(liveMaps, payload.mapid)
|
||||||
|
: Object.assign({}, liveMaps, { [payload.mapid]: omit(map, payload.userid) })
|
||||||
|
|
||||||
|
return {
|
||||||
|
connectedPeople: omit(connectedPeople, payload.userid),
|
||||||
|
liveMaps: omitBy(newLiveMaps, isNil)
|
||||||
|
}
|
||||||
|
case JOIN_CALL:
|
||||||
|
// update the user (payload.id is user id) in the given map to be marked in the conversation
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
liveMaps: Object.assign({}, liveMaps, {
|
||||||
|
[payload.mapid]: Object.assign({}, map, {
|
||||||
|
[payload.id]: IN_CONVERSATION
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
case LEAVE_CALL:
|
||||||
|
const newMap = callWillFinish
|
||||||
|
? mapValues(map, () => NOT_IN_CONVERSATION)
|
||||||
|
: Object.assign({}, map, { [payload.userid]: NOT_IN_CONVERSATION })
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
liveMaps: Object.assign({}, liveMaps, { map: newMap })
|
||||||
|
})
|
||||||
|
case 'DISCONNECT':
|
||||||
|
const mapWithoutUser = omit(map, payload.userid)
|
||||||
|
const newMapWithoutUser = callWillFinish ? mapValues(mapWithoutUser, () => NOT_IN_CONVERSATION) : mapWithoutUser
|
||||||
|
const newLiveMapsWithoutUser = mapWillEmpty ? omit(liveMaps, payload.mapid) : Object.assign({}, liveMaps, { [payload.mapid]: newMapWithoutUser })
|
||||||
|
return {
|
||||||
|
connectedPeople: omit(connectedPeople, payload.userid),
|
||||||
|
liveMaps: omitBy(newLiveMapsWithoutUser, isNil)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = reducer
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mapRoom: mapId => `maps/${mapId}`,
|
mapRoom: mapId => `maps/${mapId}`,
|
||||||
userMapRoom: (mapperId, mapId) => `mappers/${mapperId}/maps/${mapId}`,
|
userMapRoom: (mapperId, mapId) => `mappers/${mapperId}/maps/${mapId}`,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var uuid = require('node-uuid')
|
const uuid = require('node-uuid')
|
||||||
|
|
||||||
// based off of https://github.com/andyet/signalmaster
|
// based off of https://github.com/andyet/signalmaster
|
||||||
// since it was updated to socket.io 1.3.7
|
// since it was updated to socket.io 1.3.7
|
||||||
|
@ -12,7 +12,6 @@ function safeCb(cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(io, stunservers, state) {
|
module.exports = function(io, stunservers, state) {
|
||||||
|
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
socket.resources = {
|
socket.resources = {
|
||||||
screen: false,
|
screen: false,
|
||||||
|
|
Loading…
Add table
Reference in a new issue