You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
keyboard-layout-editor/kb.html

966 lines
52 KiB
HTML

<!--***********************************************
KEYBOARD LAYOUT EDITOR
Copyright (C) 2013-2015 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="bower_components/bootstrap/dist/css/bootstrap.min.css" media="screen">
<link rel="stylesheet" type="text/css" href="bower_components/fontawesome/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="bower_components/hint.css/hint.min.css">
<link rel="stylesheet" type="text/css" href="bower_components/angular-bootstrap-colorpicker/css/colorpicker.min.css">
<link rel="stylesheet" type="text/css" href="css/kb.css">
<script type="text/javascript" src="bower_components/jquery/jquery.min.js"></script>
<script type="text/javascript" src="bower_components/angular/angular.min.js"></script>
<script type="text/javascript" src="bower_components/angular-sanitize/angular-sanitize.min.js"></script>
<script type="text/javascript" src="bower_components/angular-ui-utils/components/angular-ui-docs/build/ui-utils.min.js"></script>
<script type="text/javascript" src="bower_components/angular-ui-bootstrap/dist/ui-bootstrap-tpls-0.12.0.min.js"></script>
<script type="text/javascript" src="bower_components/crypto-js/crypto-js.js"></script>
<script type="text/javascript" src="bower_components/marked/marked.min.js"></script>
<script type="text/javascript" src="bower_components/FileSaver/FileSaver.min.js"></script>
<script type="text/javascript" src="bower_components/ng-file-upload/ng-file-upload.min.js"></script>
<script type="text/javascript" src="bower_components/angular-native-dragdrop/draganddrop.js"></script>
<script type="text/javascript" src="bower_components/angular-bootstrap-colorpicker/js/bootstrap-colorpicker-module.min.js"></script>
<script type="text/javascript" src="bower_components/doT/doT.min.js"></script>
<script type="text/javascript" src="bower_components/URLON/src/urlon.js"></script>
<script type="text/javascript" src="js/color.js"></script>
<script type="text/javascript" src="js/jsonl.min.js"></script>
<script type="text/javascript" src="extensions.js"></script>
<script type="text/javascript" src="render.js"></script>
<script type="text/javascript" src="serial.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($event)',
112 : 'showHelp($event)',
118 : 'showOptions($event)',
'ctrl-83' : 'save($event)' }">
<div id="wrap">
<!--***********************************************
Nav Bar / Header
************************************************-->
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="navbar-header">
<!-- Hamburger menu, when width is too small -->
<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>
<!-- Main branding icon/title -->
<a class="navbar-brand" href="#"><i class="fa fa-keyboard-o"></i> keyboard-layout-editor.com</a>
</div>
<div class="collapse navbar-collapse navbar-ex1-collapse">
<!-- Left-aligned NavBar buttons -->
<ul class="nav navbar-nav">
<!-- Presets dropdown -->
<li class="dropdown" dropdown>
<a class="dropdown-toggle" dropdown-toggle><i class="fa fa-keyboard-o"></i> Preset <b class="caret"></b></a>
<ul class="dropdown-menu">
<li class="dropdown-header">Standard Layouts:</li>
<li ng-repeat="v in layouts"><a ng-click="loadPreset(v.data)" href="#">{{v.name}}</a></li>
<li class="divider"></li>
<li class="dropdown-header">Complex Samples:</li>
<li ng-repeat="(k,v) in samples"><a ng-click="loadSample(v)" href="#">{{k}}</a></li>
</ul>
</li>
<!-- Color swatches dropdown -->
<li class="dropdown" dropdown>
<a class="dropdown-toggle" dropdown-toggle><i class="fa fa-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>
<!-- Right-aligned NavBar buttons -->
<ul class="nav navbar-nav navbar-right" style="">
<!-- Options button -->
<li><a href="#" ng-click="showOptions()"><i class="fa fa-cog"></i> Options</a></li>
<!-- Permalink button -->
<li><a ng-href="{{getPermalink()}}" target="_blank" ng-click="dirty = false"><i class="fa fa-link"></i> Permalink</a></li>
</ul>
</div>
</nav>
<div class="body" ng-cloak>
<!--***********************************************
Main Toolbar
************************************************-->
<!-- Add Key button w/dropdown -->
<div class="btn-group" dropdown>
<button type="button" class="btn btn-primary" ng-click="addKey()"><i class="fa fa-plus-circle"></i> Add Key</button>
<button type="button" class="btn btn-primary dropdown-toggle" dropdown-toggle>
<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>
<!-- Delete Key button -->
<div class="btn-group">
<button type="button" class="btn btn-danger" ng-class="{disabled:selectedKeys.length<1}" ng-click="deleteKeys()">
<i class="fa fa-minus-circle"></i> Delete Keys
</button>
</div>
<!-- Undo/Redo button group -->
<div class="btn-group">
<button type="button" class="btn btn-default" ng-class="{disabled:!canUndo()}" ng-click="undo()"><i class="fa fa-undo"></i> Undo</button>
<button type="button" class="btn btn-default" ng-class="{disabled:!canRedo()}" ng-click="redo()"><i class="fa fa-repeat"></i> Redo</button>
</div>
<!-- Clipboard button group -->
<div class="btn-group">
<button type="button" class="btn btn-default" ng-class="{disabled:!canCopy()}" ng-click="cut()"><i class="fa fa-cut"></i> Cut</button>
<button type="button" class="btn btn-default" ng-class="{disabled:!canCopy()}" ng-click="copy()"><i class="fa fa-copy"></i> Copy</button>
<button type="button" class="btn btn-default" ng-class="{disabled:!canPaste()}" ng-click="paste()"><i class="fa fa-paste"></i> Paste</button>
</div>
<!-- Save button (right aligned) -->
<div class="btn-group pull-right">
<button type="button" class="btn btn-success" ng-class="{disabled:!canSave()}" ng-click="save()"><i class="fa fa-save"></i> Save</button>
</div>
<div class="btn-group pull-right" style="margin-right: 4px;">
<button type="button" class="btn btn-success" ng-click="downloadSvg()"><i class="fa fa-download"></i> Download SVG (experimental)</button>
</div>
<!--***********************************************
Main Keyboard Preview/Editor area
************************************************-->
<div id="keyboard" ng-cloak
tabindex="0"
ng-style="{height: kbHeight + 'px', 'background-color': keyboard.meta.backcolor}"
ui-keydown="{ left:'moveKeys(-moveStep,0,$event)',
right:'moveKeys(moveStep,0,$event)',
up:'moveKeys(0,-moveStep,$event)',
down:'moveKeys(0,moveStep,$event)',
'shift-left':'sizeKeys(-sizeStep,0,$event)',
'shift-right':'sizeKeys(sizeStep,0,$event)',
'shift-up':'sizeKeys(0,-sizeStep,$event)',
'shift-down':'sizeKeys(0,sizeStep,$event)',
'pageup':'rotateKeys(-rotateStep,$event)',
'pagedown':'rotateKeys(rotateStep,$event)',
'ctrl-left':'moveCenterKeys(-moveStep,0,$event)',
'ctrl-right':'moveCenterKeys(moveStep,0,$event)',
'ctrl-up':'moveCenterKeys(0,-moveStep,$event)',
'ctrl-down':'moveCenterKeys(0,moveStep,$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($event)',
'ctrl-67 ctrl-45': 'copy($event)',
'ctrl-88 shift-46': 'cut($event)',
'ctrl-86 shift-45': 'paste($event)',
'ctrl-90' : 'undo()',
'ctrl-shift-90' : 'redo()',
'ctrl-89' : 'redo()' }"
ng-mousedown="selectClick($event)"
ngf-drop="true" ngf-change="uploadJson($files, $event)" ngf-drag-over-class="drag-over">
<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, HOMING:key.nub}"
ng-bind-html="key.html">
</div>
<div style="position:relative;line-height:1.2;" ng-style="{top:(kbHeight-26)+'px'}"
ng-hide="!keyboard.meta.name &amp;&amp; !keyboard.meta.author">
<a href="#" ng-click="previewNotes()">{{keyboard.meta.name}}<br/><i>{{keyboard.meta.author}}</i></a>
</div>
<div style='clear:both'></div>
{{calcKbHeight()}}
</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 id="rotationCrosshairs" ng-style="{display:multi.crosshairs, left:(keyboardLeft()+multi.crosshairs_x-1)+'px', top:(keyboardTop()+multi.crosshairs_y-3)+'px'}"><i class="fa fa-crosshairs"></i></div>
<!--***********************************************
Editor controls
************************************************-->
<!-- Tab strip -->
<ul class="nav nav-tabs" ng-cloak>
<li ng-class="{active:selTab==0}"><a ng-click="selTab=0" data-toggle="tab"><i class="fa fa-edit"></i> Properties</a></li>
<li ng-class="{active:selTab==1}"><a ng-click="selTab=1" data-toggle="tab"><i class="fa fa-keyboard-o"></i> Keyboard Properties</a></li>
<li ng-class="{active:selTab==2}"><a ng-click="selTab=2" data-toggle="tab"><i class="fa fa-code"></i> Raw data</a></li>
</ul>
<div id="tab-content" class='col-md-12 col-lg-12 row' ui-keydown="{esc:'focusKb()'}" ng-cloak>
<!-- PROPERTIES EDITOR -->
<div id="properties" ng-class="{hidden:selTab!=0}">
<form class="form-horizontal col-sm-8 col-md-5 col-lg-5">
<!-- Top Label -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="labeleditor0">Top Label:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline">
<kbd-label-editor label-index="0" hint-text="Specify the primary text of the keycap; typically displayed top-left, unless the centering options are used."></kbd-label-editor>
<kbd-label-editor label-index="2" hint-text="Specify secondary text for the keycap; typically displayed top-right."></kbd-label-editor>
<kbd-multi-check field="centerx" hint-text="Center the keycap text horizontally; the labels that would normally be left-aligned will be centered instead.">Center X</kbd-multi-check>
</div>
</div>
</div>
<!-- Center Label -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="labeleditor6">Center Label:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline">
<kbd-label-editor label-index="6" hint-text="Specify secondary text for the keycap; typically displayed center-left. You can specify a horizontal rule with <hr>."></kbd-label-editor>
<kbd-label-editor label-index="7" hint-text="Specify secondary text for the keycap; typically displayed center-right."></kbd-label-editor>
<kbd-multi-check field="centery" hint-text="Center the keycap text vertically; the labels that would normally be top-aligned will be centered instead.">Center Y</kbd-multi-check>
</div>
</div>
</div>
<!-- Bottom Label -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="labeleditor1">Bottom Label:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline">
<kbd-label-editor label-index="1" hint-text="Specify secondary text for the keycap; typically displayed bottom-left."></kbd-label-editor>
<kbd-label-editor label-index="3" hint-text="Specify secondary text for the keycap; typically displayed bottom-right."></kbd-label-editor>
</div>
</div>
</div>
<!-- Side-Print Label -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="labeleditor4">Side-Print Label:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline">
<kbd-label-editor label-index="4" hint-text="Specify the primary side-printed text for the keycap."></kbd-label-editor>
<kbd-label-editor label-index="5" hint-text="Specify secondary side-printed text for the keycap."></kbd-label-editor>
<kbd-multi-check field="centerf" hint-text="Center the side-printed text.">Center</kbd-multi-check>
</div>
</div>
</div>
<!-- Legend Size -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="fontheight-editor">Legend Size:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the primary and secondary sizes for the keycap legends. The primary size affects only the primary text legend, while the secondary size is applied to all other legends.">
<kbd-multi-numbox field="fontheight" size="6" min="1" max="9"></kbd-multi-numbox>
<div class="form-group form-group-sm"> / </div>
<kbd-multi-numbox field="fontheight2" size="6" min="1" max="9"></kbd-multi-numbox>
</div>
</div>
</div>
<!-- Width -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="width-editor">Width:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the primary and secondary widths for the keycap. The secondary width is used to specify the size of non-rectangular or stepped keys.">
<kbd-multi-numbox field="width" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> / </div>
<kbd-multi-numbox field="width2" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
</div>
</div>
</div>
<!-- Height -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="height-editor">Height:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the primary and secondary heights for the keycap. The secondary height is used to specify the size of non-rectangular or stepped keys.">
<kbd-multi-numbox field="height" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> / </div>
<kbd-multi-numbox field="height2" size="6" min="0.5" max="12" step='{{sizeStep}}'></kbd-multi-numbox>
</div>
</div>
</div>
<!-- X -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="x-editor">X:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the X position of the keycap. An X-offset can also be specified to indicate how non-rectangular keys should be displayed.">
<kbd-multi-numbox field="x" size="6" min="0" max="36" step='{{moveStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> + </div>
<kbd-multi-numbox field="x2" size="6" min="-6" max="6" step='{{moveStep}}'></kbd-multi-numbox>
</div>
</div>
</div>
<!-- Y -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="y-editor">Y:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the Y position of the keycap. A Y-offset can also be specified to indicate how non-rectangular keys should be displayed.">
<kbd-multi-numbox field="y" size="6" min="0" max="36" step='{{moveStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> + </div>
<kbd-multi-numbox field="y2" size="6" min="-6" max="6" step='{{moveStep}}'></kbd-multi-numbox>
</div>
</div>
</div>
<!-- Profile / Row -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="profileeditor">Profile / Row:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the profile and (optionally) the row-number of the selected keys, e.g., 'DCS R1', 'DSA', 'DSA SPACE', etc.">
<div class="form-group form-group-sm">
<input id="profileeditor" class="form-control input-sm" size="24" type='text'
ng-model="multi.profile"
ng-change="updateMulti('profile')"
ng-blur="validateMulti('profile')"
ng-disabled="selectedKeys.length<1">
</div>
</div>
</div>
</div>
<!-- Key Color -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="coloreditor">Key Color:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline">
<kbd-color-picker picker-id="coloreditor" picker-position="top"
ng-model="multi.color"
ng-change="updateMulti('color')"
ng-blur="validateMulti('color')"
ng-disabled="selectedKeys.length<1"
hint-text="Specify the color to use for the background of the selected keycaps."
ui-on-Drop="dropSwatch($data,$event,false)" drop-channel="dragColor"></kbd-color-picker>
<div class="form-group form-group-sm color-name">{{colorName(multi.color)}}</div>
</div>
<!-- Swap Colors Button -->
<div>
<div id="swap-colors"
class="hint--top hint--rounded"
data-hint="Swap the text and keycap colors.">
<button class="btn btn-default"
ng-click="swapColors()"
ng-disabled="selectedKeys.length<1" >
<i class="fa fa-random"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Label Color -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="textcoloreditor">Label Color:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline">
<kbd-color-picker picker-id="textcoloreditor" picker-position="top"
ng-model="multi.text[0]"
ng-change="updateMulti('text',-1)"
ng-blur="validateMulti('text')"
ng-disabled="selectedKeys.length<1"
hint-text="Specify the color to use for the legend text on the selected keycaps."
ui-on-Drop="dropSwatch($data,$event,true,-1)" drop-channel="dragColor"></kbd-color-picker>
<div class="form-group form-group-sm color-name">{{colorName(multi.text[0])}}</div>
</div>
</div>
</div>
<!-- Rotation -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap" for="rotation_angle-editor">Rotation:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline hint--top hint--rounded"
data-hint="Specify the angle (in degrees) by which to rotate the keycaps, and the x &amp; y coordinates of the center of rotation.">
<kbd-multi-numbox field="rotation_angle" size="3" min="-180" max="180" step='{{rotateStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> &#176; </div>
<kbd-multi-numbox field="rotation_x" size="6" min="0" max="36" step='{{moveStep}}'></kbd-multi-numbox>
<div class="form-group form-group-sm"> , </div>
<kbd-multi-numbox field="rotation_y" size="6" min="0" max="36" step='{{moveStep}}'></kbd-multi-numbox>
</div>
</div>
</div>
<!-- Misc -->
<div class="form-group form-group-sm">
<label class="control-label col-md-3 col-lg-3 text-nowrap">Misc:</label>
<div class="col-md-9 col-lg-9">
<div class="form-inline">
<kbd-multi-check field="ghost" hint-text="Specify whether the selected keys should be rendered slightly transparently; this is useful to draw attention to the other, non-ghosted keycaps.">Ghosted</kbd-multi-check>
<kbd-multi-check field="stepped" hint-text="Specify whether the selected keys are 'stepped', i.e., part of the key is at a lower height than the rest. The secondary 'width' indicates the width of the stepped part.">Stepped</kbd-multi-check>
<kbd-multi-check field="nub" hint-text="Specifies that the selected keys are 'homing' keys, which help the user find the home-row by touch alone.">Homing</kbd-multi-check>
</div>
</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"
ng-class="{'selected-bg': color.css === multi.color, 'selected-fg': color.css === multi.text[0]}"
data-hint="{{color.name}}"
ui-draggable="true" drag-channel="dragColor" drag="color">
<div class='highlight fg'></div>
<div class='highlight bg'></div>
</li>
</ul>
<alert type="warning" style="margin-top:10px;" ng-hide="paletteAlertHidden" close="paletteAlertHidden = true">
<i class="fa fa-info-circle"></i> Click on a swatch to set the color of the selected key(s), or Ctrl+Click to set the text color. You can also drag color swatches to individual labels to set different colors for each one.
</alert>
</div>
</div>
<!-- KEYBOARD PROPERTIES EDITOR -->
<div id="kbdproperties" ng-class="{hidden:selTab!=1}">
<form class="form-horizontal col-sm-12 col-md-12 col-lg-12">
<!-- Keyboard Color -->
<div class="form-group form-group-sm">
<label class="control-label col-md-2 col-lg-1 text-nowrap" for="kbdcoloreditor">Keyboard Color:</label>
<div class="col-md-10 col-lg-11">
<div class="form-inline">
<kbd-color-picker picker-id="kbdcoloreditor"
ng-model="meta.backcolor"
ng-change="updateMeta('backcolor')"
ng-blur="validateMeta('backcolor')"
hint-text="Specify the background color for the keyboard."></kbd-color-picker>
</div>
</div>
</div>
<!-- Keyboard Name -->
<div class="form-group form-group-sm">
<label class="control-label col-md-2 col-lg-1 text-nowrap" for="kbdnameeditor">Keyboard Name:</label>
<div class="col-md-10 col-lg-11 form-outdent hint--top hint--rounded"
data-hint="Specify the name of this keyboard layout.">
<input id="kbdnameeditor" class="form-control input-sm" type='text'
ng-model="meta.name"
ng-change="updateMeta('name')"
ng-blur="validateMeta('name')">
</div>
</div>
<!-- Author -->
<div class="form-group form-group-sm">
<label class="control-label col-md-2 col-lg-1 text-nowrap" for="authoreditor">Author:</label>
<div class="col-md-10 col-lg-11 form-outdent hint--top hint--rounded"
data-hint="Specify the author of this keyboard layout.">
<input id="authoreditor" class="form-control input-sm" type='text'
ng-model="meta.author"
ng-change="updateMeta('author')"
ng-blur="validateMeta('author')">
</div>
</div>
<!-- Notes -->
<div class="form-group form-group-sm">
<label class="control-label col-md-2 col-lg-1 text-nowrap" for="noteseditor">Notes:</label>
<div class="col-md-10 col-lg-11 form-outdent">
<div class="hint--top hint--rounded" style="width:100%"
data-hint="Specify any additional notes about this keyboard layout, e.g., links to manufacturers, colors used, additional credits, etc. GitHub-Flavored Markdown is supported for rich formatting.">
<textarea id="noteseditor" class="form-control" rows="15" spellcheck="true"
ng-model="meta.notes"
ng-change="updateMeta('notes')"
ng-blur="validateMeta('notes')"></textarea>
</div>
<div class="text-right">
<div class="btn-group btn-group-xs" role="group">
<button type="button" class="btn btn-success" ng-click="previewNotes()"><i class="fa fa-eye"></i> Preview</button>
</div>
<div class="btn-group btn-group-xs" role="group">
<a class="btn btn-primary" href="https://help.github.com/articles/markdown-basics/" target='_blank'><i class="fa fa-question-circle"></i> Markdown Syntax</a>
</div>
</div>
</div>
</div>
</form>
</div>
<!-- RAW DATA EDITOR -->
<div id="tab-rawdata" ng-class="{hidden:selTab!=2}">
<div ngf-drop="true" ngf-change="uploadJson($files, $event)" ngf-drag-over-class="drag-over">
<textarea id="rawdata" spellcheck="false" ng-model="serialized" ng-change="updateFromSerialized()" ng-class="{error:deserializeException!==''}" rows="15"></textarea>
</div>
<div style='text-align:right;margin-top:4px;'>
<div class="btn-group btn-group-xs" role="group">
<button type="button" class="btn btn-success" ng-click="downloadJson()"><i class="fa fa-download"></i> Download JSON</button>
<button ngf-select="true" class="btn btn-success" ngf-change="uploadJson($files, $event)"><i class="fa fa-upload"></i> Upload JSON</button>
</div>
<div class="btn-group btn-group-xs" role="group">
<a class="btn btn-primary" href="https://github.com/ijprest/keyboard-layout-editor/wiki/Serialized-Data-Format" target='_blank'><i class="fa fa-question-circle"></i> Raw Data Syntax</a>
</div>
</div>
<div id="rawdata-error" class="alert alert-danger" style='margin-top:1em;margin-bottom:0px' ng-class="{hidden:!deserializeException}">{{deserializeException}}</div>
</div>
</div>
<div class="col-md-12 col-lg-12 row" style="margin-top:1em;margin-left:0px;">
<alert type="success" ng-hide="!saved" close="saved=false" ng-cloak>
Saved! Your saved layout can be accessed via <a class="alert-link" ng-href="#/layouts/{{saved}}">this link</a>.
</alert>
<alert type="danger" ng-hide="!saveError" close="saveError=''" ng-cloak>
<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-click="downloadJson()">download your layout as JSON</a>.</P>
</alert>
<alert type="danger" ng-hide="!loadError" close="loadError=false" ng-cloak>
The requested layout does not exist.
</alert>
</div>
</div>
</div>
<!--***********************************************
Footer
************************************************-->
<div id="footer" ng-cloak>
<div class="container">
<p class="text-muted credit" style="float:left">
Keyboard Layout Editor v{{version}} (<a href='#' ng-click="showMarkdown('CHANGELOG.md', $event)">changelog</a>)<br/>
Copyright &copy; 2013-2015 &mdash; Ian Prest (<a href='#' ng-click="showMarkdown('CONTRIB.md', $event)">and contributors</a>)<br/>
All rights reserved. (<a href='#' ng-click="showMarkdown('LICENSE.md', $event)">LICENSE</a>)
</p>
<div style="float:right;">
<a href="#" ng-click="showHelp($event)"><i class="fa fa-question-circle"></i> Help &amp; keyboard shortcuts</a><br/>
<a href="https://github.com/ijprest/keyboard-layout-editor/issues" target="_blank"><i class="fa fa-bug"></i> Found a bug?</a><br/>
<a href="https://github.com/ijprest/keyboard-layout-editor" target="_blank"><i class="fa fa-github"></i> Code hosted on GitHub</a><br/>
</div>
</div>
</div>
<!--***********************************************
Help Dialog
************************************************-->
<script type="text/ng-template" id="helpDialog.html">
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()">&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.</li>
<ul>
<li>The <em>standard layouts</em> are intended to be used as a starting point for customization.</li>
<li>The <em>complex samples</em> are intended to give you an idea of the editor's capabilities, and to show off some awesome user-creations!</li>
</ul>
<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>Global Keyboard Shortcuts:</h5>
<dl class='dl-horizontal'>
<dt><kbd>F1</kbd> / <kbd>?</kbd></dt><dd>Show this help dialog</dd>
<dt><kbd>F7</kbd></dt><dd>Show the Options dialog</dd>
<dt><kbd>Ctrl&ndash;S</kbd></dt><dd>Save your layout (on the server)</dd>
</dl>
<h5>Editor Keyboard Shortcuts (these require that the editor has input focus):</h5>
<dl class='dl-horizontal' style='float:left;width:50%;'>
<dt><kbd></kbd></dt><dd>Move the selected keys</dd>
<dt><kbd>Shift&ndash;</kbd></dt><dd>Resize the selected keys</dd>
<dt><kbd>Ctrl&ndash;</kbd></dt><dd>Move the center of rotation for the selected keys</dd>
<dt><kbd>PgUp</kbd> / <kbd>PgDn</kbd></dt><dd>Change the angle of rotation for the selected keys</dd>
<dt><kbd>Ins</kbd></dt><dd>Add a new key</dd>
<dt><kbd>Del</kbd></dt><dd>Delete the selected keys</dd>
<dt><kbd>F2</kbd></dt><dd>Edit the text of the selected key</dd>
</dl>
<dl class='dl-horizontal' style='float:left;width:50%;'>
<dt><kbd>Ctrl&ndash;A</kbd> / <kbd>Esc</kbd></dt><dd>Select / deselect all keys</dd>
<dt><kbd>J</kbd> / <kbd>K</kbd></dt><dd>Select the previous / next key in the editor.</dd>
<dt><kbd>Ctrl&ndash;Z</kbd></dt><dd>Undo</dd>
<dt><kbd>Ctrl&ndash;Shift&ndash;Z</kbd> or <kbd>Ctrl&ndash;Y</kbd></dt><dd>Redo</dd>
<dt><kbd>Ctrl&ndash;X</kbd> or <kbd>Shift&ndash;Del</kbd></dt><dd>Cut the selected keys</dd>
<dt><kbd>Ctrl&ndash;C</kbd> or <kbd>Ctrl&ndash;Ins</kbd></dt><dd>Copy the selected keys</dd>
<dt><kbd>Ctrl&ndash;V</kbd> or <kbd>Shift&ndash;Ins</kbd></dt><dd>Paste keys from the clipboard</dd>
</dl>
<p>&nbsp;</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="ok()">OK</button>
</div>
</script>
<!--***********************************************
Markdown dialog for displaying README, etc.
************************************************-->
<script type="text/ng-template" id="markdownDialog.html">
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()">&times;</button>
<h4 class="modal-title">{{$parent.markdownTitle}}</h4>
</div>
<div class="modal-body" ng-bind-html="$parent.markdownContent">&nbsp;</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="ok()">OK</button>
</div>
</script>
<!--***********************************************
Options Dialog
************************************************-->
<script type="text/ng-template" id="optionsDialog.html">
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()">&times;</button>
<h4 class="modal-title">Options</h4>
</div>
<div class="modal-body">
<h5>Editor Step Sizes:</h5>
<div class="form-horizontal">
<div class="form-group form-group-sm">
<label for="moveStepEditor" class="col-sm-6 col-md-3 col-lg-2 control-label">Move Step:</label>
<div class="col-sm-6 col-md-9 col-lg-10">
<div class="form-inline">
<div class="form-group form-group-sm">
<div class="input-group input-group-sm hint--top hint--rounded"
data-hint="Controls the step size when moving keys around the keyboard layout.">
<input id="moveStepEditor" class="form-control" type="number" min="0.05" max="2.5" step="0.05" size="6" ng-model="params.moveStep">
<span class="input-group-addon">units</span>
</div>
</div>
</div>
</div>
</div>
<div class="form-group form-group-sm">
<label for="sizeStepEditor" class="col-sm-6 col-md-3 col-lg-2 control-label">Size Step:</label>
<div class="col-sm-6 col-md-9 col-lg-10">
<div class="form-inline">
<div class="form-group form-group-sm">
<div class="input-group input-group-sm hint--top hint--rounded"
data-hint="Controls the step size when resizing keys in the keyboard layout.">
<input id="sizeStepEditor" class="form-control" type="number" min="0.05" max="2.5" step="0.05" size="6" ng-model="params.sizeStep">
<span class="input-group-addon">units</span>
</div>
</div>
</div>
</div>
</div>
<div class="form-group form-group-sm">
<label for="rotateStepEditor" class="col-sm-6 col-md-3 col-lg-2 control-label">Rotate Step:</label>
<div class="col-sm-6 col-md-9 col-lg-10">
<div class="form-inline">
<div class="form-group form-group-sm">
<div class="input-group input-group-sm hint--top hint--rounded"
data-hint="Controls the step size when rotating keys in the keyboard layout.">
<input id="rotateStepEditor" class="form-control" type="number" min="1" max="90" step="1" size="6" ng-model="params.rotateStep">
<span class="input-group-addon">degrees</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" ng-click="cancel()">Cancel</button>
<button type="button" class="btn btn-primary" ng-click="ok()">OK</button>
</div>
</script>
<!--***********************************************
Angular Templates
************************************************-->
<!-- Color Picker Template -->
<script type="text/ng-template" id="colorPicker.html">
<div class="form-group form-group-sm">
<div class="input-group input-group-sm hint--top hint--rounded" ng-attr-data-hint="{{hintText}}">
<input id="{{pickerId}}" class="form-control" size="8" type='text'
ng-model="color" ng-change="onChange()" ng-blur="onBlur()" ng-disabled="isDisabled &amp;&amp; isDisabled()"
colorpicker="hex" colorpicker-close-on-select="true" colorpicker-position="{{pickerPosition || 'bottom'}}">
<span class="input-group-btn">
<button class="btn btn-default" ng-style="{'background-color':color}" ng-model="color" ng-change="onChange()" ng-disabled="isDisabled &amp;&amp; isDisabled()"
colorpicker="hex" colorpicker-close-on-select="true" colorpicker-position="{{pickerPosition || 'bottom'}}">&nbsp;</button>
</span>
</div>
</div>
</script>
<!-- Simple Editor Templates -->
<script type="text/ng-template" id="multiCheck.html">
<div class="form-group form-group-sm">
<div class="checkbox hint--top hint--rounded" ng-attr-data-hint="{{hintText}}">
<label>
<input id="{{field}}" type="checkbox"
ng-model="$parent.multi[field]"
ng-change="$parent.updateMulti(field)"
ng-blur="$parent.validateMulti(field)"
ng-disabled="$parent.selectedKeys.length<1">
<span ng-transclude></span>
</label>
</div>
</div>
</script>
<script type="text/ng-template" id="multiNumbox.html">
<div class="form-group form-group-sm">
<input id="{{field}}-editor" class="form-control input-sm" size="{{size}}" type='number' min="{{min}}" max="{{max}}" step="{{step || 1}}"
ng-model="$parent.multi[field]"
ng-change="$parent.updateMulti(field)"
ng-blur="$parent.validateMulti(field)"
ng-disabled="$parent.selectedKeys.length<1">
</div>
</script>
<!-- Label Editor Template -->
<script type="text/ng-template" id="labelEditor.html">
<div class="form-group form-group-sm">
<div class="input-group input-group-sm hint--top hint--rounded" ng-attr-data-hint="{{hintText}}"
ui-on-Drop="$parent.dropSwatch($data,$event,true,labelIndex)" drop-channel="dragColor">
<input id="labeleditor{{labelIndex}}" class="form-control" size="6" type='text'
ng-model="$parent.multi.labels[labelIndex]"
ng-change="$parent.updateMulti('labels',labelIndex)"
ng-blur="$parent.validateMulti('labels',labelIndex)"
ng-disabled="$parent.labelDisabled(labelIndex)">
<span class="input-group-btn">
<button class="btn btn-default"
ng-style="{'background-color':$parent.getTextColor($parent.multi.text,labelIndex)}"
ng-model="$parent.multi.text[labelIndex]"
ng-change="$parent.updateMulti('text',labelIndex)"
colorpicker="hex"
colorpicker-close-on-select="true"
colorpicker-with-input="true"
ng-disabled="$parent.labelDisabled(labelIndex)">&nbsp;</button>
</span>
</div>
</div>
</script>
<!--***********************************************
DOT.js Templates
************************************************-->
<!-- Keycap Template -->
<script type="text/ng-template" id="keycap_html">
<div class='{{=key.ghost ? "ghosted keycap" : "keycap"}}'
{{? key.rotation_angle }}
style='transform:rotate({{=key.rotation_angle}}deg); transform-origin:{{=parms.origin_x}}px {{=parms.origin_y}}px;'
{{?}}>
<div style="left: {{=parms.capx}}px; top: {{=parms.capy}}px;
width: {{=parms.capwidth}}px; height: {{=parms.capheight}}px;
border: solid {{=sizes.strokeWidth}}px black; border-radius: {{=sizes.roundOuter}}px;
background-color: {{=parms.darkColor}};"
class="keyborder"></div>
{{? parms.jShaped }}
<div style="left: {{=parms.capx2}}px; top: {{=parms.capy2}}px;
width: {{=parms.capwidth2}}px; height: {{=parms.capheight2}}px;
border: solid {{=sizes.strokeWidth}}px black; border-radius: {{=sizes.roundOuter}}px;
background-color: {{=parms.darkColor}};"
class="keyborder"></div>
<div style="left: {{=parms.capx + sizes.strokeWidth}}px; top: {{=parms.capy + sizes.strokeWidth}}px;
width: {{=parms.capwidth - sizes.strokeWidth*2}}px;
height: {{=parms.capheight - sizes.strokeWidth*2}}px;
background-color: {{=parms.darkColor}};
border-radius: {{=sizes.roundOuter}}px;"></div>
{{?}}
{{? !key.ghost }}
<div style="left: {{=parms.innercapx}}px; top: {{=parms.innercapy}}px;
width: {{=parms.innercapwidth}}px; height: {{=parms.innercapheight}}px;
border: solid {{=sizes.strokeWidth}}px rgba(0,0,0,0.1);
background-color: {{=parms.lightColor}};
border-radius: {{=sizes.roundInner}}px;"
class="keytop"></div>
{{? parms.jShaped && !key.stepped }}
<div style="left: {{=parms.innercapx2}}px; top: {{=parms.innercapy2}}px;
width: {{=parms.innercapwidth2}}px; height: {{=parms.innercapheight2}}px;
border: solid {{=sizes.strokeWidth}}px rgba(0,0,0,0.1); border-radius: {{=sizes.roundInner}}px;
background-color: {{=parms.lightColor}};
background-position: {{=Math.min(parms.innercapx-parms.innercapx2,0)}}px {{=Math.min(parms.innercapy-parms.innercapy2,0)}}px;
background-size: {{=Math.max(parms.innercapwidth,parms.innercapwidth2)}}px {{=Math.max(parms.innercapheight,parms.innercapheight2)}}px;"
class="keytop"></div>
<div style="left: {{=parms.innercapx + sizes.strokeWidth}}px; top: {{=parms.innercapy + sizes.strokeWidth}}px;
width: {{=parms.innercapwidth - sizes.strokeWidth*2}}px; height: {{=parms.innercapheight - sizes.strokeWidth*2}}px;
border-radius: {{=sizes.roundInner}}px;
background-color: {{=parms.lightColor}};
background-position: {{=Math.min(parms.innercapx2-parms.innercapx,0)}}px {{=Math.min(parms.innercapy2-parms.innercapy,0)}}px;
background-size: {{=Math.max(parms.innercapwidth,parms.innercapwidth2)}}px {{=Math.max(parms.innercapheight,parms.innercapheight2)}}px;"
class="keytop"></div>
{{?}}
<div style='left: {{=parms.innercapx}}px; top: {{=parms.innercapy}}px; width: {{=parms.innercapwidth}}px; height: {{=parms.innercapheight}}px; padding: {{=sizes.padding}}px;' class='keylabels'>
{{~key.labels :label:i}}
{{? label && label != "" && !(key.align&$renderKey.noRenderText[i]) }}
{{ var sanitizedLabel = ""; try { sanitizedLabel = $sanitize(label.replace(/<([^a-zA-Z\/]|$)/,"&lt;$1")); } catch(e) {console.log(e);} }}
{{? sanitizedLabel !== "" }}
{{ var textColor = i < key.text.length ? key.text[i] : key.text[0]; if(!textColor) textColor = key.text[0]; }}
{{ var textColorLight = lightenColor($color.hex(textColor), 1.2).hex(); }}
<div class='keylabel keylabel{{=i+1}} centerx-{{=key.centerx}} centery-{{=key.centery}} centerf-{{=key.centerf}} textsize{{= i>0 ? key.fontheight2 : key.fontheight}}' style='color:{{= i===4||i===5 ? textColor : textColorLight}}; width:{{=parms.textcapwidth}}px; height:{{=parms.textcapheight}}px;'>
<div style='width:{{=parms.textcapwidth}}px; max-width:{{=parms.textcapwidth}}px; height:{{=parms.textcapheight}}px;'>{{=sanitizedLabel}}</div>
</div>
{{??}}
<div class="hint--top hint--rounded" data-hint="Error: Invalid HTML in label field."><i class="fa fa-times-circle"></i></div>
{{?}}
{{?}}
{{~}}
</div>
{{?}} <!--!key.ghost-->
</div>
</script>
<!-- Keycap Template for SVG-->
<script type="text/ng-template" id="keycap_svg">
<g class='{{=key.ghost ? "ghosted keycap" : "keycap"}}'
{{? key.rotation_angle }}
transform="rotate({{=key.rotation_angle}} {{=parms.origin_x}} {{=parms.origin_y}})"
{{?}}>
<!-- Outer Border -->
<rect x="{{=parms.capx+sizes.strokeWidth}}" y="{{=parms.capy+sizes.strokeWidth}}"
width="{{=parms.capwidth-sizes.strokeWidth*2}}" height="{{=parms.capheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.darkColor}}" class="outer border"/>
{{? parms.jShaped }}
<rect x="{{=parms.capx2+sizes.strokeWidth}}" y="{{=parms.capy2+sizes.strokeWidth}}"
width="{{=parms.capwidth2-sizes.strokeWidth*2}}" height="{{=parms.capheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.darkColor}}" class="outer border"/>
{{?}}
<!-- Outer Fill -->
<rect x="{{=parms.capx+sizes.strokeWidth}}" y="{{=parms.capy+sizes.strokeWidth}}"
width="{{=parms.capwidth-sizes.strokeWidth*2}}" height="{{=parms.capheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.darkColor}}"/>
{{? parms.jShaped }}
<rect x="{{=parms.capx2+sizes.strokeWidth}}" y="{{=parms.capy2+sizes.strokeWidth}}"
width="{{=parms.capwidth2-sizes.strokeWidth*2}}" height="{{=parms.capheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.darkColor}}"/>
{{?}}
{{? !key.ghost }}
<!-- Inner Border -->
<rect x="{{=parms.innercapx+sizes.strokeWidth}}" y="{{=parms.innercapy+sizes.strokeWidth}}"
width="{{=parms.innercapwidth-sizes.strokeWidth*2}}" height="{{=parms.innercapheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}" class="inner border"/>
{{? parms.jShaped && !key.stepped }}
<rect x="{{=parms.innercapx2+sizes.strokeWidth}}" y="{{=parms.innercapy2+sizes.strokeWidth}}"
width="{{=parms.innercapwidth2-sizes.strokeWidth*2}}" height="{{=parms.innercapheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}" class="inner border"/>
{{?}}
<!-- Inner Fill -->
<rect x="{{=parms.innercapx+sizes.strokeWidth}}" y="{{=parms.innercapy+sizes.strokeWidth}}"
width="{{=parms.innercapwidth-sizes.strokeWidth*2}}" height="{{=parms.innercapheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}"/>
{{? parms.jShaped && !key.stepped }}
<rect x="{{=parms.innercapx2+sizes.strokeWidth}}" y="{{=parms.innercapy2+sizes.strokeWidth}}"
width="{{=parms.innercapwidth2-sizes.strokeWidth*2}}" height="{{=parms.innercapheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}"/>
{{?}}
{{? sizes.profile !== "" }}
{{var theProfile = sizes.profile; if(/\b(SPACE)\b/.exec(key.profile)) theProfile = "SPACE";}}
{{? parms.jShaped && !key.stepped }}
{{
/*much harder to do j-shaped keys in SVG*/
theProfile = '_fill'+parms.index;
var rect = {
left: Math.min(parms.innercapx, parms.innercapx2),
top: Math.min(parms.innercapy, parms.innercapy2),
right: Math.min(parms.innercapx+parms.innercapwidth, parms.innercapx2+parms.innercapwidth2),
bottom: Math.min(parms.innercapy+parms.innercapheight, parms.innercapy2+parms.innercapheight2)
};
}}
{{? sizes.profile==="SA" || sizes.profile==="DSA" }}
<radialGradient id="{{=theProfile}}" xlink:href="#{{=sizes.profile}}" gradientUnits="userSpaceOnUse"
cx="{{=(rect.left+rect.right)/2}}" cy="{{=(rect.top+rect.bottom)/2}}"
r="{{=Math.min(rect.right-rect.left, rect.bottom-rect.top)}}"/>
{{??}}
<linearGradient id="{{=theProfile}}" xlink:href="#{{=sizes.profile}}" gradientUnits="userSpaceOnUse"
x1="{{=rect.left}}" y1="{{=rect.top}}" x2="{{=rect.right}}" y2="{{=rect.top}}"/>
{{?}}
<rect x="{{=parms.innercapx2+sizes.strokeWidth}}" y="{{=parms.innercapy2+sizes.strokeWidth}}"
width="{{=parms.innercapwidth2-sizes.strokeWidth*2}}" height="{{=parms.innercapheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}"/>
<rect x="{{=parms.innercapx2+sizes.strokeWidth}}" y="{{=parms.innercapy2+sizes.strokeWidth}}"
width="{{=parms.innercapwidth2-sizes.strokeWidth*2}}" height="{{=parms.innercapheight2-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="url(#{{=theProfile}})"/>
<rect x="{{=parms.innercapx+sizes.strokeWidth}}" y="{{=parms.innercapy+sizes.strokeWidth}}"
width="{{=parms.innercapwidth-sizes.strokeWidth*2}}" height="{{=parms.innercapheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="{{=parms.lightColor}}"/>
{{?}}
<rect x="{{=parms.innercapx+sizes.strokeWidth}}" y="{{=parms.innercapy+sizes.strokeWidth}}"
width="{{=parms.innercapwidth-sizes.strokeWidth*2}}" height="{{=parms.innercapheight-sizes.strokeWidth*2}}"
rx="{{=sizes.roundOuter}}" fill="url(#{{=theProfile}})"/>
{{?}}
{{?}}
</g>
</script>
<script type="text/ng-template" id="keyboard_svg">
<svg width='{{=parms.width+parms.margin*2+parms.padding*2}}{{=parms.units}}'
height='{{=parms.height+parms.margin*2+parms.padding*2}}{{=parms.units}}'
viewBox='0 0 {{=parms.width+parms.margin*2+parms.padding*2}} {{=parms.height+parms.margin*2+parms.padding*2}}'
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink="http://www.w3.org/1999/xlink">
<style type='text/css'>
.keycap .border { stroke: black; stroke-width: {{=parms.strokeWidth*2}}; }
.keycap .inner.border { stroke: rgba(0,0,0,.1); }
</style>
<defs>
<linearGradient id="DCS">
<stop offset="0%" stop-color="black" stop-opacity="0"/>
<stop offset="40%" stop-color="black" stop-opacity="0.1"/>
<stop offset="60%" stop-color="black" stop-opacity="0.1"/>
<stop offset="100%" stop-color="black" stop-opacity="0"/>
</linearGradient>
<linearGradient id="SPACE" x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stop-color="black" stop-opacity="0.1"/>
<stop offset="20%" stop-color="black" stop-opacity="0.0"/>
<stop offset="40%" stop-color="black" stop-opacity="0.0"/>
<stop offset="100%" stop-color="black" stop-opacity="0.1"/>
</linearGradient>
<radialGradient id="DSA">
<stop offset="0%" stop-color="black" stop-opacity="0.1"/>
<stop offset="10%" stop-color="black" stop-opacity="0.1"/>
<stop offset="100%" stop-color="black" stop-opacity="0"/>
</radialGradient>
<radialGradient id="SA" xlink:href="#DSA" />
</defs>
<g transform='translate({{=parms.margin}},{{=parms.margin}})'>
<rect width="{{=parms.width+parms.padding*2}}" height="{{=parms.height+parms.padding*2}}"
stroke="#ddd" stroke-width="1" fill="{{=parms.backcolor}}" rx="6"/>
<g transform='translate({{=parms.padding}},{{=parms.padding}})'>
{{=parms.keys}}
</g>
</g>
</svg>
</script>
</body>
</html>