mirror of https://github.com/keeweb/keeweb.git
generator presets
This commit is contained in:
parent
e978f2adb8
commit
f94493a82c
|
@ -0,0 +1,127 @@
|
|||
'use strict';
|
||||
|
||||
const AppSettingsModel = require('../models/app-settings-model');
|
||||
const Locale = require('../util/locale');
|
||||
|
||||
let GeneratorPresets = {
|
||||
get defaultPreset() {
|
||||
return { name: 'Default', title: Locale.genPresetDefault,
|
||||
length: 16, upper: true, lower: true, digits: true };
|
||||
},
|
||||
|
||||
get builtIn() {
|
||||
return [
|
||||
this.defaultPreset,
|
||||
{ name: 'Pronounceable', title: Locale.genPresetPronounceable,
|
||||
length: 10, lower: true, upper: true },
|
||||
{ name: 'Med', title: Locale.genPresetMed,
|
||||
length: 16, upper: true, lower: true, digits: true, special: true, brackets: true, ambiguous: true },
|
||||
{ name: 'Long', title: Locale.genPresetLong,
|
||||
length: 32, upper: true, lower: true, digits: true },
|
||||
{ name: 'Pin4', title: Locale.genPresetPin4,
|
||||
length: 4, digits: true },
|
||||
{ name: 'Mac', title: Locale.genPresetMac,
|
||||
length: 17, upper: true, digits: true, special: true },
|
||||
{ name: 'Hash128', title: Locale.genPresetHash128,
|
||||
length: 32, lower: true, digits: true },
|
||||
{ name: 'Hash256', title: Locale.genPresetHash256,
|
||||
length: 64, lower: true, digits: true }
|
||||
];
|
||||
},
|
||||
|
||||
get all() {
|
||||
let presets = this.builtIn;
|
||||
presets.forEach(preset => { preset.builtIn = true; });
|
||||
let setting = AppSettingsModel.instance.get('generatorPresets');
|
||||
if (setting) {
|
||||
if (setting.user) {
|
||||
presets = presets.concat(setting.user.map(_.clone));
|
||||
}
|
||||
let hasDefault = false;
|
||||
presets.forEach(preset => {
|
||||
if (setting.disabled && setting.disabled[preset.name]) {
|
||||
preset.disabled = true;
|
||||
}
|
||||
if (setting.default === preset.name) {
|
||||
hasDefault = true;
|
||||
preset.default = true;
|
||||
}
|
||||
});
|
||||
if (!hasDefault) {
|
||||
presets[0].default = true;
|
||||
}
|
||||
}
|
||||
return presets;
|
||||
},
|
||||
|
||||
get enabled() {
|
||||
let allPresets = this.all.filter(preset => !preset.disabled);
|
||||
if (!allPresets.length) {
|
||||
allPresets.push(this.defaultPreset);
|
||||
}
|
||||
return allPresets;
|
||||
},
|
||||
|
||||
getOrCreateSetting() {
|
||||
let setting = AppSettingsModel.instance.get('generatorPresets');
|
||||
if (!setting) {
|
||||
setting = { user: [] };
|
||||
}
|
||||
return setting;
|
||||
},
|
||||
|
||||
add(preset) {
|
||||
let setting = this.getOrCreateSetting();
|
||||
if (preset.name && !setting.user.filter(p => p.name === preset.name).length) {
|
||||
setting.user.push(preset);
|
||||
this.save(setting);
|
||||
}
|
||||
},
|
||||
|
||||
remove(name) {
|
||||
let setting = this.getOrCreateSetting();
|
||||
setting.user = setting.user.filter(p => p.name !== name);
|
||||
this.save(setting);
|
||||
},
|
||||
|
||||
setPreset(name, props) {
|
||||
let setting = this.getOrCreateSetting();
|
||||
let preset = setting.user.filter(p => p.name === name)[0];
|
||||
if (preset) {
|
||||
_.extend(preset, props);
|
||||
this.save(setting);
|
||||
}
|
||||
},
|
||||
|
||||
setDisabled(name, disabled) {
|
||||
let setting = this.getOrCreateSetting();
|
||||
if (disabled) {
|
||||
if (!setting.disabled) {
|
||||
setting.disabled = {};
|
||||
}
|
||||
setting.disabled[name] = true;
|
||||
} else {
|
||||
if (setting.disabled) {
|
||||
delete setting.disabled[name];
|
||||
}
|
||||
}
|
||||
this.save(setting);
|
||||
},
|
||||
|
||||
setDefault(name) {
|
||||
let setting = this.getOrCreateSetting();
|
||||
if (name) {
|
||||
setting.default = name;
|
||||
} else {
|
||||
delete setting.default;
|
||||
}
|
||||
this.save(setting);
|
||||
},
|
||||
|
||||
save: function(setting) {
|
||||
AppSettingsModel.instance.unset('generatorPresets', { silent: true });
|
||||
AppSettingsModel.instance.set('generatorPresets', setting);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = GeneratorPresets;
|
|
@ -89,11 +89,21 @@ var Locale = {
|
|||
tagBadNameBody: 'Tag name can not contain characters `,`, `;`, `:`. Please remove them.',
|
||||
|
||||
genPsTitle: 'Generator Presets',
|
||||
genPsEmpty: 'You have no presets yet',
|
||||
genPsEmptyDesc: 'Presets allow you to generate passwords by your rules faster',
|
||||
genPsCreate: 'New preset',
|
||||
genPsDelete: 'Delete preset',
|
||||
genPsNew: 'preset',
|
||||
genPsEnabled: 'Show in presets list',
|
||||
genPsDefault: 'Selected by default',
|
||||
genPsDefaultLength: 'Default length',
|
||||
genPsUpper: 'Uppercase latin letters',
|
||||
genPsLower: 'Lowercase latin letters',
|
||||
genPsDigits: 'Digits',
|
||||
genPsSpecial: 'Special symbols',
|
||||
genPsBrackets: 'Brackets',
|
||||
genPsHigh: 'High ASCII characters',
|
||||
genPsAmbiguous: 'Ambiguous symbols',
|
||||
genPsInclude: 'Additional symbols to include',
|
||||
genPsExample: 'Example of generated password',
|
||||
|
||||
keyChangeTitleRemote: 'Master Key Changed',
|
||||
keyChangeMessageRemote: 'Master key was changed for this database. Please enter a new key',
|
||||
|
|
|
@ -31,6 +31,9 @@ var PasswordGenerator = {
|
|||
var ranges = Object.keys(this.charRanges)
|
||||
.filter(r => opts[r])
|
||||
.map(function(r) { return this.charRanges[r]; }, this);
|
||||
if (opts.include && opts.include.length) {
|
||||
ranges.push(opts.include);
|
||||
}
|
||||
if (!ranges.length) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
const Backbone = require('backbone');
|
||||
const Scrollable = require('../mixins/scrollable');
|
||||
const Locale = require('../util/locale');
|
||||
const GeneratorPresets = require('../comp/generator-presets');
|
||||
const PasswordGenerator = require('../util/password-generator');
|
||||
|
||||
let GeneratorPresetsView = Backbone.View.extend({
|
||||
template: require('templates/generator-presets.hbs'),
|
||||
|
@ -11,87 +14,165 @@ let GeneratorPresetsView = Backbone.View.extend({
|
|||
'change .gen-ps__list': 'changePreset',
|
||||
'click .gen-ps__btn-create': 'createPreset',
|
||||
'click .gen-ps__btn-delete': 'deletePreset',
|
||||
'input #gen-ps__field-name': 'changeName'
|
||||
'input #gen-ps__field-title': 'changeTitle',
|
||||
'change #gen-ps__check-enabled': 'changeEnabled',
|
||||
'change #gen-ps__check-default': 'changeDefault',
|
||||
'input #gen-ps__field-length': 'changeLength',
|
||||
'change .gen-ps__check-range': 'changeRange',
|
||||
'input #gen-ps__field-include': 'changeInclude'
|
||||
},
|
||||
|
||||
selected: null,
|
||||
|
||||
reservedTitles: [Locale.genPresetDerived],
|
||||
|
||||
initialize: function() {
|
||||
this.appModel = this.model;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let presets = this.appModel.settings.get('generatorPresets') || [];
|
||||
if (!this.selected || presets.indexOf(this.selected) < 0) {
|
||||
this.selected = presets[0];
|
||||
this.presets = GeneratorPresets.all;
|
||||
if (!this.selected || !this.presets.some(p => p.name === this.selected)) {
|
||||
this.selected = (this.presets.filter(p => p.default)[0] || this.presets[0]).name;
|
||||
}
|
||||
this.renderTemplate({
|
||||
empty: !presets.length,
|
||||
presets: presets,
|
||||
selected: this.selected
|
||||
presets: this.presets,
|
||||
selected: this.getPreset(this.selected),
|
||||
ranges: this.getSelectedRanges()
|
||||
}, true);
|
||||
this.createScroll({
|
||||
root: this.$el.find('.gen-ps')[0],
|
||||
scroller: this.$el.find('.scroller')[0],
|
||||
bar: this.$el.find('.scroller__bar')[0]
|
||||
});
|
||||
this.renderExample();
|
||||
return this;
|
||||
},
|
||||
|
||||
renderExample: function() {
|
||||
let selectedPreset = this.getPreset(this.selected);
|
||||
let example = PasswordGenerator.generate(selectedPreset);
|
||||
this.$el.find('.gen-ps__example').text(example);
|
||||
this.pageResized();
|
||||
},
|
||||
|
||||
getSelectedRanges: function() {
|
||||
let sel = this.getPreset(this.selected);
|
||||
let rangeOverride = {
|
||||
high: '¡¢£¤¥¦§©ª«¬®¯°±¹²´µ¶»¼÷¿ÀÖîü...'
|
||||
};
|
||||
return ['Upper', 'Lower', 'Digits', 'Special', 'Brackets', 'High', 'Ambiguous'].map(name => {
|
||||
let nameLower = name.toLowerCase();
|
||||
return {
|
||||
name: nameLower,
|
||||
title: Locale['genPs' + name],
|
||||
enabled: sel[nameLower],
|
||||
sample: rangeOverride[nameLower] || PasswordGenerator.charRanges[nameLower]
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
getPreset: function(name) {
|
||||
return this.presets.filter(p => p.name === name)[0];
|
||||
},
|
||||
|
||||
returnToApp: function() {
|
||||
Backbone.trigger('edit-generator-presets');
|
||||
},
|
||||
|
||||
changePreset: function(e) {
|
||||
let id = e.target.value;
|
||||
let presets = this.appModel.settings.get('generatorPresets');
|
||||
this.selected = presets.filter(p => p.id === id)[0];
|
||||
this.selected = e.target.value;
|
||||
this.render();
|
||||
},
|
||||
|
||||
createPreset: function() {
|
||||
let presets = this.appModel.settings.get('generatorPresets') || [];
|
||||
let name;
|
||||
let id;
|
||||
let title;
|
||||
for (let i = 1; ; i++) {
|
||||
let newName = Locale.genPsNew + ' ' + i;
|
||||
if (!presets.filter(p => p.name === newName).length) {
|
||||
let newName = 'Custom' + i;
|
||||
let newTitle = Locale.genPsNew + ' ' + i;
|
||||
if (!this.presets.filter(p => p.name === newName || p.title === newTitle).length) {
|
||||
name = newName;
|
||||
title = newTitle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let i = 1; ; i++) {
|
||||
let newId = 'custom' + i;
|
||||
if (!presets.filter(p => p.id === newId).length) {
|
||||
id = newId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let preset = { id, name };
|
||||
presets.push(preset);
|
||||
this.selected = preset;
|
||||
this.appModel.settings.set('generatorPresets', presets);
|
||||
let selected = this.getPreset(this.selected);
|
||||
let preset = {
|
||||
name, title,
|
||||
length: selected.length,
|
||||
upper: selected.upper, lower: selected.lower, digits: selected.digits,
|
||||
special: selected.special, brackets: selected.brackets, ambiguous: selected.ambiguous,
|
||||
include: selected.include
|
||||
};
|
||||
GeneratorPresets.add(preset);
|
||||
this.selected = name;
|
||||
this.render();
|
||||
},
|
||||
|
||||
deletePreset: function() {
|
||||
let presets = this.appModel.settings.get('generatorPresets');
|
||||
presets = presets.filter(p => p.id !== this.selected.id);
|
||||
this.appModel.settings.set('generatorPresets', presets.length ? presets : null);
|
||||
GeneratorPresets.remove(this.selected);
|
||||
this.render();
|
||||
},
|
||||
|
||||
changeName: function(e) {
|
||||
let name = $.trim(e.target.value);
|
||||
if (name && name !== this.selected.name) {
|
||||
let presets = this.appModel.settings.get('generatorPresets');
|
||||
let another = presets.filter(p => p.name.toLowerCase() === name.toLowerCase())[0];
|
||||
if (another) {
|
||||
changeTitle: function(e) {
|
||||
let title = $.trim(e.target.value);
|
||||
if (title && title !== this.getPreset(this.selected).title) {
|
||||
let duplicate = this.presets.some(p => p.title.toLowerCase() === title.toLowerCase());
|
||||
if (!duplicate) {
|
||||
duplicate = this.reservedTitles.some(p => p.toLowerCase() === title.toLowerCase());
|
||||
}
|
||||
if (duplicate) {
|
||||
$(e.target).addClass('input--error');
|
||||
return;
|
||||
} else {
|
||||
$(e.target).removeClass('input--error');
|
||||
}
|
||||
this.selected.name = name;
|
||||
this.appModel.settings.set('generatorPresets', presets);
|
||||
this.$el.find('.gen-ps__list option[selected]').text(name);
|
||||
GeneratorPresets.setPreset(this.selected, { title });
|
||||
this.$el.find('.gen-ps__list option[selected]').text(title);
|
||||
}
|
||||
},
|
||||
|
||||
changeEnabled: function(e) {
|
||||
let enabled = e.target.checked;
|
||||
GeneratorPresets.setDisabled(this.selected, !enabled);
|
||||
},
|
||||
|
||||
changeDefault: function(e) {
|
||||
let isDefault = e.target.checked;
|
||||
GeneratorPresets.setDefault(isDefault ? this.selected : null);
|
||||
},
|
||||
|
||||
changeLength: function(e) {
|
||||
let length = +e.target.value;
|
||||
if (length > 0) {
|
||||
GeneratorPresets.setPreset(this.selected, { length });
|
||||
$(e.target).removeClass('input--error');
|
||||
} else {
|
||||
$(e.target).addClass('input--error');
|
||||
}
|
||||
this.presets = GeneratorPresets.all;
|
||||
this.renderExample();
|
||||
},
|
||||
|
||||
changeRange: function(e) {
|
||||
let enabled = e.target.checked;
|
||||
let range = e.target.dataset.range;
|
||||
GeneratorPresets.setPreset(this.selected, { [range]: enabled });
|
||||
this.presets = GeneratorPresets.all;
|
||||
this.renderExample();
|
||||
},
|
||||
|
||||
changeInclude: function(e) {
|
||||
let include = e.target.value;
|
||||
if (include !== this.getPreset(this.selected).include) {
|
||||
GeneratorPresets.setPreset(this.selected, { include: include });
|
||||
}
|
||||
this.presets = GeneratorPresets.all;
|
||||
this.renderExample();
|
||||
}
|
||||
});
|
||||
|
||||
_.extend(GeneratorPresetsView.prototype, Scrollable);
|
||||
|
||||
module.exports = GeneratorPresetsView;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
PasswordGenerator = require('../util/password-generator'),
|
||||
CopyPaste = require('../comp/copy-paste'),
|
||||
Locale = require('../util/locale');
|
||||
const Backbone = require('backbone');
|
||||
const PasswordGenerator = require('../util/password-generator');
|
||||
const CopyPaste = require('../comp/copy-paste');
|
||||
const GeneratorPresets = require('../comp/generator-presets');
|
||||
const Locale = require('../util/locale');
|
||||
|
||||
var GeneratorView = Backbone.View.extend({
|
||||
el: 'body',
|
||||
|
@ -13,7 +14,6 @@ var GeneratorView = Backbone.View.extend({
|
|||
events: {
|
||||
'click': 'click',
|
||||
'mousedown .gen__length-range': 'generate',
|
||||
'mousemove .gen__length-range': 'lengthChange',
|
||||
'change .gen__length-range': 'lengthChange',
|
||||
'change .gen__check input[type=checkbox]': 'checkChange',
|
||||
'click .gen__btn-ok': 'btnOkClick',
|
||||
|
@ -45,39 +45,30 @@ var GeneratorView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
createPresets: function() {
|
||||
this.presets = [
|
||||
{ name: 'Default', length: 16, upper: true, lower: true, digits: true },
|
||||
{ name: 'Pronounceable', length: 10, lower: true, upper: true },
|
||||
{ name: 'Med', length: 16, upper: true, lower: true, digits: true, special: true, brackets: true, ambiguous: true },
|
||||
{ name: 'Long', length: 32, upper: true, lower: true, digits: true },
|
||||
{ name: 'Pin4', length: 4, digits: true },
|
||||
{ name: 'Mac', length: 17, upper: true, digits: true, special: true },
|
||||
{ name: 'Hash128', length: 32, lower: true, digits: true },
|
||||
{ name: 'Hash256', length: 64, lower: true, digits: true }
|
||||
];
|
||||
this.presets = GeneratorPresets.enabled;
|
||||
if (this.model.password && (!this.model.password.isProtected || this.model.password.byteLength)) {
|
||||
var derivedPreset = { name: 'Derived' };
|
||||
var derivedPreset = { name: 'Derived', title: Locale.genPresetDerived };
|
||||
_.extend(derivedPreset, PasswordGenerator.deriveOpts(this.model.password));
|
||||
for (var i = 0; i < this.valuesMap.length; i++) {
|
||||
if (this.valuesMap[i] >= derivedPreset.length) {
|
||||
derivedPreset.length = this.valuesMap[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (derivedPreset.length > this.valuesMap[this.valuesMap.length - 1]) {
|
||||
derivedPreset.length = this.valuesMap[this.valuesMap.length - 1];
|
||||
}
|
||||
this.presets.splice(1, 0, derivedPreset);
|
||||
this.presets.splice(0, 0, derivedPreset);
|
||||
this.preset = 'Derived';
|
||||
} else {
|
||||
this.preset = 'Default';
|
||||
let defaultPreset = this.presets.filter(p => p.default)[0] || this.presets[0];
|
||||
this.preset = defaultPreset.name;
|
||||
}
|
||||
this.presets.forEach(function(pr) {
|
||||
pr.pseudoLength = this.valuesMap.indexOf(pr.length);
|
||||
pr.title = Locale['genPreset' + pr.name];
|
||||
pr.pseudoLength = this.lengthToPseudoValue(pr.length);
|
||||
}, this);
|
||||
},
|
||||
|
||||
lengthToPseudoValue: function(length) {
|
||||
for (let ix = 0; ix < this.valuesMap.length; ix++) {
|
||||
if (this.valuesMap[ix] >= length) {
|
||||
return ix;
|
||||
}
|
||||
}
|
||||
return this.valuesMap.length - 1;
|
||||
},
|
||||
|
||||
click: function(e) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
|
|
|
@ -43,7 +43,7 @@ var GrpView = Backbone.View.extend({
|
|||
}
|
||||
}
|
||||
this.createScroll({
|
||||
root: this.$el.find('.details__body')[0],
|
||||
root: this.$el.find('.grp')[0],
|
||||
scroller: this.$el.find('.scroller')[0],
|
||||
bar: this.$el.find('.scroller__bar')[0]
|
||||
});
|
||||
|
|
|
@ -4,11 +4,32 @@
|
|||
@include align-items(stretch);
|
||||
@include flex-direction(column);
|
||||
@include justify-content(flex-start);
|
||||
@include scrollbar-on-hover;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&__empty-text {
|
||||
padding-bottom: $large-padding;
|
||||
>.scroller {
|
||||
@include flex(1);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
margin-top: $base-padding-v;
|
||||
}
|
||||
|
||||
&__sample {
|
||||
font-weight: normal;
|
||||
@include th { color: muted-color(); }
|
||||
}
|
||||
|
||||
&__example {
|
||||
@include user-select(text);
|
||||
font-family: $monospace-font-family;
|
||||
margin-top: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
&__list, &__input {
|
||||
|
|
|
@ -30,10 +30,12 @@
|
|||
@include user-select(text);
|
||||
font-family: $monospace-font-family;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 3px;
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
&--long-pass {
|
||||
font-size: .75em;
|
||||
}
|
||||
|
|
|
@ -7,15 +7,12 @@
|
|||
@include scrollbar-on-hover;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
>.scroller {
|
||||
@include flex(1);
|
||||
@include display(flex);
|
||||
@include align-items(stretch);
|
||||
@include flex-direction(column);
|
||||
@include justify-content(flex-start);
|
||||
overflow-x: hidden;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
|
@ -30,6 +27,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__icon-wrap {
|
||||
@include display(flex);
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
@include display(flex);
|
||||
@include flex-direction(row);
|
||||
|
|
|
@ -2,27 +2,52 @@
|
|||
<div class="back-button">
|
||||
{{res 'retToApp'}} <i class="fa fa-external-link-square"></i>
|
||||
</div>
|
||||
<h1>{{res 'genPsTitle'}}</h1>
|
||||
{{#if empty}}
|
||||
<div class="empty-block empty-block--flex muted-color">
|
||||
<h1 class="empty-block__title">{{res 'genPsEmpty'}}</h1>
|
||||
<div class="gen-ps__empty-text">{{res 'genPsEmptyDesc'}}</div>
|
||||
<button class="gen-ps__btn-create">{{res 'genPsCreate'}}</button>
|
||||
<div class="scroller">
|
||||
<h1>{{res 'genPsTitle'}}</h1>
|
||||
<select class="gen-ps__list input-base">
|
||||
{{#each presets as |ps|}}
|
||||
<option value="{{ps.name}}" {{#ifeq ps ../selected}}selected{{/ifeq}}>{{#if ps.builtIn}}* {{/if}}{{ps.title}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<div class="gen-ps__field">
|
||||
<input type="checkbox" class="input-base" id="gen-ps__check-enabled" {{#unless selected.disabled}}checked{{/unless}} />
|
||||
<label for="gen-ps__check-enabled">{{res 'genPsEnabled'}}</label>
|
||||
</div>
|
||||
<div class="gen-ps__field">
|
||||
<input type="checkbox" class="input-base" id="gen-ps__check-default" {{#if selected.default}}checked{{/if}} />
|
||||
<label for="gen-ps__check-default">{{res 'genPsDefault'}}</label>
|
||||
</div>
|
||||
<div class="gen-ps__field">
|
||||
<label for="gen-ps__field-title">{{Res 'name'}}:</label>
|
||||
<input type="text" class="input-base" id="gen-ps__field-title" value="{{selected.title}}"
|
||||
size="50" maxlength="64" required {{#if selected.builtIn}}readonly{{/if}} />
|
||||
</div>
|
||||
<div class="gen-ps__field">
|
||||
<label for="gen-ps__field-length">{{res 'genPsDefaultLength'}}:</label>
|
||||
<input type="text" class="input-base" id="gen-ps__field-length" value="{{selected.length}}"
|
||||
size="50" maxlength="3" required pattern="\d+" {{#if selected.builtIn}}readonly{{/if}} />
|
||||
</div>
|
||||
{{#each ranges as |range|}}
|
||||
<div class="gen-ps__field">
|
||||
<input type="checkbox" class="input-base gen-ps__check-range" id="gen-ps__check-{{range.name}}"
|
||||
data-range="{{range.name}}"
|
||||
{{#if range.enabled}}checked{{/if}} {{#if ../selected.builtIn}}disabled{{/if}} />
|
||||
<label for="gen-ps__check-{{range.name}}">{{range.title}}<span class="gen-ps__sample"> {{range.sample}}</span></label>
|
||||
</div>
|
||||
{{else}}
|
||||
<select class="gen-ps__list input-base">
|
||||
{{#each presets as |ps|}}
|
||||
<option value="{{ps.id}}" {{#ifeq ps ../selected}}selected{{/ifeq}}>{{ps.name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<div class="gen-ps__field">
|
||||
<label for="gen-ps__field-name">{{Res 'name'}}:</label>
|
||||
<input type="text" class="input-base" id="gen-ps__field-name" value="{{selected.name}}"
|
||||
size="50" maxlength="64" required />
|
||||
<div class="gen-ps__field">
|
||||
<label for="gen-ps__field-include">{{res 'genPsInclude'}}:</label>
|
||||
<input type="text" class="input-base" id="gen-ps__field-include" value="{{selected.include}}"
|
||||
{{#if selected.builtIn}}readonly{{/if}} />
|
||||
</div>
|
||||
<div class="gen-ps__field">
|
||||
<label>{{res 'genPsExample'}}:</label>
|
||||
<div class="gen-ps__example"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroller__bar-wrapper"><div class="scroller__bar"></div></div>
|
||||
<div class="gen-ps__buttons">
|
||||
<button class="gen-ps__btn-create">{{res 'genPsCreate'}}</button>
|
||||
<button class="gen-ps__btn-delete btn-error">{{res 'genPsDelete'}}</button>
|
||||
{{#unless selected.builtIn}}<button class="gen-ps__btn-delete btn-error">{{res 'genPsDelete'}}</button>{{/unless}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
</div>
|
||||
{{/unless}}
|
||||
<label>{{Res 'icon'}}:</label>
|
||||
{{#if customIcon}}
|
||||
<img src="{{{customIcon}}}" class="grp__icon grp__icon--image" />
|
||||
{{else}}
|
||||
<i class="fa fa-{{icon}} grp__icon"></i>
|
||||
{{/if}}
|
||||
<div class="grp__icon-wrap">
|
||||
{{#if customIcon}}
|
||||
<img src="{{{customIcon}}}" class="grp__icon grp__icon--image" />
|
||||
{{else}}
|
||||
<i class="fa fa-{{icon}} grp__icon"></i>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="grp__icons"></div>
|
||||
{{#if canAutoType}}
|
||||
{{#unless readonly}}
|
||||
|
|
|
@ -5,6 +5,7 @@ Release notes
|
|||
`+` auto-type improvements
|
||||
`+` context menu
|
||||
`+` solarized themes
|
||||
`+` generator presets
|
||||
`+` group reorder
|
||||
`+` select field contents on search hotkey
|
||||
`+` option to preload default config and file
|
||||
|
|
Loading…
Reference in New Issue