Merge pull request #1592 from friarzen/master

Alpha webclient split interface support
This commit is contained in:
Griatch 2018-04-21 19:10:11 +02:00 committed by GitHub
commit 3c2e3f8d63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 522 additions and 137 deletions

View file

@ -71,7 +71,7 @@ class CmdLook(COMMAND_DEFAULT_CLASS):
target = caller.search(self.args)
if not target:
return
self.msg(caller.at_look(target))
self.msg((caller.at_look(target), {'type':'look'}), options=None)
class CmdNick(COMMAND_DEFAULT_CLASS):

View file

@ -1875,7 +1875,7 @@ class DefaultCharacter(DefaultObject):
"""
self.msg("\nYou become |c%s|n.\n" % self.name)
self.msg(self.at_look(self.location))
self.msg((self.at_look(self.location), {'type':'look'}), options = None)
def message(obj, from_obj):
obj.msg("%s has entered the game." % self.get_display_name(obj), from_obj=from_obj)

View file

@ -8,10 +8,11 @@
--- */
/* Overall element look */
html, body, #clientwrapper { height: 100% }
html, body {
height: 100%;
width: 100%;
}
body {
margin: 0;
padding: 0;
background: #000;
color: #ccc;
font-size: .9em;
@ -19,6 +20,12 @@ body {
line-height: 1.6em;
overflow: hidden;
}
@media screen and (max-width: 480px) {
body {
font-size: .5rem;
line-height: .7rem;
}
}
a:link, a:visited { color: inherit; }
@ -74,93 +81,109 @@ div {margin:0px;}
}
/* Style specific classes corresponding to formatted, narative text. */
.wrapper {
height: 100%;
}
/* Container surrounding entire client */
#wrapper {
position: relative;
height: 100%
#clientwrapper {
height: 100%;
}
/* Main scrolling message area */
#messagewindow {
position: absolute;
overflow: auto;
padding: 1em;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
top: 0;
left: 0;
right: 0;
bottom: 70px;
overflow-y: auto;
overflow-x: hidden;
overflow-wrap: break-word;
}
/* Input area containing input field and button */
#inputform {
position: absolute;
width: 100%;
padding: 0;
bottom: 0;
margin: 0;
padding-bottom: 10px;
border-top: 1px solid #555;
}
#inputcontrol {
width: 100%;
padding: 0;
#messagewindow {
overflow-y: auto;
overflow-x: hidden;
overflow-wrap: break-word;
}
/* Input field */
#inputfield, #inputsend, #inputsizer {
display: block;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
height: 50px;
#inputfield, #inputsizer {
height: 100%;
background: #000;
color: #fff;
padding: 0 .45em;
font-size: 1.1em;
padding: 0 .45rem;
font-size: 1.1rem;
font-family: 'DejaVu Sans Mono', Consolas, Inconsolata, 'Lucida Console', monospace;
}
#inputfield, #inputsizer {
float: left;
width: 95%;
border: 0;
resize: none;
line-height: normal;
}
#inputsend {
height: 100%;
}
#inputcontrol {
height: 100%;
}
#inputfield:focus {
outline: 0;
}
#inputsizer {
margin-left: -9999px;
}
/* Input 'send' button */
#inputsend {
float: right;
width: 3%;
max-width: 25px;
margin-right: 10px;
border: 0;
background: #555;
}
/* prompt area above input field */
#prompt {
margin-top: 10px;
padding: 0 .45em;
.prompt {
max-height: 3rem;
}
#splitbutton {
width: 2rem;
font-size: 2rem;
color: #a6a6a6;
background-color: transparent;
border: 0px;
}
#splitbutton:hover {
color: white;
cursor: pointer;
}
#panebutton {
width: 2rem;
font-size: 2rem;
color: #a6a6a6;
background-color: transparent;
border: 0px;
}
#panebutton:hover {
color: white;
cursor: pointer;
}
#undobutton {
width: 2rem;
font-size: 2rem;
color: #a6a6a6;
background-color: transparent;
border: 0px;
}
#undobutton:hover {
color: white;
cursor: pointer;
}
.button {
width: fit-content;
padding: 1em;
color: black;
border: 1px solid black;
background-color: darkgray;
margin: 0 auto;
}
.splitbutton:hover {
cursor: pointer;
}
#optionsbutton {
width: 40px;
font-size: 20px;
width: 2rem;
font-size: 2rem;
color: #a6a6a6;
background-color: transparent;
border: 0px;
@ -173,8 +196,8 @@ div {margin:0px;}
#toolbar {
position: fixed;
top: 0;
right: 5px;
top: .5rem;
right: .5rem;
z-index: 1;
}
@ -248,6 +271,52 @@ div {margin:0px;}
text-decoration: none;
cursor: pointer;
}
.gutter.gutter-vertical {
cursor: row-resize;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=')
}
.gutter.gutter-horizontal {
cursor: col-resize;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==')
}
.split {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;
}
.split-sub {
padding: .5rem;
}
.content {
border: 1px solid #C0C0C0;
box-shadow: inset 0 1px 2px #e4e4e4;
background-color: black;
padding: 1rem;
}
@media screen and (max-width: 480px) {
.content {
padding: .5rem;
}
}
.gutter {
background-color: grey;
background-repeat: no-repeat;
background-position: 50%;
}
.split.split-horizontal, .gutter.gutter-horizontal {
height: 100%;
float: left;
}
/* XTERM256 colors */

View file

@ -0,0 +1,145 @@
// Use split.js to create a basic ui
var SplitHandler = (function () {
var split_panes = {};
var backout_list = new Array;
var set_pane_types = function(splitpane, types) {
split_panes[splitpane]['types'] = types;
}
var dynamic_split = function(splitpane, direction, pane_name1, pane_name2, update_method1, update_method2, sizes) {
// find the sub-div of the pane we are being asked to split
splitpanesub = splitpane + '-sub';
// create the new div stack to replace the sub-div with.
var first_div = $( '<div id="'+pane_name1+'" class="split split-'+direction+'" />' )
var first_sub = $( '<div id="'+pane_name1+'-sub" class="split-sub" />' )
var second_div = $( '<div id="'+pane_name2+'" class="split split-'+direction+'" />' )
var second_sub = $( '<div id="'+pane_name2+'-sub" class="split-sub" />' )
// check to see if this sub-pane contains anything
contents = $('#'+splitpanesub).contents();
if( contents ) {
// it does, so move it to the first new div-sub (TODO -- selectable between first/second?)
contents.appendTo(first_sub);
}
first_div.append( first_sub );
second_div.append( second_sub );
// update the split_panes array to remove this pane name, but store it for the backout stack
var backout_settings = split_panes[splitpane];
delete( split_panes[splitpane] );
// now vaporize the current split_N-sub placeholder and create two new panes.
$('#'+splitpane).append(first_div);
$('#'+splitpane).append(second_div);
$('#'+splitpane+'-sub').remove();
// And split
Split(['#'+pane_name1,'#'+pane_name2], {
direction: direction,
sizes: sizes,
gutterSize: 4,
minSize: [50,50],
});
// store our new split sub-divs for future splits/uses by the main UI.
split_panes[pane_name1] = { 'types': [], 'update_method': update_method1 };
split_panes[pane_name2] = { 'types': [], 'update_method': update_method2 };
// add our new split to the backout stack
backout_list.push( {'pane1': pane_name1, 'pane2': pane_name2, 'undo': backout_settings} );
}
var undo_split = function() {
// pop off the last split pair
var back = backout_list.pop();
if( !back ) {
return;
}
// Collect all the divs/subs in play
var pane1 = back['pane1'];
var pane2 = back['pane2'];
var pane1_sub = $('#'+pane1+'-sub');
var pane2_sub = $('#'+pane2+'-sub');
var pane1_parent = $('#'+pane1).parent();
var pane2_parent = $('#'+pane2).parent();
if( pane1_parent.attr('id') != pane2_parent.attr('id') ) {
// sanity check failed...somebody did something weird...bail out
console.log( pane1 );
console.log( pane2 );
console.log( pane1_parent );
console.log( pane2_parent );
return;
}
// create a new sub-pane in the panes parent
var parent_sub = $( '<div id="'+pane1_parent.attr('id')+'-sub" class="split-sub" />' )
// check to see if the special #messagewindow is in either of our sub-panes.
var msgwindow = pane1_sub.find('#messagewindow')
if( !msgwindow ) {
//didn't find it in pane 1, try pane 2
msgwindow = pane2_sub.find('#messagewindow')
}
if( msgwindow ) {
// It is, so collect all contents into it instead of our parent_sub div
// then move it to parent sub div, this allows future #messagewindow divs to flow properly
msgwindow.append( pane1_sub.contents() );
msgwindow.append( pane2_sub.contents() );
parent_sub.append( msgwindow );
} else {
//didn't find it, so move the contents of the two panes' sub-panes into the new sub-pane
parent_sub.append( pane1_sub.contents() );
parent_sub.append( pane2_sub.contents() );
}
// clear the parent
pane1_parent.empty();
// add the new sub-pane back to the parent div
pane1_parent.append(parent_sub);
// pull the sub-div's from split_panes
delete split_panes[pane1];
delete split_panes[pane2];
// add our parent pane back into the split_panes list for future splitting
split_panes[pane1_parent.attr('id')] = back['undo'];
}
var init = function(settings) {
//change Mustache tags to ruby-style (Django gets mad otherwise)
var customTags = [ '<%', '%>' ];
Mustache.tags = customTags;
var input_template = $('#input-template').html();
Mustache.parse(input_template);
Split(['#main','#input'], {
direction: 'vertical',
sizes: [90,10],
gutterSize: 4,
minSize: [50,50],
});
split_panes['main'] = { 'types': [], 'update_method': 'append' };
var input_render = Mustache.render(input_template);
$('[data-role-input]').html(input_render);
console.log("SplitHandler initialized");
}
return {
init: init,
set_pane_types: set_pane_types,
dynamic_split: dynamic_split,
split_panes: split_panes,
undo_split: undo_split,
}
})();

View file

@ -15,8 +15,13 @@
(function () {
"use strict"
var num_splits = 0; //unique id counter for default split-panel names
var options = {};
var known_types = new Array();
known_types.push('help');
//
// GUI Elements
//
@ -106,6 +111,7 @@ function togglePopup(dialogname, content) {
// Grab text from inputline and send to Evennia
function doSendText() {
console.log("sending text");
if (!Evennia.isConnected()) {
var reconnect = confirm("Not currently connected. Reconnect?");
if (reconnect) {
@ -158,7 +164,11 @@ function onKeydown (event) {
var code = event.which;
var history_entry = null;
var inputfield = $("#inputfield");
inputfield.focus();
if (code === 9) {
return;
}
//inputfield.focus();
if (code === 13) { // Enter key sends text
doSendText();
@ -205,74 +215,68 @@ function onKeyPress (event) {
}
var resizeInputField = function () {
var min_height = 50;
var max_height = 300;
var prev_text_len = 0;
return function() {
var wrapper = $("#inputform")
var input = $("#inputcontrol")
var prompt = $("#prompt")
// Check to see if we should change the height of the input area
return function () {
var inputfield = $("#inputfield");
var scrollh = inputfield.prop("scrollHeight");
var clienth = inputfield.prop("clientHeight");
var newh = 0;
var curr_text_len = inputfield.val().length;
if (scrollh > clienth && scrollh <= max_height) {
// Need to make it bigger
newh = scrollh;
}
else if (curr_text_len < prev_text_len) {
// There is less text in the field; try to make it smaller
// To avoid repaints, we draw the text in an offscreen element and
// determine its dimensions.
var sizer = $('#inputsizer')
.css("width", inputfield.prop("clientWidth"))
.text(inputfield.val());
newh = sizer.prop("scrollHeight");
}
if (newh != 0) {
newh = Math.min(newh, max_height);
if (clienth != newh) {
inputfield.css("height", newh + "px");
doWindowResize();
}
}
prev_text_len = curr_text_len;
input.height(wrapper.height() - (input.offset().top - wrapper.offset().top));
}
}();
// Handle resizing of client
function doWindowResize() {
var formh = $('#inputform').outerHeight(true);
var message_scrollh = $("#messagewindow").prop("scrollHeight");
$("#messagewindow")
.css({"bottom": formh}) // leave space for the input form
.scrollTop(message_scrollh); // keep the output window scrolled to the bottom
resizeInputField();
var resizable = $("[data-update-append]");
var parents = resizable.closest(".split")
parents.animate({
scrollTop: parents.prop("scrollHeight")
}, 0);
}
// Handle text coming from the server
function onText(args, kwargs) {
// append message to previous ones, then scroll so latest is at
// the bottom. Send 'cls' kwarg to modify the output class.
var renderto = "main";
if (kwargs["type"] == "help") {
if (("helppopup" in options) && (options["helppopup"])) {
renderto = "#helpdialog";
var use_default_pane = true;
if ( kwargs && 'type' in kwargs ) {
var msgtype = kwargs['type'];
if ( ! known_types.includes(msgtype) ) {
// this is a new output type that can be mapped to panes
console.log('detected new output type: ' + msgtype)
known_types.push(msgtype);
}
// pass this message to each pane that has this msgtype mapped
if( SplitHandler ) {
for ( var key in SplitHandler.split_panes) {
var pane = SplitHandler.split_panes[key];
// is this message type mapped to this pane?
if ( (pane['types'].length > 0) && pane['types'].includes(msgtype) ) {
// yes, so append/replace this pane's inner div with this message
var text_div = $('#'+key+'-sub');
if ( pane['update_method'] == 'replace' ) {
text_div.html(args[0])
} else {
text_div.append(args[0]);
var scrollHeight = text_div.parent().prop("scrollHeight");
text_div.parent().animate({ scrollTop: scrollHeight }, 0);
}
// record sending this message to a pane, no need to update the default div
use_default_pane = false;
}
}
}
}
if (renderto == "main") {
// append message to default pane, then scroll so latest is at the bottom.
if(use_default_pane) {
var mwin = $("#messagewindow");
var cls = kwargs == null ? 'out' : kwargs['cls'];
mwin.append("<div class='" + cls + "'>" + args[0] + "</div>");
mwin.animate({
scrollTop: document.getElementById("messagewindow").scrollHeight
}, 0);
var scrollHeight = mwin.parent().parent().prop("scrollHeight");
mwin.parent().parent().animate({ scrollTop: scrollHeight }, 0);
onNewLine(args[0], null);
} else {
openPopup(renderto, args[0]);
}
}
@ -430,6 +434,105 @@ function doStartDragDialog(event) {
$(document).bind("mouseup", undrag);
}
function onSplitDialogClose() {
var pane = $("input[name=pane]:checked").attr("value");
var direction = $("input[name=direction]:checked").attr("value");
var new_pane1 = $("input[name=new_pane1]").val();
var new_pane2 = $("input[name=new_pane2]").val();
var flow1 = $("input[name=flow1]:checked").attr("value");
var flow2 = $("input[name=flow2]:checked").attr("value");
if( new_pane1 == "" ) {
new_pane1 = 'pane_'+num_splits;
num_splits++;
}
if( new_pane2 == "" ) {
new_pane2 = 'pane_'+num_splits;
num_splits++;
}
if( document.getElementById(new_pane1) ) {
alert('An element: "' + new_pane1 + '" already exists');
return;
}
if( document.getElementById(new_pane2) ) {
alert('An element: "' + new_pane2 + '" already exists');
return;
}
SplitHandler.dynamic_split( pane, direction, new_pane1, new_pane2, flow1, flow2, [50,50] );
closePopup("#splitdialog");
}
function onSplitDialog() {
var dialog = $("#splitdialogcontent");
dialog.empty();
dialog.append("<h3>Split?</h3>");
dialog.append('<input type="radio" name="direction" value="vertical" checked> top/bottom<br />');
dialog.append('<input type="radio" name="direction" value="horizontal"> side-by-side<br />');
dialog.append("<h3>Split Which Pane?</h3>");
for ( var pane in SplitHandler.split_panes ) {
dialog.append('<input type="radio" name="pane" value="'+ pane +'">'+ pane +'<br />');
}
dialog.append("<h3>New Pane Names</h3>");
dialog.append('<input type="text" name="new_pane1" value="" />');
dialog.append('<input type="text" name="new_pane2" value="" />');
dialog.append("<h3>New First Pane</h3>");
dialog.append('<input type="radio" name="flow1" value="append" checked>append new incoming messages<br />');
dialog.append('<input type="radio" name="flow1" value="replace">replace old messages with new ones<br />');
dialog.append("<h3>New Second Pane</h3>");
dialog.append('<input type="radio" name="flow2" value="append" checked>append new incoming messages<br />');
dialog.append('<input type="radio" name="flow2" value="replace">replace old messages with new ones<br />');
dialog.append('<div id="splitclose" class="button">Split It</div>');
$("#splitclose").bind("click", onSplitDialogClose);
togglePopup("#splitdialog");
}
function onPaneControlDialogClose() {
var pane = $("input[name=pane]:checked").attr("value");
var types = new Array;
$('#splitdialogcontent input[type=checkbox]:checked').each(function() {
types.push( $(this).attr('value') );
});
SplitHandler.set_pane_types( pane, types );
closePopup("#splitdialog");
}
function onPaneControlDialog() {
var dialog = $("#splitdialogcontent");
dialog.empty();
dialog.append("<h3>Set Which Pane?</h3>");
for ( var pane in SplitHandler.split_panes ) {
dialog.append('<input type="radio" name="pane" value="'+ pane +'">'+ pane +'<br />');
}
dialog.append("<h3>Which content types?</h3>");
for ( var type in known_types ) {
dialog.append('<input type="checkbox" value="'+ known_types[type] +'">'+ known_types[type] +'<br />');
}
dialog.append('<div id="paneclose" class="button">Make It So</div>');
$("#paneclose").bind("click", onPaneControlDialogClose);
togglePopup("#splitdialog");
}
//
// Register Events
//
@ -437,6 +540,18 @@ function doStartDragDialog(event) {
// Event when client finishes loading
$(document).ready(function() {
if( SplitHandler ) {
SplitHandler.init();
$("#splitbutton").bind("click", onSplitDialog);
$("#panebutton").bind("click", onPaneControlDialog);
$("#undobutton").bind("click", SplitHandler.undo_split);
$("#optionsbutton").hide();
} else {
$("#splitbutton").hide();
$("#panebutton").hide();
$("#undobutton").hide();
}
if ("Notification" in window) {
Notification.requestPermission();
}
@ -453,7 +568,7 @@ $(document).ready(function() {
//$(document).on("visibilitychange", onVisibilityChange);
$("#inputfield").bind("resize", doWindowResize)
$("[data-role-input]").bind("resize", doWindowResize)
.keypress(onKeyPress)
.bind("paste", resizeInputField)
.bind("cut", resizeInputField);
@ -506,6 +621,7 @@ $(document).ready(function() {
},
60000*3
);
console.log("Completed GUI setup");
});

View file

@ -13,6 +13,10 @@ JQuery available.
<meta http-equiv="content-type", content="application/xhtml+xml; charset=UTF-8" />
<meta name="author" content="Evennia" />
<meta name="generator" content="Evennia" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link rel='stylesheet' type="text/css" media="screen" href={% static "webclient/css/webclient.css" %}>
@ -20,7 +24,7 @@ JQuery available.
<!-- Import JQuery and warn if there is a problem -->
{% block jquery_import %}
<script src="https://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript" charset="utf-8"></script>
{% endblock %}
<script type="text/javascript" charset="utf-8">
@ -29,6 +33,14 @@ JQuery available.
}
</script>
<!-- This is will only fire if javascript is actually active -->
<script language="javascript" type="text/javascript">
$(document).ready(function() {
$('#noscript').remove();
$('#clientwrapper').removeClass('d-none');
})
</script>
<!-- Set up Websocket url and load the evennia.js library-->
<script language="javascript" type="text/javascript">
{% if websocket_enabled %}
@ -51,6 +63,12 @@ JQuery available.
</script>
<script src={% static "webclient/js/evennia.js" %} language="javascript" type="text/javascript" charset="utf-8"/></script>
<!-- set up splits before loading the GUI -->
<script src="https://unpkg.com/split.js/split.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
<script src={% static "webclient/js/splithandler.js" %} language="javascript"></script>
<!-- Load gui library -->
{% block guilib_import %}
<script src={% static "webclient/js/webclient_gui.js" %} language="javascript" type="text/javascript" charset="utf-8"></script>
@ -63,7 +81,11 @@ JQuery available.
}
</script>
<!-- jQuery first, then Tether, then Bootstrap JS. -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
{% block scripts %}
{% endblock %}
</head>
<body>
@ -86,10 +108,9 @@ JQuery available.
</div>
<!-- main client -->
<div id=clientwrapper>
<div id=clientwrapper class="d-none">
{% block client %}
{% endblock %}
</div>
</body>
</html>

View file

@ -8,20 +8,29 @@
{% block client %}
<div id="toolbar">
<button id="optionsbutton" type="button" aria-haspopup="true" aria-owns="#optionsdialog">&#x2699;<span class="sr-only sr-only-focusable">Settings</span></button>
<button id="splitbutton" type="button" aria-haspopup="true" aria-owns="#optionsdialog">&#x21f9;<span class="sr-only sr-only-focusable">Splits</span></button>
<button id="panebutton" type="button" aria-haspopup="true" aria-owns="#optionsdialog">&#x2699;<span class="sr-only sr-only-focusable">Splits</span></button>
<button id="undobutton" type="button" aria-haspopup="true" aria-owns="#optionsdialog">&#x21B6;<span class="sr-only sr-only-focusable">Splits</span></button>
</div>
<div id="wrapper">
<div id="toolbar">
<button id="optionsbutton" type="button" class="hidden">&#x2699;</button>
</div>
<div id="messagewindow" role="log"></div>
<div id="inputform">
<div id="prompt"></div>
<div id="inputcontrol">
<textarea id="inputfield" type="text"></textarea>
<input id="inputsend" type="button" value="&gt;"/>
<!-- The "Main" Content -->
<div id="main" class="split split-vertical" data-role-default>
<div id="main-sub" class="split-sub">
<div id="messagewindow"></div>
</div>
</div>
<!-- The "Input" Pane -->
<div id="input" class="split split-vertical" data-role-input data-update-append></div>
<!-- Basic UI Components -->
<div id="splitdialog" class="dialog">
<div class="dialogtitle">Split Pane<span class="dialogclose">&times;</span></div>
<div class="dialogcontentparent">
<div id="splitdialogcontent" class="dialogcontent">
</div>
</div>
<div id="inputsizer"></div>
</div>
<div id="optionsdialog" class="dialog">
@ -47,4 +56,29 @@
</div>
</div>
<script type="text/html" id="split-template">
<div class="split content<%#horizontal%> split-horizontal<%/horizontal%>" id='<%id%>'>
</div>
</script>
<script type="text/html" id="output-template">
<div id="<%id%>" role="log" data-role-output data-update-append data-tags='[<%#tags%>"<%.%>", <%/tags%>]'></div>
</script>
<script type="text/html" id="input-template">
<div id="inputform" class="wrapper">
<div id="prompt" class="prompt">
</div>
<div id="inputcontrol" class="input-group">
<textarea id="inputfield" type="text" class="form-control"></textarea>
<span class="input-group-btn">
<button class="btn btn-large btn-outline-primary" id="inputsend" type="button" value="">&gt;</button>
</span>
</div>
</div>
</script>
{% endblock %}
{% block scripts %}
{% endblock %}