keyboard-layout-editor/kb.html
Ian Prest a5055e99d5 Can now SAVE layouts to the server.
Implemented as a POST upload to AWS/S3.
-- Each layout is a separate file; identified by its MD5 hash
-- No real security to protect against malicious users "erasing"
layouts, but S3 offers versioning.

Also:
-- Added save button on the toolbar.
-- Added load/save alert boxes.
-- Added Ctrl+S hotkey to save.
2013-10-14 02:08:29 -04:00

305 lines
No EOL
16 KiB
HTML

<!--
KEYBOARD LAYOUT EDITOR
Copyright (C) 2013 Ian Prest
All rights reserved.
-->
<!DOCTYPE html>
<html ng-app="kbApp">
<head>
<title>Keyboard Layout Editor</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" media="screen">
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="css/hint.min.css">
<link rel="stylesheet" type="text/css" href="kb.css">
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript" src="js/angular-sanitize.min.js"></script>
<script type="text/javascript" src="js/ui-utils.min.js"></script>
<script type="text/javascript" src="js/jquery-2.0.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/urlon.js"></script>
<script type="text/javascript" src="js/md5.js"></script>
<script type="text/javascript" src="kb.js"></script>
</head>
<body ng-controller="kbCtrl"
ng-mouseup="selectRelease($event)"
ng-mousemove="selectMove($event)"
ui-keydown="{'shift-191' : 'showHelp()',
'ctrl-90' : 'undo()',
'ctrl-shift-90' : 'redo()',
'ctrl-89' : 'redo()',
'ctrl-83' : 'save($event)' }">
<div id="wrap">
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"><i class="icon-keyboard"></i> keyboard-layout-editor.com</a>
</div>
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-keyboard"></i> Preset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li ng-repeat="(k,v) in layouts"><a ng-click="loadPreset(v)" href="#">{{k}}</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-th"></i> Color Swatches <b class="caret"></b></a>
<ul class="dropdown-menu">
<li ng-repeat="pal in palettes"><a ng-click="loadPalette(pal)" href="#">{{pal.name}}</a></li>
</ul>
</li>
</ul>
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav navbar-right">
<li><a ng-href="{{getPermalink()}}" target="_blank" ng-click="dirty = false"><i class="icon-link"></i> Permalink</a></li>
</ul>
</div>
</nav>
<div class="body">
<div class="btn-group">
<button type="button" class="btn btn-primary" ng-click="addKey()"><i class="glyphicon glyphicon-plus-sign"></i> Add Key</button>
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a ng-click="addKeys(1)">Add 1 Key</a></li>
<li><a ng-click="addKeys(5)">Add 5 Keys</a></li>
<li><a ng-click="addKeys(10)">Add 10 Keys</a></li>
<li><a ng-click="addKeys(25)">Add 25 Keys</a></li>
<li class="divider" ng-class="{hidden: !specialKeys}"></li>
<li ng-repeat="(k,v) in specialKeys"><a ng-click="addKey(v)">Add '{{k}}' Key</a></li>
</ul>
</div>
<div class="btn-group">
<button type="button" class="btn btn-danger" ng-class="{disabled:selectedKeys.length<1}" ng-click="deleteKeys()"><i class="glyphicon glyphicon-remove-sign"></i> Delete Keys</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default" ng-class="{disabled:!canUndo()}" ng-click="undo()"><i class="icon-undo"></i> Undo</button>
<button type="button" class="btn btn-default" ng-class="{disabled:!canRedo()}" ng-click="redo()"><i class="icon-repeat"></i> Redo</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default" ng-class="{disabled:!canCopy()}" ng-click="cut()"><i class="icon-cut"></i> Cut</button>
<button type="button" class="btn btn-default" ng-class="{disabled:!canCopy()}" ng-click="copy()"><i class="icon-copy"></i> Copy</button>
<button type="button" class="btn btn-default" ng-class="{disabled:!canPaste()}" ng-click="paste()"><i class="icon-paste"></i> Paste</button>
</div>
<div class="btn-group pull-right">
<button type="button" class="btn btn-success" ng-class="{disabled:!canSave()}" ng-click="save()"><i class="icon-save"></i> Save</button>
</div>
<div id="keyboard"
tabindex="0"
ng-style="{height: kbHeight + 'px'}"
ui-keydown="{ left:'moveKeys(-.25,0,$event)',
right:'moveKeys(.25,0,$event)',
up:'moveKeys(0,-.25,$event)',
down:'moveKeys(0,.25,$event)',
'shift-left':'sizeKeys(-.25,0,$event)',
'shift-right':'sizeKeys(.25,0,$event)',
'shift-up':'sizeKeys(0,-.25,$event)',
'shift-down':'sizeKeys(0,.25,$event)',
delete:'deleteKeys()',
insert:'addKey()',
74: 'prevKey($event)',
75: 'nextKey($event)',
'shift-74': 'prevKey($event)',
'shift-75': 'nextKey($event)',
113: 'focusEditor()',
esc: 'unselectAll()',
'ctrl-65': 'selectAll()',
'ctrl-67 ctrl-45': 'copy($event)',
'ctrl-88 shift-46': 'cut($event)',
'ctrl-86 shift-45': 'paste($event)' }"
ng-mousedown="selectClick($event)">
<div ng-repeat="key in keys"
class="key {{key.profile}}"
ng-mouseover="hoveredKey=key"
ng-mouseleave="hoveredKey=null"
ng-class="{hover: hoveredKey==key, selected: selectedKeys.indexOf(key)>=0}"
ng-bind-html="key.html">
</div>
<div id="selectionRectangle" ng-style="{display:selRect.display, left:selRect.l+'px', width:selRect.w+'px', top:selRect.t+'px', height:selRect.h+'px'}"></div>
<div style='clear:both'></div>
{{calcKbHeight()}}
</div>
<ul class="nav nav-tabs">
<li ng-class="{active:selTab==0}"><a ng-click="selTab=0" data-toggle="tab"><i class="icon-edit"></i> Properties</a></li>
<li ng-class="{active:selTab==1}"><a ng-click="selTab=1" data-toggle="tab"><i class="icon-code"></i> Raw data</a></li>
</ul>
<div class='tab-content' ui-keydown="{esc:'focusKb()'}">
<div id="properties" ng-class="{hidden:selTab!=0}" class="col-md-12 row">
<form class="form-horizontal col-md-4 col-lg-4">
<div class="control-group">
<label class="control-label" for="labeleditor">Top Label:</label>
<div class="controls">
<input id="labeleditor" size="8" type='text' ng-model="multi.label" ng-change="updateMulti('label')" ng-blur="validateMulti('label')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="labeleditor2">Bottom Label:</label>
<div class="controls">
<input id="labeleditor2" size="8" type='text' ng-model="multi.label2" ng-change="updateMulti('label2')" ng-blur="validateMulti('label2')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="widtheditor">Width:</label>
<div class="controls">
<input id="widtheditor" size="6" type='number' min='0.5' max='12' step='.25' ng-model="multi.width" ng-change="updateMulti('width')" ng-blur="validateMulti('width')" ng-disabled="selectedKeys.length<1">
/
<input size="6" type='number' min='0.5' max='12' step='.25' ng-model="multi.width2" ng-change="updateMulti('width2')" ng-blur="validateMulti('width2')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="heighteditor">Height:</label>
<div class="controls">
<input id="heighteditor" size="6" type='number' min='0.5' max='12' step='.25' ng-model="multi.height" ng-change="updateMulti('height')" ng-blur="validateMulti('height')" ng-disabled="selectedKeys.length<1">
/
<input size="6" type='number' min='0.5' max='12' step='.25' ng-model="multi.height2" ng-change="updateMulti('height2')" ng-blur="validateMulti('height2')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="xeditor">X:</label>
<div class="controls">
<input id="xeditor" size="6" type='number' min='0' max='36' step='.25' ng-model="multi.x" ng-change="updateMulti('x')" ng-blur="validateMulti('x')" ng-disabled="selectedKeys.length<1">
+
<input size="6" type='number' min='-6' max='6' step='.25' ng-model="multi.x2" ng-change="updateMulti('x2')" ng-blur="validateMulti('x2')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="yeditor">Y:</label>
<div class="controls">
<input id="yeditor" size="6" type='number' min='0' max='36' step='.25' ng-model="multi.y" ng-change="updateMulti('y')" ng-blur="validateMulti('y')" ng-disabled="selectedKeys.length<1">
+
<input size="6" type='number' min='-6' max='6' step='.25' ng-model="multi.y2" ng-change="updateMulti('y2')" ng-blur="validateMulti('y2')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="coloreditor">Key Color:</label>
<div class="controls">
<input id="coloreditor" size="8" type='text' ng-model="multi.color" ng-change="updateMulti('color')" ng-blur="validateMulti('color')" ng-disabled="selectedKeys.length<1">
<input type='color' class="colorpicker" ng-model="multi.color" ng-change="updateMulti('color')" ng-blur="validateMulti('color')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="textcoloreditor">Label Color:</label>
<div class="controls">
<input id="textcoloreditor" size="8" type='text' ng-model="multi.text" ng-change="updateMulti('text')" ng-blur="validateMulti('text')" ng-disabled="selectedKeys.length<1">
<input type='color' class="colorpicker" ng-model="multi.text" ng-change="updateMulti('text')" ng-blur="validateMulti('text')" ng-disabled="selectedKeys.length<1">
</div>
</div>
<div class="control-group">
<label class="control-label" for="ghosteditor">Ghosted:</label>
<div class="controls">
<input id="ghosteditor" type="checkbox" ng-model="multi.ghost" ng-change="updateMulti('ghost')" ng-blur="validateMulti('ghost')" ng-disabled="selectedKeys.length<1">
</div>
</div>
</form>
<div class="col-md-4 col-lg-4" ng-class="{hidden:!palette.name}">
{{palette.name}} <a ng-href='{{palette.href}}' data-hint="{{palette.description}}" class="hint--top hint--rounded" target="_blank">(more info)</a>
<ul id="swatches" ng-class="{disabled:selectedKeys.length<1}">
<li ng-repeat="color in palette.colors"
ng-style="{'background-color':color.css}"
ng-click="clickSwatch(color,$event)"
class="swatch hint--top"
data-hint="{{color.name}}"></li>
</ul>
</div>
</div>
<div ng-class="{hidden:selTab!=1}" class="col-md-12">
<textarea id="rawdata" spellcheck="false" ng-model="serialized" ng-change="updateFromSerialized()" ng-class="{error:deserializeException!==''}"></textarea>
<div class="alert alert-danger" style='margin-top:1em;margin-bottom:0px' ng-class="{hidden:!deserializeException}">{{deserializeException}}</div>
</div>
</div>
<P></P>
<div ng-class="{hidden:!saved}" class="alert alert-success">
<a class="close" href="#" aria-hidden="true" ng-click="saved=false">&times;</a>
Saved! Your saved layout can be accessed via <a class="alert-link" ng-href="#/layouts/{{saved}}">this link</a>.
</div>
<div ng-class="{hidden:!saveError}" class="alert alert-danger">
<a class="close" href="#" aria-hidden="true" ng-click="saveError=''">&times;</a>
<P>There was an error saving your layout on the server: {{saveError}}</P>
<P>To ensure you don't lose any data, it is recommended that you <a class="alert-link" ng-href="{{getPermalink()}}" target="_blank" ng-click="dirty = false">bookmark this permalink</a>.</P>
</div>
<div ng-class="{hidden:!loadError}" class="alert alert-danger">
<a class="close" href="#" aria-hidden="true" ng-click="loadError=false">&times;</a>
The requested layout does not exist.
</div>
</div>
</div>
<div id="footer">
<div class="container">
<p class="text-muted credit" style="float:left">
Keyboard Layout Editor v{{version}}<br/>
Copyright &copy; 2013 &mdash; Ian Prest<br/>
All rights reserved.
</p>
<div style="float:right; text-alignment:right;">
<a href="#" ng-click="showHelp()"><i class="icon-keyboard"></i> Keyboard shortcuts available</a><br/>
<br/>
<a href="https://github.com/ijprest/keyboard-layout-editor" target="_blank"><i class="icon-github"></i> Code hosted on GitHub</a>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="helpDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog" style='width:70%;left:15%;'>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">How to use keyboard-layout-editor.com</h4>
</div>
<div class="modal-body">
<h5>Getting Started</h5>
<ul>
<li>Try loading one of the preset layouts from the dropdown at the top of the page; it will give you a good idea of the editor's capabilities.</li>
<li>Select a key in the editor by clicking on it; then try changing the various properties in the property-editor form.</li>
<li>You can select multiple keys by holding down CTRL and clicking on them, or extend the current selection by SHIFT-clicking on an item. You can also drag a marquee rectangle around the keys you want to select.</li>
<li>Try the keyboard shortcuts (described below); they make editing various properties much quicker.</li>
<li>You can save your layout to the server by clicking the 'Save' button on the toolbar.</li>
<li>You can save your layout locally by bookmarking the 'Permalink' (at the top of the page) in your bookmarks list.</li>
</ul>
<h5>Keyboard Shortcuts</h5>
<table class='shortcuts col-md-6' style='margin-bottom:5px;'>
<tr><td><span class='shortcut'>?</span></td><td>Show this help dialog</td></tr>
<tr><td><span class='shortcut'>↑↓←→</span></td><td>Move the selected keys</td></tr>
<tr><td><span class='shortcut'>Shift&ndash;↑↓←→</span></td><td>Resize the selected keys</td></tr>
<tr><td><span class='shortcut'>Ins</span></td><td>Add a new key</td></tr>
<tr><td><span class='shortcut'>Del</span></td><td>Delete the selected keys</td></tr>
<tr><td><span class='shortcut'>F2</span></td><td>Edit the text of the selected key</td></tr>
<tr><td><span class='shortcut'>J</span> / <span class='shortcut'>K</span></td><td>Select the previous / next key in the editor.</td></tr>
</table>
<table class='shortcuts col-md-6' style='margin-bottom:5px;'>
<tr><td><span class='shortcut'>Ctrl&ndash;S</span></td><td>Save your layout (on the server)</td></tr>
<tr><td><span class='shortcut'>Ctrl&ndash;A</span> / <span class='shortcut'>Esc</span></td><td>Select / deselect all keys</td></tr>
<tr><td><span class='shortcut'>Ctrl&ndash;Z</span></td><td>Undo</td></tr>
<tr><td><span class='shortcut'>Ctrl&ndash;Shift&ndash;Z</span> or <span class='shortcut'>Ctrl&ndash;Y</span></td><td>Redo</td></tr>
<tr><td><span class='shortcut'>Ctrl&ndash;X</span> or <span class='shortcut'>Shift&ndash;Del</span></td><td>Cut the selected keys</td></tr>
<tr><td><span class='shortcut'>Ctrl&ndash;C</span> or <span class='shortcut'>Ctrl&ndash;Ins</span></td><td>Copy the selected keys</td></tr>
<tr><td><span class='shortcut'>Ctrl&ndash;V</span> or <span class='shortcut'>Shift&ndash;Ins</span></td><td>Paste keys from the clipboard</td></tr>
</table>
<P style='clear:both'>Note that most of these keys require that the keyboard editor has input focus.</P>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>
</body>
</html>