Pretend this page is your web application... Embedding Gate One can be as simple as this:
<!-- Include gateone.js somewhere on your page -->
<script src="static/gateone.js"></script>
<!-- Decide where you want to put Gate One -->
<div id="gateone_container" style="position: relative; width: 60em; height: 30em;">
<div id="gateone"></div>
</div>
<!-- Call GateOne.init() at some point after the page is done loading -->
<script>
window.onload = function() {
// Initialize Gate One:
GateOne.init({url: 'https://gateone.mycompany.com/'});
}
</script>
<!-- That's it! -->
Reload the page then click on Basic Terminal to see what Gate One looks like when embedded.
That's all fine and good but what if you want to change things around? Any preference can be overridden by passing it to GateOne.init(). For example, if you wanted to use the 'white' theme by default with an increased font size:
<script src="static/gateone.js"></script>
<div id="gateone_container" style="position: relative; width: 60em; height: 30em;">
<div id="gateone"></div>
</div>
<script>
window.onload = function() {
// Initialize Gate One:
GateOne.init({
url: 'https://gateone.mycompany.com/',
theme: 'white',
fontSize: '130%'
});
}
</script>
Reload the page then click on Advanced Terminal to see an example of the above code in action.
So you want every little thing to be customized? A tab-based interface with custom pink icons? No problem! That's what "embedded mode" is for. To initialize Gate One in embedded mode you have only to pass '{embedded: true}' to GateOne.init():
<script src="static/gateone.js"></script>
<div id="gateone_container" style="position: relative; width: 60em; height: 30em;">
<div id="gateone"></div>
</div>
<script>
window.onload = function() {
// Initialize Gate One:
GateOne.init({
url: 'https://gateone.mycompany.com/',
embedded: true,
// Let's apply some custom styles while we're at it...
style: {'background-color': 'yellowgreen', 'box-shadow': '0 0 40px blueViolet'}
// Oh yeah, that's the way to style a terminal!
});
}
</script>
Reload the page then click on DIY Terminal to see an example of the above code in action.
Notice anything missing? The actual terminal! Why isn't it there? Because we haven't placed it yet. That's one of the caveats of embedded mode... You have to be explicity about nearly everything.
To solve this problem we'll add a button that when clicked adds a new terminal...
<form id="add_terminal">
<input type="submit" value="Add a Terminal!" style="margin-left: .5em;"></input>
</form>
<script>
document.querySelector('#add_terminal').onsubmit = function(e) {
// NOTE: To avoid name conflicts GateOne.Utils.createElement() automatically prepends
// GateOne.prefs.prefix to the ID of the created element. This is why
// GateOne.Utils.getNode() below has the prefix inserted in the middle...
var existingContainer = GateOne.Utils.getNode('#'+GateOne.prefs.prefix+'container'),
container = GateOne.Utils.createElement('div', {
'id': 'container', 'class': 'terminal', 'style': {'height': '100%', 'width': '100%'}
}),
gateone3 = GateOne.Utils.getNode('#gateone3');
e.preventDefault(); // Don't actually submit the form
if (!existingContainer) {
gateone3.appendChild(container); // Put our terminal in the container
} else {
container = existingContainer;
}
termNum = GateOne.Terminal.newTerminal(null, null, container); // Create the new terminal
}
</script>
You might have to scroll back up a bit after clicking the button. What happens if you add a second terminal?
In the next section we'll cover how to handle overlapping terminals and switching between them.
Let's take things to the next level and make a tabbed interface for our Gate One terminal. Like the previous example we'll pass 'embedded: true' to GateOne.init() but this time we'll create a function, newTabTerminal(), that creates tabs and puts new terminals inside of them. We'll also make a function, reattachTerminals(terminals), that gets attached to GateOne.Terminal.reattachTerminalsCallbacks so that these tabs will get re-created automatically when the user's session is resumed (e.g. reload). Finally, we'll make a function, selectTerminal(termNum) that we can call to switch to whatever tab we want (will be important in the next part of the tutorial).
<script src="static/gateone.js"></script>
<div id="tabContainer">
<div class="tabs">
<ul id="tabs1">
<!-- Tabs will go here -->
</ul>
</div>
<div id="gateonecontainer4" style="position: relative; width: 60em; height: 30em;">
<div id="gateone4">
<form id="go_embed4" style="background-color: #fff; color: #000; text-align: center;">
Enter the URL for your Gate One server<br><br>
<p>
<input name="gourl4" id="gourl4" size=40 />
<input type="submit" value="Go!" style="margin-left: .5em;"></input>
</p>
</form></div>
</div>
</div>
<script>
window.onload = function() {
// Initialize Gate One:
GateOne.init({
url: 'https://gateone.mycompany.com/',
embedded: true,
// Let's apply some custom styles while we're at it...
style: {'background-color': 'transparent', 'box-shadow': '0 0 40px black'}
});
}
var selectTerminal = function(termNum) {
// Switches to the terminal tab given by *termNum*
var go = GateOne,
u = go.Utils,
prefix = go.prefs.prefix,
terminals = u.getNodes(go.prefs.goDiv + ' .terminal');
u.toArray(terminals).forEach(function(termNode) {
// Hide all but the selected terminal...
if (termNode.id == prefix+'term'+termNum) { // Translation: 'go4_term1'
termNode.style.display = ''; // Default to visible
} else {
termNode.style.display = 'none'; // Hide it
}
});
// This makes sure our keystrokes are sent to the correct terminal
go.Net.setTerminal(termNum);
// This displays the terminal info in a transient pop-up
go.Visual.displayTermInfo(termNum);
// Scroll the pre to the bottom (undoing display:none scrolls elements to the top)
u.scrollToBottom('#'+prefix+'term'+termNum+'_pre');
// This makes sure that we (re)enable keyboard input after switching terminals
go.Input.capture();
// NOTE: disableCapture() gets called whenever you click outside of a terminal
}
var newTabTerminal = function(termNum) {
// Creates a new Gate One terminal in a new tab
// *termNum* is optional and really only used when re-attaching existing terminals
var go = GateOne,
u = go.Utils,
prefix = go.prefs.prefix,
existingContainer = u.getNode('#'+prefix+'tab_container'),
container = u.createElement('div', {
'id': 'tab_container',
'style': {'height': '100%', 'width': '100%'}
}),
gateone4 = u.getNode('#gateone4'),
tabContainer = u.getNode('#tabs1'),
newTab = u.createElement('li'),
existingTerminals = u.getNodes(go.prefs.goDiv + ' .terminal');
// Hide all existing terminals to ensure the user only sees the new one
if (existingTerminals) {
u.toArray(existingTerminals).forEach(function(termNode) {
// Tip: You could get really fancy here with effects if you wanted...
termNode.style.display = 'none';
});
}
// Figure out if we need to add our terminal container or if we can use an existing one
if (!existingContainer) {
gateone4.appendChild(container);
} else {
container = existingContainer;
}
// Tell Gate One to create the new terminal
if (termNum) {
GateOne.Terminal.newTerminal(termNum, null, container);
} else {
termNum = GateOne.Terminal.newTerminal(null, null, container);
}
// Make our tab header information to match the new terminal
newTab.id = 'tabHeader' + termNum;
newTab.innerHTML = 'Term ' + termNum;
// Make the tabs clickable
newTab.onclick = function(e) {
selectTerminal(termNum);
}
// Add our new tab
tabContainer.appendChild(newTab);
GateOne.Input.capture(); // (re)start capturing keyboard input
}
var reattachTerminals = function(terminals) {
// This gets appended to GateOne.Terminal.reattachTerminalsCallbacks.
// It just calls newTabTerminal() for each term in *terminals*
var go = GateOne,
u = go.Utils;
terminals.forEach(function(termNum) {
newTabTerminal(termNum);
});
}
// This is what loads the Gate One terminal when you press that "Go!" button...
// Figured I'd put it in the code example this time since we're getting all "advaced"
document.querySelector('#go_embed4').onsubmit = function(e) {
var gourl = document.querySelector('#gourl4').value;
e.preventDefault(); // Don't actually submit the form
localStorage['gourl'] = gourl;
// Initialize Gate One like usual...
GateOne.init({
url: gourl,
embedded: true,
goDiv: '#gateone4',
prefix: 'go4_',
logLevel: 'DEBUG', 'style': {
'background-color': 'rgba(0, 0, 0, 0.85)',
'box-shadow': '.5em .5em .5em black',
'margin-bottom': '0.5em'
}
});
GateOne.Net.reauthenticate = reauthenticate;
GateOne.Terminal.reattachTerminalsCallbacks.push(reattachTerminals);
}
// Attach the newTabTerminal function to our little "Add a Terminal!" button
document.querySelector('#add_tab').onsubmit = function(e) {
e.preventDefault(); // Don't actually submit the form
newTabTerminal();
}
</script>
Reload the page then click on Tabbed Terminal to see an example of the above code in action.
Now that we've made a (mostly) working tab-based interface we still have a few things to nail down to make it completely usable: Closing tabs/terminals and keyboard shortcuts. Just like 'reattachTerminalsCallbacks' there's 'closeTermCallbacks'. We'll modify the previous code to take advantage of this by adding a new termClosed(termNum) function and we'll also add two keyboard shortcuts...
// For the sake of brevity the code above this part from the previous example has been cut.
var termClosed = function(termNum) {
// This gets attached to closeTermCallbacks to ensure that the closed tab
// is removed/cleaned up
var tabHeader = GateOne.Utils.getNode('#tabHeader' + termNum);
GateOne.Utils.removeElement(tabHeader);
var firstTerminal = GateOne.Utils.getNode('.terminal'), // Returns the first terminal
if (firstTerminal) {
var prevTermNum = firstTerminal.id.split('term')[1]; // Just want the number
selectTerminal(prevTermNum);
} else {
// If no remaining terminals, add a new one
newTabTerminal(); // Don't leave the user hanging with no open terminals!
}
}
var closeCurrentTerm = function() {
GateOne.Terminal.closeTerminal(localStorage[GateOne.prefs.prefix+"selectedTerminal"]);
}
document.querySelector('#go_embed4').onsubmit = function(e) {
var gourl = document.querySelector('#gourl4').value;
e.preventDefault(); // Don't actually submit the form
localStorage['gourl'] = gourl;
// Initialize Gate One like usual...
GateOne.init({
url: gourl,
embedded: true,
goDiv: '#gateone4',
prefix: 'go4_',
logLevel: 'DEBUG', 'style': {
'background-color': 'rgba(0, 0, 0, 0.85)',
'box-shadow': '.5em .5em .5em black',
'margin-bottom': '0.5em'
}
});
GateOne.Net.reauthenticate = reauthenticate;
GateOne.Terminal.reattachTerminalsCallbacks.push(reattachTerminals);
// NEW:
GateOne.Terminal.closeTermCallbacks.push(termClosed);
// ALSO NEW (keyboard shortcuts):
GateOne.Input.registerShortcut('KEY_W', {
'modifiers': {
'ctrl': false,
'alt': false,
'meta': false,
'shift': true
},
'action': closeCurrentTerm
});
}
// Attach the newTabTerminal function to our little "Add a Terminal!" button
document.querySelector('#add_tab').onsubmit = function(e) {
e.preventDefault(); // Don't actually submit the form
newTabTerminal();
}
The termClosed() function is pretty straightforward but the registerShortcut function could use a little explaining... registerShortcut takes three arguments: a key string (e.g. KEY_W), an object that defines modifiers, and an 'action'. The first two don't need explaining. The 'action' can either be a string which will be called via eval() or a function that will be called with no arguments (protip: Check out GateOne.Utils.partial for a way to create functions with preloaded arguments).
Reload the page then click on Tabbed Terminal 2 to see an example of the above code in action.
That's it! Of course there's lots more to explore and an infinite amount of ways this can be customized further. For more information see the Gate One documentation and also check out the Example Plugin.