import { gt3dutil } from "./util.js"
import { rkWebUtil } from "./rkwebutil/rkwebutil.js"
import { gt3d } from "./gt3d.js"
import { gt3dObject } from "./object.js"
import { gt3dMap } from "./map.js"
import { gt3dMapLayer } from "./layer.js"
import { gt3dFolderList } from "./folder.js"
import { gt3dImageView } from "./imageview.js"
// ROB! Find the thing that uses this, and move
// the connection to the controller?
import { gt3dapdir } from "./config.js"
// I use this so that I can import *just* this function
// to other modules, to avoid circular dependencies.
var gt3dUIFactory = function( parent ) {
return new gt3dUI( parent );
}
// This handles rendering the UI. It doesn't maintain any state, but
// gets all of that from its parent gt3d object.
var gt3dUI = function( parent )
{
var self = this;
this.parent = parent;
var body = document.body;
rkWebUtil.wipeDiv( body );
this.topdiv = rkWebUtil.elemaker( "div", body,
{ "classes": [ "gt3dtopdiv", "gt3dtopdivuncollapsed" ] } );
this.leftdiv = rkWebUtil.elemaker( "div", this.topdiv,
{ "classes": [ "gt3dleftcol", "gt3dleftcoluncollapsed" ] } );
this.midcol = rkWebUtil.elemaker( "div", this.topdiv,
{ "classes": [ "gt3dmidcol", "gt3dmidcoluncollapsed" ] } );
this.viewwrap = rkWebUtil.elemaker( "div", this.midcol,
{ "classes": [ "gt3dviewwrap" ] } );
this.viewdiv = rkWebUtil.elemaker( "div", this.viewwrap,
{ "classes": [ "gt3dview" ] } );
this.bottomdiv = rkWebUtil.elemaker( "div", this.midcol,
{ "classes": [ "gt3dbottomrow", "gt3dbottomrowuncollapsed" ] } );
this.rightdiv = rkWebUtil.elemaker( "div", this.topdiv,
{ "classes": [ "gt3drightcol", "gt3drightcoluncollapsed" ]} );
this.layoutLeftCol();
this.layoutRightCol()
this.layoutBottomRow();
this.layoutView( null );
this.leftcollapsed = false;
this.rightcollapsed = false;
this.bottomcollapsed = false;
this.collapseleft = rkWebUtil.button( this.viewwrap, "−", function(e) { self.collapse( "left" ) } );
this.collapseleft.classList.add( "collapseleft" );
this.collapseright = rkWebUtil.button( this.viewwrap, "−", function(e) { self.collapse( "right" ) } );
this.collapseright.classList.add( "collapseright" );
this.collapsebottom = rkWebUtil.button( this.viewwrap, "−", function(e) { self.collapse( "bottom" ) } );
this.collapsebottom.classList.add( "collapsebottom" );
this.map = null;
this.parent.addMapListener( this );
this.parent.addLayerListener( this );
}
// **********************************************************************
gt3dUI.prototype.collapse = function( which ) {
if ( which == 'left' ) this.leftcollapsed = ! this.leftcollapsed;
else if ( which == 'right' ) this.rightcollapsed = ! this.rightcollapsed;
else if ( which == 'bottom' ) this.bottomcollapsed = ! this.bottomcollapsed;
else {
console.log( "Unknown collapse " + which );
return;
}
console.log( "leftcollapsed = " + this.leftcollapsed +
"; rightcollapsed = " + this.rightcollapsed +
"; bottomcollapsed = " + this.bottomcollapsed );
if ( this.leftcollapsed && this.rightcollapsed ) {
this.topdiv.className = 'gt3dtopdiv gt3dtopdivbothcollapsed';
this.leftdiv.className = 'gt3dleftcol gt3dleftcolcollapsed';
this.rightdiv.className = 'gt3drightcol gt3drightcolcollapsed';
this.collapseleft.value = '+';
this.collapseright.value = '+';
}
else if ( this.leftcollapsed ) {
this.topdiv.className = 'gt3dtopdiv gt3dtopdivleftcollapsed';
this.leftdiv.className = 'gt3dleftcol gt3dleftcolcollapsed';
this.rightdiv.className = 'gt3drightcol gt3drightcoluncollapsed';
this.collapseleft.value = '+';
this.collapseright.value = '−';
}
else if ( this.rightcollapsed ) {
this.topdiv.className = 'gt3dtopdiv gt3dtopdivrightcollapsed';
this.leftdiv.className = 'gt3dleftcol gt3dleftcoluncollapsed';
this.rightdiv.className = 'gt3drightcol gt3drightcolcollapsed';
this.collapseleft.value = '−';
this.collapseright.value = '+';
}
else {
this.topdiv.className = 'gt3dtopdiv gt3dtopdivuncollapsed';
this.leftdiv.className = 'gt3dleftcol gt3dleftcoluncollapsed';
this.rightdiv.className = 'gt3drightcol gt3drightcoluncollapsed';
this.collapseleft.value = '−';
this.collapseright.value = '−';
}
if ( this.bottomcollapsed ) {
this.midcol.className = 'gt3dmidcol gt3dmidcolcollapsed';
this.bottomdiv.className = 'gt3dbottomrow gt3dbottomrowcollapsed';
this.collapsebottom.value = '+';
}
else {
this.midcol.className = 'gt3dmidcol gt3dmidcoluncollapsed';
this.bottomdiv.className = 'gt3dbottomrow gt3dbottomrowuncollapsed';
this.collapsebottom.value = '−';
}
// I really don't think this should be necessary; the resize observer
// should take care of it. But, either due to a bug I haven't found
// in my code, or a bug in (at least) firefox, sometimes the view div
// wasn't getting resized properly when the css of the parent elements
// was changed.
// if ( this.map != null ) {
// this.map.view.windowResized( undefined );
// }
}
// **********************************************************************
// A function for tabbed divs.
//
// divlist should be a list of structures with:
// text : the thing to match which to
// div : the div to show ( display: flex ) or hide ( display: none )
// button : the button to highlight (class sel) or unhighlight (class none)
gt3dUI.prototype.showMetaDiv = function( event, divlist, which )
{
for ( let div of divlist ) {
if ( div.text == which ) {
div.button.classList.remove( "unsel" );
div.button.classList.add( "sel" );
div.div.style.display = "flex";
} else {
div.button.classList.remove( "sel" );
div.button.classList.add( "unsel" );
div.div.style.display = "none";
}
}
}
// **********************************************************************
gt3dUI.prototype.layoutLeftCol = function()
{
var hdrdiv, div, subdiv, button, img, texts, images, classes;
var self=this;
rkWebUtil.wipeDiv( this.leftdiv );
this.mapmetadivdivs = [];
var mapmetadivlist = [ "maps", "layers" ];
texts = { "maps": "Maps", "layers": "Layers" };
images = { "maps": "img/map.svg", "layers": "img/layers.svg" };
// classes = { "maps": "maps", "layers": "layers" };
this.mapmetadiv = rkWebUtil.elemaker( "div", this.leftdiv, { "attributes": { "class": "mapmeta" } } );
hdrdiv = rkWebUtil.elemaker( "div", this.mapmetadiv, { "attributes": { "class": "fullwidheader hbox" } } );
div = rkWebUtil.elemaker( "div", hdrdiv, { "attributes": { "class": "hboxleft" } } );
for ( let text of mapmetadivlist ) {
button = rkWebUtil.elemaker( "button", div,
{ "click": function( e ) { self.showMetaDiv( e, self.mapmetadivdivs, text ); },
"attributes": { "class": "unsel" } } );
img = rkWebUtil.elemaker( "img", button,
{ "attributes": { "src": gt3dapdir + images[text],
"alt": texts[text] } } );
img.style.height = "1.5em";
button.classList.add( "tooltipcontainer" );
subdiv = rkWebUtil.elemaker( "div", button, { "text": texts[text], "attributes": { "class": "tooltip" } } );
subdiv = rkWebUtil.elemaker( "div", this.mapmetadiv, { "attributes": { "class": "mapmetasub" } } );
subdiv.style.display = "none";
this.mapmetadivdivs.push( { "div": subdiv,
"text": text,
"button": button } );
if ( text == "maps" )
this.maplist = new gt3dUI.MapList( this, this.parent, subdiv );
else if ( text == "layers" )
this.layerlist = new gt3dUI.LayerList( this, this.parent, subdiv );
}
this.showMetaDiv( null, this.mapmetadivdivs, "maps" );
div = rkWebUtil.elemaker( "div", hdrdiv, { "attributes": { "class": "hboxright" } } );
button = rkWebUtil.elemaker( "button", div, { "text": "New" } );
this.new_map_layer_div = rkWebUtil.popupMenu(
[ 'New Map', 'New Layer', '-', 'Cancel' ],
{ 'New Layer': function(e) { self.promptCreateLayer(); },
'New Map': function(e) { self.promptCreateMap(); } },
[ "popup", "vbox" ], null, [], [ "marginhalfex" ] );
button.addEventListener( "click",
function( e ) {
self.new_map_layer_div.style.left = ( e.pageX - 10 ) + "px";
self.new_map_layer_div.style.top = ( e.pageY - 10 ) + "px";
self.new_map_layer_div.style.visibility = "visible"; } );
this.foldermetadivdivs = [];
var foldermetadivlist = [ "folders", "maptokens", "mapobjects" ];
texts = { "folders": "Folders",
"maptokens": "Map Tokens",
"mapobjects": "Map Objects",
};
images = { "folders": "img/folder.svg",
"maptokens": "img/pawn_filled.svg",
"mapobjects": "img/cube.svg",
};
this.foldermetadiv = rkWebUtil.elemaker( "div", this.leftdiv, { "attributes": { "class": "foldermeta" } } );
hdrdiv = rkWebUtil.elemaker( "div", this.foldermetadiv, { "classes": [ "fullwidheader", "hbox" ] } );
div = rkWebUtil.elemaker( "div", hdrdiv, { "classes": [ "hboxleft" ] } );
for (let text of foldermetadivlist) {
button = rkWebUtil.elemaker( "button", div,
{ "click": function( e ) { self.showMetaDiv( e, self.foldermetadivdivs, text ); },
"attributes": { "class": "unsel" } } );
img = rkWebUtil.elemaker( "img", button,
{ "attributes":
{ "src": gt3dapdir + images[ text ],
"alt": texts[ text ]
}
});
img.style.height = "1.5em";
button.classList.add( "tooltipcontainer" );
subdiv = rkWebUtil.elemaker( "div", button,
{ "text": texts[ text ],
"attributes": { "class": "tooltip" } } );
subdiv = rkWebUtil.elemaker( "div", this.foldermetadiv, { "attributes": { "class": "foldermetasub" } } );
subdiv.style.display = "none";
this.foldermetadivdivs.push( { "div": subdiv,
"text": text,
"button": button } );
if ( text == "folders" ) {
this.folderlist = new gt3dFolderList( this, subdiv );
} else {
/****/
rkWebUtil.elemaker( "p", subdiv, { "text": text + " div." } );
/****/
}
}
// this.tokenlist = new gt3dUI.TokenList( this, this.parent, this.foldermetadivdivs );
// Others
this.showMetaDiv( null, this.foldermetadivdivs, "folders" );
}
// **********************************************************************
gt3dUI.prototype.layoutRightCol = function()
{
var div, subdiv, texts, images, button, img, hbox, hdrdiv, subdiv;
var self = this;
rkWebUtil.wipeDiv( this.rightdiv );
this.usermetadivdivs = [];
var usermetadivlist = [ "users", "turnorder" ];
texts = { "users": "Users", "turnorder": "Turn Order" };
images = { "users": "img/users.svg", "turnorder": "img/stopwatch.svg" };
this.usermetadiv = rkWebUtil.elemaker( "div", this.rightdiv, { "classes": [ "usersmeta" ] } );
hdrdiv = rkWebUtil.elemaker( "div", this.usermetadiv, { "classes": [ "fullwidheader", "hbox" ] } );
div = rkWebUtil.elemaker( "div", hdrdiv, { "attributes": { "class": "hboxleft" } } );
for ( let text of usermetadivlist ) {
button = rkWebUtil.elemaker( "button", div,
{ "click": function( e ) { self.showMetaDiv( e, self.usermetadivdivs, text ); },
"classes": [ "unsel", "tooltipcontainer" ] } );
img = rkWebUtil.elemaker( "img", button,
{ "attributes": { "src": gt3dapdir + images[text],
"alt": texts[text] } } );
img.style.height = "1.5em";
rkWebUtil.elemaker( "div", button, { "text": texts[text], "classes": [ "tooltip" ] } );
subdiv = rkWebUtil.elemaker( "div", this.usermetadiv, { "classes": [ "usermetasub" ] } );
this.usermetadivdivs.push( { "div": subdiv,
"text": text,
"button": button } );
if ( text == "users" ) {
this.userlist = new gt3dUI.UserList( this, this.parent, subdiv );
hbox = rkWebUtil.elemaker( "div", subdiv, { "classes": [ "hbox", "margintopauto", "fullwidth" ] } );
if ( this.parent.isgm ) {
rkWebUtil.button( hbox, "Add New Players", function( e ) { window.alert( "Dunno how." ) } );
}
button = rkWebUtil.button( hbox, "Exit", function( e ) { self.parent.logout() } );
button.classList.add( "flex-end" );
}
else {
rkWebUtil.elemaker( "div", subdiv, { "text": "Initiative tracker" } );
}
}
this.showMetaDiv( null, this.usermetadivdivs, "users" );
// Rob, think: this next thing may get called an extra time because of listeners
this.userlist.userListUpdated();
// ========================================
this.chatmetadiv = rkWebUtil.elemaker( "div", this.rightdiv, { "attributes": { "class": "chatmeta" } } );
div = rkWebUtil.elemaker( "div", this.chatmetadiv, { "attributes": { "class": "fullwidheader" } } );
div.appendChild( document.createTextNode( "Chat window" ) )
}
// **********************************************************************
gt3dUI.prototype.layoutBottomRow = function()
{
rkWebUtil.wipeDiv( this.bottomdiv );
this.bottomedit = rkWebUtil.elemaker( "div", this.bottomdiv, { "text": "Edit div",
"attributes": { "class": "gt3dbottomedit" } } );
this.bottomedit.style.display = "none";
this.bottominfo = rkWebUtil.elemaker( "div", this.bottomdiv, { "text": "Info div",
"attributes": { "class": "gt3dbottominfo" } } );
this.bottominfo.style.display = "block";
}
// **********************************************************************
/* This should only be called after the other columns are laid out */
gt3dUI.prototype.layoutView = function()
{
var self = this;
rkWebUtil.wipeDiv( this.viewdiv );
if ( this.map == null ) {
var img = rkWebUtil.elemaker( "div", this.viewdiv );
img.style["background-image"] = "url(" + gt3dapdir + "img/cat.svg)";
img.style["background-repeat"] = "no-repeat";
img.style["background-size"] = "contain";
img.style["background-position"] = "center";
img.style.width = "100%";
img.style.height = "100%";
return;
}
if ( this.map != null ) {
console.log( "Attaching mapview " + this.map.id_ + " to this.viewdiv" );
this.map.view.attach( this.viewdiv );
this.map.view.start();
this.viewdiv.addEventListener( "dragenter", function (e) { e.preventDefault() } );
this.viewdiv.addEventListener( "dragover", function (e) { e.preventDefault() } );
this.viewdiv.addEventListener( "drop", function(e) { self.map.view.dragDrop( e ) } );
}
}
// **********************************************************************
gt3dUI.prototype.mapListUpdated = function( parent ) {
this.maplist.mapListUpdated();
if ( this.map != null ) {
if ( ! ( this.map.id_ in parent.maps ) ) {
this.map.cleanup();
this.map = null;
this.layoutView();
} else {
this.maplist.map_lis[ this.map.id_ ].classList.add( "current" );
}
}
}
// **********************************************************************
gt3dUI.prototype.setCurrentMap = function( parent, forceredraw )
{
if ( ( ! forceredraw ) && ( this.map !=null ) && ( this.map.id_ == parent.currentmap ) )
return;
if ( this.map != null ) this.map.cleanup();
this.maplist.mapListUpdated( );
if ( parent.currentmap == null ) {
this.map = null;
} else {
this.map = new gt3dMap( parent.maps[parent.currentmap], parent, this );
this.maplist.map_lis[ parent.currentmap ].classList.add( "selected" );
this.layerlist.layerListUpdated();
if ( ( this.map.layers != null )
&& ( parent.currentmap != null )
&& ( parent.maps[ parent.currentmap ].currentlayer != null ) ) {
this.setCurrentLayer( this.id_, parent.maps[ parent.currentmap ].currentlayer );
}
}
this.layoutView();
}
// **********************************************************************
gt3dUI.prototype.promptCreateMap = function()
{
var hbox, vbox, subbox, subhbox;
var self = this;
this.bottominfo.style.display = "none";
this.bottomedit.style.display = "block";
rkWebUtil.wipeDiv( this.bottomedit );
hbox = rkWebUtil.elemaker( "div", this.bottomedit, { "attributes" : { "class": "hbox" } } );
vbox = rkWebUtil.elemaker( "div", hbox, { "attributes": { "class": "vbox marginr1ex" } } );
rkWebUtil.elemaker( "h4", vbox, { "text": "Create New Map" } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "Name: ",
"attributes": { "class": "hbox marginhalfex" } } );
this.newmapname = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "text",
"width": 32,
"value": "New Map" } } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "Size of map in cells:",
"attributes": { "class": "hbox marginhalfex" } } );
subbox = rkWebUtil.elemaker( "div", vbox, { "attributes": { "class": "hbox marginhalfex" } } );
subbox.appendChild( document.createTextNode( "x: " ) );
this.newmapxsize = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "number",
"size": 4,
"value": 33 } } );
subbox.appendChild( document.createTextNode( " y: " ) );
this.newmapysize = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "number",
"size": 4,
"value": 33 } } );
vbox = rkWebUtil.elemaker( "div", hbox, { "attributes": { "class": "vbox marginl1ex marginr1ex" } } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "One cell = ",
"attributes": { "class": "hbox marginhalfex" } } );
this.newmapworldfac = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "number",
"step": "any",
"size": 4,
"value": 1.0 } } );
subbox.appendChild( document.createTextNode( " world units." ) );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "Grid type: ",
"attributes": { "class": "hbox marginhalfex" } } );
this.newmapgridtype = rkWebUtil.elemaker( "select", subbox );
rkWebUtil.elemaker( "option", this.newmapgridtype,
{ "text": "No Grid", "attributes": { "value": "Nogrid" } } );
rkWebUtil.elemaker( "option", this.newmapgridtype,
{ "text": "Squares", "attributes": { "value": "Squares" } } );
rkWebUtil.elemaker( "option", this.newmapgridtype,
{ "text": "Horiz. Hexes", "attributes": { "value": "HHex" } } );
rkWebUtil.elemaker( "option", this.newmapgridtype,
{ "text": "Vert. Hexes", "attributes": { "value": "VHex" } } );
rkWebUtil.elemaker( "div", vbox, { "text": "Distance calculation: " } );
subbox = rkWebUtil.elemaker( "div", vbox, { "attributes": { "class": "hbox marginhalfex" } } );
this.newmapdistalg = rkWebUtil.elemaker( "select", subbox );
rkWebUtil.elemaker( "option", this.newmapdistalg,
{ "text": "Euclidean", "attributes": { "value": "Euclidean" } } );
rkWebUtil.elemaker( "option", this.newmapdistalg,
{ "text": "√2=1.5", "attributes": { "value": "√2=1.5" } } );
rkWebUtil.elemaker( "option", this.newmapdistalg,
{ "text": "Count Squares", "attributes": { "value": "Count Squares" } } );
rkWebUtil.elemaker( "option", this.newmapdistalg,
{ "text": "Count Hexes", "attributes": { "value": "Count Hexes" } } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "Visible to Players? ",
"attributes": { "class": "hbox marginhalfex" } } );
this.newmapplayervisible = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "checkbox" } } );
vbox = rkWebUtil.elemaker( "div", hbox, { "attributes": { "class": "vbox marginl1ex" } } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "Bkgd. Color: ",
"attributes": { "class": "hbox marginhalfex" } } );
this.newmapbgcolor = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "color",
"value": "#60c0ff" } } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "Layer Color: ",
"attributes": { "class": "hbox marginhalfex" } } );
this.newmaplayercolor = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "color",
"value": "#10a040" } } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "text": "Grid Color: ",
"attributes": { "class": "hbox marginhalfex" } } );
this.newmapgridcolor = rkWebUtil.elemaker( "input", subbox,
{ "attributes": { "type": "color",
"value": "#202020" } } );
subbox = rkWebUtil.elemaker( "div", vbox,
{ "attributes": { "class": "hbox marginhalfex" } } );
subhbox = rkWebUtil.elemaker( "div", subbox,
{ "attributes": { "class": "hbox" } } );
rkWebUtil.elemaker( "button", subhbox,
{ "text": "Create New Map",
"attributes": { "class": "marginr1ex" },
"click": function( e ) { self.submitCreateNewMap( e ); } } );
rkWebUtil.elemaker( "button", subhbox,
{ "text": "Cancel",
"click": function( e ) {
self.bottominfo.style.display = "block";
self.bottomedit.style.display = "none";
rkWebUtil.wipeDiv( self.bottomedit );
} } );
}
// **********************************************************************
gt3dUI.prototype.submitCreateNewMap = function( event )
{
var data = {
"table": this.parent.table.id_,
"name": this.newmapname.value,
"xsize": this.newmapxsize.value,
"ysize": this.newmapysize.value,
"worldfac": this.newmapworldfac.value,
"gridtype": this.newmapgridtype.value,
"distalg": this.newmapdistalg.value,
"bgcolor": this.newmapbgcolor.value,
"layercolor": this.newmaplayercolor.value,
"gridcolor": this.newmapgridcolor.value,
"playervisible": this.newmapplayervisible.checked
}
this.parent.createNewMap( data );
}
// **********************************************************************
gt3dUI.prototype.newLayer = function( parent, mapid, layerid ) {
window.alert( "OMG I don't know what to do with newLayer yet." );
}
// **********************************************************************
gt3dUI.prototype.layerListUpdated = function( parent, mapid ) {
if ( ( this.map == null ) || ( this.map.id_ != mapid ) ) return;
this.layerlist.layerListUpdated();
this.map.refreshAllLayers();
}
// **********************************************************************
gt3dUI.prototype.modifyLayer = function( mapid, layerid ) {
if ( ( this.map == null ) || ( this.map.id_ != mapid ) ) return;
this.layerlist.layerListUpdated();
}
// **********************************************************************
// Don't call this directly, this should only be called as the listener
// to gt3d. Call gt3d.setCurrentLayer
gt3dUI.prototype.setCurrentLayer = function( mapid, layerid ) {
if ( mapid == parent.currentmap ) {
this.map.setCurrentLayer( layerid );
this.layerlist.setCurrentLayer( layerid );
}
}
// **********************************************************************
// Don't do anything here; the layer object itself is
// listening and will do things.
gt3dUI.prototype.tokenOnLayer = function( tokenlayer ) {
}
// **********************************************************************
gt3dUI.prototype.promptCreateLayer = function() {
if ( this.parent.currentmap == null ) {
window.alert( "Select a map to make a new layer." );
return;
}
var map = this.parent.maps[ this.parent.currentmap ];
var layer = new gt3d.layer( this.parent,{ mapid: map.id_ } );
new gt3dMapLayer.editor( null, layer, map, this, true );
}
// **********************************************************************
// **********************************************************************
// **********************************************************************
gt3dUI.MapList = function( parentui, parentcontroller, parentdiv )
{
var self = this;
this.parentui = parentui;
this.controller = parentcontroller;
this.parentdiv = parentdiv
var div;
var self = this;
rkWebUtil.elemaker( "h4", parentdiv, { "text": "Maps" } );
this.mapsdiv = rkWebUtil.elemaker( "div", parentdiv, { "attributes": { "class": "maps" } } );
this.maplist = rkWebUtil.elemaker( "ul", this.mapsdiv, { "attributes": { "class": "maplist" } } );
this.map_lis = {};
}
// **********************************************************************
// ROB you still have to indicate player visible or not for maps
gt3dUI.MapList.prototype.mapListUpdated = function( )
{
var li;
var self = this;
rkWebUtil.wipeDiv( this.maplist );
this.map_lis = {};
var maporder = []
for ( let m in self.controller.maps ) maporder.push( m )
maporder.sort( function( m1, m2 ) {
if ( self.controller.maps[m1].ordinal < self.controller.maps[m2].ordinal ) return -1;
else if ( self.controller.maps[m1].ordinal > self.controller.maps[m2].ordinal ) return 1;
else return 0;
} );
for ( let mo of maporder ) {
let m = self.controller.maps[mo];
if ( this.controller.isgm || m.allplayervisible
|| m.playervisible.includes( this.controller.user.id_ ) ) {
li = rkWebUtil.elemaker( "li", this.maplist,
{ "text": m.name,
"click": function( e ) { self.clickedOnMap( e, m.id_ ) },
"attributes": { "id": m.id_ } } );
this.map_lis[ m.id_ ] = li;
}
}
}
// **********************************************************************
gt3dUI.MapList.prototype.clickedOnMap = function( e, mapid ) {
var div, vbox, hbox, uids, button, p;
let self = this;
let mapobj = this.controller.maps[ mapid ];
this.infodisplayedmapid = mapid;
rkWebUtil.wipeDiv( this.parentui.bottominfo );
div = rkWebUtil.elemaker( "div", this.parentui.bottominfo, { "classes": [ "hbox" ] } );
vbox = rkWebUtil.elemaker( "div", div, { "classes": [ "vboxtop", "margin1ex", "grow" ] } )
rkWebUtil.elemaker( "h4", vbox, { "text": "Map: " + mapobj.name } );
if ( this.controller.isgm ) {
hbox = rkWebUtil.elemaker( "div", vbox, { "classees": [ "hbox", "marginb1ex" ] } );
this.newmapname = rkWebUtil.elemaker( "input", hbox,
{ "attributes": { "type": "text",
"width": 42,
"value": mapobj.name } } );
this.newmapname.addEventListener( "keyup", function( ev ) { self.checkMapName( ev ); } );
rkWebUtil.button( hbox, "Change", function() { self.checkMapName( null ); } );
hbox = rkWebUtil.elemaker( "div", vbox, { "classes": [ "hbox" ] } );
this.allplayersseemap = rkWebUtil.elemaker( "input", hbox,
{ "attributes": { "type": "checkbox",
"id_": "allplayersseemapcheckbox" }
} );
rkWebUtil.elemaker( "label", hbox, { "text": "All players see map",
"attributes": { "for": "allplayersseemapcheckbox" } } );
if ( mapobj.allplayervisible )
this.allplayersseemap.setAttribute( "checked", "checked" );
this.allplayersseemap.addEventListener( "change",
function() { window.alert( "I should change this now." ); } );
button = rkWebUtil.button( vbox, "Move All Players Here",
function(e) { window.alert( "Dunno how." ) } );
button.classList.add( "marginb1ex" );
button.classList.add( "margint1ex" );
button = rkWebUtil.button( vbox, "Move Players & GMs Here",
function(e) { window.alert( "Dunno how." ) } );
}
var usersort = function( uid1, uid2 ) {
let ui = self.controller.users[ uid1 ];
let u2 = self.controller.users[ uid2 ];
if ( u1.isloggedin && ( ! u2.isloggedin ) ) return -1;
else if ( ( ! u1.isloggedin ) && u2.isloggedin ) return 1;
else {
if ( u1.displayname < u2.displayname ) return -1;
else if ( u1.displayname > u2.displayname ) return 1;
else return 0;
}
};
vbox = rkWebUtil.elemaker( "div", div, { "classes": [ "vboxtop", "margin1ex", "grow" ] } );
rkWebUtil.elemaker( "span", vbox, { "text": "Visible to:", "classes": [ "bold" ] } );
if ( mapobj.allplayervisible ) {
rkWebUtil.elemaker( "br", p );
rkWebUtil.elemaker( "b", p, { "text": "All Players" } );
}
uids = [...mapobj.playervisible];
uids.sort( usersort );
for ( let uid in uids ) {
rkWebUtil.elemaker( "br", p );
rkWebUtil.elemaker( "span", p, { "text": this.controller.users[uid].username } );
}
vbox = rkWebUtil.elemaker( "div", div, { "classes": [ "vboxtop", "margin1ex", "grow" ] } );
rkWebUtil.elemaker( "span", vbox, { "text": "Players on map:", "classes": [ "bold" ] } );
uids = [];
for ( let uid in this.controller.users ) {
if ( ( this.controller.users[uid].currentmap == mapid )
||
( uid == this.controller.user.id_ ) )
uids.push( uid )
}
uids.sort( usersort );
for ( let uid of uids ) {
rkWebUtil.elemaker( "br", vbox );
rkWebUtil.elemaker( "span", vbox, { "text": this.controller.users[uid].username } );
}
if ( mapid != this.controller.currentmap ) {
this.controller.makeMapCurrent( mapid );
}
rkWebUtil.elemaker( "div", this.parentui.bottominfo, { "text": 'id: ' + mapid,
"classes": [ "hboxright", "smallfadetext",
"margintopauto" ] } );
}
// **********************************************************************
gt3dUI.MapList.prototype.checkMapName = function( ev ) {
if ( ( ev != null ) && ( ev.key != "Enter" ) ) return;
if ( this.controller.maps[this.infodisplayedmapid].name != this.newmapname.value ) {
window.alert( "I need to rename map " + mapid + " from " +
this.controller.maps[this.infodisplayedmapid].name +
" to " + this.newmapname.value );
}
}
// **********************************************************************
// **********************************************************************
// **********************************************************************
gt3dUI.LayerList = function( parentui, parentcontroller, parentdiv )
{
var self = this;
this.parentui = parentui;
this.controller = parentcontroller;
this.parentdiv = parentdiv;
rkWebUtil.elemaker( "h4", parentdiv, { "text": "Map Layers" } );
this.layersdiv = rkWebUtil.elemaker( "div", this.parentdiv, { "attributes": { "class": "maps" } } );
this.layerlist = rkWebUtil.elemaker( "ul", this.layersdiv, { "attributes": { "class": "layerlist" } } );
// parentui is doing the listening for me
// this.controller.addLayerListener( self );
}
// **********************************************************************
// ROB you still have to indicate player visible or not for GMs
gt3dUI.LayerList.prototype.layerListUpdated = function( )
{
var self = this;
if ( this.parentui.map == null ) return;
this.layerwithinfoshown = null;
var layers = this.parentui.map.layers;
rkWebUtil.wipeDiv( this.layerlist );
if ( layers == null ) return;
this.layerlielems = {};
var layerorder = [];
for ( let o in layers ) layerorder.push( o );
layerorder.sort( function( lo1, lo2 ) {
let l1 = layers[ lo1 ];
let l2 = layers[ lo2 ];
if ( l1.layer.ordinal < l2.layer.ordinal ) return -1;
else if ( l1.layer.ordinal > l2.layer.ordinal ) return 1;
else {
if ( Math.abs( l1.layer.defheight - l2.layer.defheight < 0.001 ) ) {
if ( l1.layer.name < l2.layer.name ) return -1;
else if ( l1.layer.name = l2.layer.name ) return 1;
else return 0;
}
else if ( l1.layer.defheight < l2.layer.defheight ) return -1;
else if ( l1.layer.defheight = l2.layer.defneight ) return 1;
else return 0;
}
} );
for ( let lo of layerorder ) {
let l = layers[lo];
// I don't have enough confidence in my understasnding of
// javascript scoping rules to know that this will do the right
// thing. I *want* it to pass self.controller.maps... at
// the time the click event is generated. I *fear* it may pass
// what self.controller.amp.id_ has right now as I create
// the li element.
this.layerlielems[l.layer.id_] = rkWebUtil.elemaker( "li", this.layerlist,
{ "text": l.layer.name,
"click": function() { self.clickedOnLayer( l ) },
"attributes": { "id": l.layer.id_ } } );
if ( ! l.layer.isshown ) this.layerlielems[l.layer.id_].classList.add( "italic" );
this.layerlielems[l.layer.id_].addEventListener( 'dblclick', function() { self.showHideLayer( l ) } );
}
}
// **********************************************************************
// Don't call this directly. CAll gt3d.setCurrentLayer
gt3dUI.LayerList.prototype.setCurrentLayer = function( layerid ) {
for ( let lid in this.layerlielems ) {
let lay = this.layerlielems[lid];
lay.classList.remove( "selected" );
if ( lid == layerid ) {
lay.classList.add( "selected" );
}
}
}
// **********************************************************************
gt3dUI.LayerList.prototype.showHideLayer = function( layer ) {
layer.layer.isshown = ! layer.layer.isshown;
if ( this.layerwithinfoshown == layer.id_ )
this.showhidelayerbutton.value = ( layer.layer.isshown ? "Hide" : "Show" );
if ( layer.layer.isshown )
this.layerlielems[ layer.id_ ].classList.remove( "italic" );
else
this.layerlielems[ layer.id_ ].classList.add( "italic" );
// ROB TODO -- actually change the view to show or hide the layer!!!!
console.log( "NEED TO CHANGE THE VIEW" );
}
// **********************************************************************
gt3dUI.LayerList.prototype.clickedOnLayer = function( layer ) {
let maps = this.controller.maps;
let map = maps[ this.controller.currentmap ];
this.controller.setCurrentLayer( map.id_, layer.id_ );
this.showLayerInfo( layer, map );
}
// **********************************************************************
gt3dUI.LayerList.prototype.showLayerInfo = function( uilayer, map ) {
var self = this;
var vbox, hbox, subbox, button;
var layer = uilayer.layer;
this.layerwithinfoshown = layer.id_;
rkWebUtil.wipeDiv( this.parentui.bottominfo );
rkWebUtil.elemaker( "h4", this.parentui.bottominfo, { "text": "Layer " + layer.name + " on map " + map.name } );
hbox = rkWebUtil.elemaker( "div", this.parentui.bottominfo, { "classes": [ "hbox" ] } );
vbox = rkWebUtil.elemaker( "div", hbox, { "classes": [ "vbox", "marginr1ex", "padr1em" ] } )
subbox = rkWebUtil.elemaker( "div", vbox, { "classes": [ "hbox" ] } );
this.showhidelayerbutton = rkWebUtil.button( subbox, ( layer.isshown ? "Hide" : "Show" ),
function() { self.showHideLayer( uilayer ) } );
subbox = rkWebUtil.elemaker( "div", vbox, { "classes": [ "hbox" ] } );
button = rkWebUtil.button( subbox, "Edit Layer",
function() {
new gt3dMapLayer.editor( uilayer, uilayer.layer, uilayer.map, self.parentui );
} );
button.classList.add( "margint1ex" );
vbox = rkWebUtil.elemaker( "div", hbox, { "classes": [ "fbox", "marginr1ex", "padr1em" ] } );
this.allplayervisiblecheckbox = rkWebUtil.elemaker( "input", vbox, { "attributes":
{ "type": "checkbox",
"id": "layer_allplayervisiblecheckbox"
} } );
rkWebUtil.elemaker( "label", vbox, { "text": "visible to all players?",
"attributes": { "for": "layer_allplayervisiblecheckbox" } } );
this.allplayervisiblecheckbox.addEventListener( "change",
function( e ) {
window.alert( "Warning: player visibility not implemented." );
} );
vbox = rkWebUtil.elemaker( "div", hbox, { "classes": [ "fbox" ] } );
rkWebUtil.elemaker( "span", vbox, { "text": "Visible to players:" } );
rkWebUtil.elemaker( "div", vbox, { "text": "(Not implemented)", "classes": [ "inset" ] } );
rkWebUtil.elemaker( "div", this.parentui.bottominfo, { "text": 'id: ' + layer.id_,
"classes": [ "hboxright", "smallfadetext",
"margintopauto" ] } );
}
// **********************************************************************
// **********************************************************************
// **********************************************************************
gt3dUI.UserList = function( parentui, parentcontroller, parentdiv )
{
this.parentui = parentui;
this.controller = parentcontroller;
this.parentdiv = parentdiv;
var h4, div;
var self = this;
this.topdiv = rkWebUtil.elemaker( "div", this.parentdiv, { "classes": [ "userlist" ] } );
h4 = rkWebUtil.elemaker( "h4", this.topdiv, { "text": "GMs" } );
this.gmlist = rkWebUtil.elemaker( "ul", this.topdiv, { "classes": [ "userlist" ] } );
h4 = rkWebUtil.elemaker( "h4", this.topdiv, { "text": "Players", "classes": [ "martint1em" ] } );
this.playerlist = rkWebUtil.elemaker( "ul", this.topdiv, { "classes": [ "userlist" ] } );
this.controller.addUserListener( self );
}
// **********************************************************************
// Rob: you still have to add something about indicating which users are
// on the current map, and perhaps more info for GMs
gt3dUI.UserList.prototype.userListUpdated = function()
{
var self=this;
var gms = [];
var players = [];
for ( let userid in this.controller.users ) {
if ( this.controller.users[ userid ].isgm )
gms.push( userid );
else
players.push( userid );
}
var usersort = function( uid1, uid2 ) {
let u1 = self.controller.users[ uid1 ];
let u2 = self.controller.users[ uid2 ];
if ( u1.isloggedin && ( ! u2.isloggedin ) ) return -1;
else if ( ( ! u1.isloggedin ) && u2.isloggedin ) return 1;
else {
if ( u1.displayname < u2.displayname ) return -1;
else if ( u1.displayname > u2.displayname ) return 1;
else return 0;
}
};
gms.sort( usersort );
players.sort( usersort );
rkWebUtil.wipeDiv( this.gmlist );
rkWebUtil.wipeDiv( this.userplayer );
var renderuser = function( uid, ul ) {
let u = self.controller.users[ uid ];
let li = rkWebUtil.elemaker( "li", ul,
{ "text": u.displayname + " (" + u.username + ")",
"click": function() { self.showUserInfo( uid ) }
} );
// ROB ADD CLICK BEHAVIOR
if ( u.currentmap == self.controller.currentmap ) li.classList.add( "currentmap" );
if ( ! u.isloggedin ) li.classList.add( "notloggedin" );
}
var ul = rkWebUtil.elemaker( "ul", this.gmlist, { "classes": [ "userlist" ] } );
for ( let uid of gms ) renderuser( uid, ul );
var ul = rkWebUtil.elemaker( "ul", this.playerlist, { "classes": [ "userlist" ] } );
for ( let uid of players ) renderuser( uid, ul );
}
// **********************************************************************
gt3dUI.UserList.prototype.showUserInfo = function( userid ) {
var vbox, hbox, ul, li, button;
var self = this;
var user = this.controller.users[ userid ];
rkWebUtil.wipeDiv( this.parentui.bottominfo );
rkWebUtil.elemaker( "h4", this.parentui.bottominfo, { "text": "User: " + user.username } );
var mainhbox = rkWebUtil.elemaker( "div", this.parentui.bottominfo, { "classes": [ "hbox" ] } );
vbox = rkWebUtil.elemaker( "div", mainhbox, { "classes": [ "vbox", "padr1em" ] } );
ul = rkWebUtil.elemaker( "ul", vbox, { "classes": [ "markerless", "marginless" ] } );
li = rkWebUtil.elemaker( "li", ul, { "classes": [ "marginbhalfex" ] } );
if ( userid == this.controller.user.id_ ) {
this.newdisplayname = rkWebUtil.elemaker( "input", li,
{ "attributes": { "type": "text",
"width": 48,
"value": user.displayname } } );
this.newdisplayname.addEventListener( "keyup", function( ev ) { self.checkDisplayName( ev ); } );
rkWebUtil.button( li, "Change", function() { self.checkDisplayName( null ) } );
}
else {
li.innerHTML = "" + user.displayname + "";
}
li = rkWebUtil.elemaker( "li", ul, { "classes": [ "marginbhalfex" ] } );
li.innerHTML = (user.isgm ? "GM" : "Player" );
if ( this.controller.isgm ) {
li = rkWebUtil.elemaker( "li", ul, { "classes": [ "marginbhalfex" ] } );
li.innerHTML = "email: " + user.email
}
li = rkWebUtil.elemaker( "li", ul, { "classes": [ "marginbhalfex" ] } );
if ( user.isloggedin ) {
li.innerHTML = "Logged in since: " + user.logintime;
} else {
li.innerHTML = "Last login: " + user.lastlogin;
}
li = rkWebUtil.elemaker( "li", ul, { "classes": [ "marginbhalfex" ] } );
if ( user.currentmap == null ) {
li.innerHTML = "Current map: None";
} else {
li.innerHTML = "Current map: " + this.controller.maps[user.currentmap].name;
}
if ( this.controller.isgm ) {
vbox = rkWebUtil.elemaker( "div", mainhbox, { "classes": [ "vboxspacedinputs" ] } );
rkWebUtil.button( vbox, "Move To My Map", function() { window.alert( "Dunno how." ) } );
rkWebUtil.button( vbox, "Promote To GM", function() { window.alert( "Dunno how." ) } );
if ( this.controller.table.owner.id_ == this.controller.user.id_ ) {
rkWebUtil.button( vbox, "Demote To Player", function() { window.alert( "Dunno how." ) } );
}
rkWebUtil.button( vbox, "Kick Player From Game", function() { window.alert( "Dunno how." ) } );
}
rkWebUtil.elemaker( "div", this.parentui.bottominfo, { "text": 'it: ' + userid,
"classes": [ "hboxright", "smallfadetext",
"margintopauto" ] } );
}
// **********************************************************************
gt3dUI.UserList.prototype.moveUserToMap = function( userid, mapid )
{
// Overdone?
this.userListUpdated();
}
// **********************************************************************
gt3dUI.UserList.prototype.checkDisplayName = function( ev ) {
if ( ( ev != null ) && ( ev.key != "Enter" ) ) return;
if ( this.controller.user.displayname != this.newdisplayname.value ) {
window.alert( "I should change your displayname from " +
this.controller.user.displayname + " to " +
this.newdisplayname.value );
}
}
// **********************************************************************
export { gt3dUI, gt3dUIFactory };