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.',
|
tagBadNameBody: 'Tag name can not contain characters `,`, `;`, `:`. Please remove them.',
|
||||||
|
|
||||||
genPsTitle: 'Generator Presets',
|
genPsTitle: 'Generator Presets',
|
||||||
genPsEmpty: 'You have no presets yet',
|
|
||||||
genPsEmptyDesc: 'Presets allow you to generate passwords by your rules faster',
|
|
||||||
genPsCreate: 'New preset',
|
genPsCreate: 'New preset',
|
||||||
genPsDelete: 'Delete preset',
|
genPsDelete: 'Delete preset',
|
||||||
genPsNew: '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',
|
keyChangeTitleRemote: 'Master Key Changed',
|
||||||
keyChangeMessageRemote: 'Master key was changed for this database. Please enter a new key',
|
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)
|
var ranges = Object.keys(this.charRanges)
|
||||||
.filter(r => opts[r])
|
.filter(r => opts[r])
|
||||||
.map(function(r) { return this.charRanges[r]; }, this);
|
.map(function(r) { return this.charRanges[r]; }, this);
|
||||||
|
if (opts.include && opts.include.length) {
|
||||||
|
ranges.push(opts.include);
|
||||||
|
}
|
||||||
if (!ranges.length) {
|
if (!ranges.length) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Backbone = require('backbone');
|
const Backbone = require('backbone');
|
||||||
|
const Scrollable = require('../mixins/scrollable');
|
||||||
const Locale = require('../util/locale');
|
const Locale = require('../util/locale');
|
||||||
|
const GeneratorPresets = require('../comp/generator-presets');
|
||||||
|
const PasswordGenerator = require('../util/password-generator');
|
||||||
|
|
||||||
let GeneratorPresetsView = Backbone.View.extend({
|
let GeneratorPresetsView = Backbone.View.extend({
|
||||||
template: require('templates/generator-presets.hbs'),
|
template: require('templates/generator-presets.hbs'),
|
||||||
|
@ -11,87 +14,165 @@ let GeneratorPresetsView = Backbone.View.extend({
|
||||||
'change .gen-ps__list': 'changePreset',
|
'change .gen-ps__list': 'changePreset',
|
||||||
'click .gen-ps__btn-create': 'createPreset',
|
'click .gen-ps__btn-create': 'createPreset',
|
||||||
'click .gen-ps__btn-delete': 'deletePreset',
|
'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,
|
selected: null,
|
||||||
|
|
||||||
|
reservedTitles: [Locale.genPresetDerived],
|
||||||
|
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
this.appModel = this.model;
|
this.appModel = this.model;
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
let presets = this.appModel.settings.get('generatorPresets') || [];
|
this.presets = GeneratorPresets.all;
|
||||||
if (!this.selected || presets.indexOf(this.selected) < 0) {
|
if (!this.selected || !this.presets.some(p => p.name === this.selected)) {
|
||||||
this.selected = presets[0];
|
this.selected = (this.presets.filter(p => p.default)[0] || this.presets[0]).name;
|
||||||
}
|
}
|
||||||
this.renderTemplate({
|
this.renderTemplate({
|
||||||
empty: !presets.length,
|
presets: this.presets,
|
||||||
presets: presets,
|
selected: this.getPreset(this.selected),
|
||||||
selected: this.selected
|
ranges: this.getSelectedRanges()
|
||||||
}, true);
|
}, 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;
|
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() {
|
returnToApp: function() {
|
||||||
Backbone.trigger('edit-generator-presets');
|
Backbone.trigger('edit-generator-presets');
|
||||||
},
|
},
|
||||||
|
|
||||||
changePreset: function(e) {
|
changePreset: function(e) {
|
||||||
let id = e.target.value;
|
this.selected = e.target.value;
|
||||||
let presets = this.appModel.settings.get('generatorPresets');
|
|
||||||
this.selected = presets.filter(p => p.id === id)[0];
|
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
createPreset: function() {
|
createPreset: function() {
|
||||||
let presets = this.appModel.settings.get('generatorPresets') || [];
|
|
||||||
let name;
|
let name;
|
||||||
let id;
|
let title;
|
||||||
for (let i = 1; ; i++) {
|
for (let i = 1; ; i++) {
|
||||||
let newName = Locale.genPsNew + ' ' + i;
|
let newName = 'Custom' + i;
|
||||||
if (!presets.filter(p => p.name === newName).length) {
|
let newTitle = Locale.genPsNew + ' ' + i;
|
||||||
|
if (!this.presets.filter(p => p.name === newName || p.title === newTitle).length) {
|
||||||
name = newName;
|
name = newName;
|
||||||
|
title = newTitle;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let i = 1; ; i++) {
|
let selected = this.getPreset(this.selected);
|
||||||
let newId = 'custom' + i;
|
let preset = {
|
||||||
if (!presets.filter(p => p.id === newId).length) {
|
name, title,
|
||||||
id = newId;
|
length: selected.length,
|
||||||
break;
|
upper: selected.upper, lower: selected.lower, digits: selected.digits,
|
||||||
}
|
special: selected.special, brackets: selected.brackets, ambiguous: selected.ambiguous,
|
||||||
}
|
include: selected.include
|
||||||
let preset = { id, name };
|
};
|
||||||
presets.push(preset);
|
GeneratorPresets.add(preset);
|
||||||
this.selected = preset;
|
this.selected = name;
|
||||||
this.appModel.settings.set('generatorPresets', presets);
|
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
deletePreset: function() {
|
deletePreset: function() {
|
||||||
let presets = this.appModel.settings.get('generatorPresets');
|
GeneratorPresets.remove(this.selected);
|
||||||
presets = presets.filter(p => p.id !== this.selected.id);
|
|
||||||
this.appModel.settings.set('generatorPresets', presets.length ? presets : null);
|
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
changeName: function(e) {
|
changeTitle: function(e) {
|
||||||
let name = $.trim(e.target.value);
|
let title = $.trim(e.target.value);
|
||||||
if (name && name !== this.selected.name) {
|
if (title && title !== this.getPreset(this.selected).title) {
|
||||||
let presets = this.appModel.settings.get('generatorPresets');
|
let duplicate = this.presets.some(p => p.title.toLowerCase() === title.toLowerCase());
|
||||||
let another = presets.filter(p => p.name.toLowerCase() === name.toLowerCase())[0];
|
if (!duplicate) {
|
||||||
if (another) {
|
duplicate = this.reservedTitles.some(p => p.toLowerCase() === title.toLowerCase());
|
||||||
|
}
|
||||||
|
if (duplicate) {
|
||||||
$(e.target).addClass('input--error');
|
$(e.target).addClass('input--error');
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
$(e.target).removeClass('input--error');
|
$(e.target).removeClass('input--error');
|
||||||
}
|
}
|
||||||
this.selected.name = name;
|
GeneratorPresets.setPreset(this.selected, { title });
|
||||||
this.appModel.settings.set('generatorPresets', presets);
|
this.$el.find('.gen-ps__list option[selected]').text(title);
|
||||||
this.$el.find('.gen-ps__list option[selected]').text(name);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
module.exports = GeneratorPresetsView;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Backbone = require('backbone'),
|
const Backbone = require('backbone');
|
||||||
PasswordGenerator = require('../util/password-generator'),
|
const PasswordGenerator = require('../util/password-generator');
|
||||||
CopyPaste = require('../comp/copy-paste'),
|
const CopyPaste = require('../comp/copy-paste');
|
||||||
Locale = require('../util/locale');
|
const GeneratorPresets = require('../comp/generator-presets');
|
||||||
|
const Locale = require('../util/locale');
|
||||||
|
|
||||||
var GeneratorView = Backbone.View.extend({
|
var GeneratorView = Backbone.View.extend({
|
||||||
el: 'body',
|
el: 'body',
|
||||||
|
@ -13,7 +14,6 @@ var GeneratorView = Backbone.View.extend({
|
||||||
events: {
|
events: {
|
||||||
'click': 'click',
|
'click': 'click',
|
||||||
'mousedown .gen__length-range': 'generate',
|
'mousedown .gen__length-range': 'generate',
|
||||||
'mousemove .gen__length-range': 'lengthChange',
|
|
||||||
'change .gen__length-range': 'lengthChange',
|
'change .gen__length-range': 'lengthChange',
|
||||||
'change .gen__check input[type=checkbox]': 'checkChange',
|
'change .gen__check input[type=checkbox]': 'checkChange',
|
||||||
'click .gen__btn-ok': 'btnOkClick',
|
'click .gen__btn-ok': 'btnOkClick',
|
||||||
|
@ -45,39 +45,30 @@ var GeneratorView = Backbone.View.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
createPresets: function() {
|
createPresets: function() {
|
||||||
this.presets = [
|
this.presets = GeneratorPresets.enabled;
|
||||||
{ 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 }
|
|
||||||
];
|
|
||||||
if (this.model.password && (!this.model.password.isProtected || this.model.password.byteLength)) {
|
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));
|
_.extend(derivedPreset, PasswordGenerator.deriveOpts(this.model.password));
|
||||||
for (var i = 0; i < this.valuesMap.length; i++) {
|
this.presets.splice(0, 0, derivedPreset);
|
||||||
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.preset = 'Derived';
|
this.preset = 'Derived';
|
||||||
} else {
|
} 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) {
|
this.presets.forEach(function(pr) {
|
||||||
pr.pseudoLength = this.valuesMap.indexOf(pr.length);
|
pr.pseudoLength = this.lengthToPseudoValue(pr.length);
|
||||||
pr.title = Locale['genPreset' + pr.name];
|
|
||||||
}, this);
|
}, 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) {
|
click: function(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,7 +43,7 @@ var GrpView = Backbone.View.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.createScroll({
|
this.createScroll({
|
||||||
root: this.$el.find('.details__body')[0],
|
root: this.$el.find('.grp')[0],
|
||||||
scroller: this.$el.find('.scroller')[0],
|
scroller: this.$el.find('.scroller')[0],
|
||||||
bar: this.$el.find('.scroller__bar')[0]
|
bar: this.$el.find('.scroller__bar')[0]
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,11 +4,32 @@
|
||||||
@include align-items(stretch);
|
@include align-items(stretch);
|
||||||
@include flex-direction(column);
|
@include flex-direction(column);
|
||||||
@include justify-content(flex-start);
|
@include justify-content(flex-start);
|
||||||
|
@include scrollbar-on-hover;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&__empty-text {
|
>.scroller {
|
||||||
padding-bottom: $large-padding;
|
@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 {
|
&__list, &__input {
|
||||||
|
|
|
@ -30,10 +30,12 @@
|
||||||
@include user-select(text);
|
@include user-select(text);
|
||||||
font-family: $monospace-font-family;
|
font-family: $monospace-font-family;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
|
margin-bottom: 3px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
overflow: hidden;
|
||||||
&--long-pass {
|
&--long-pass {
|
||||||
font-size: .75em;
|
font-size: .75em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,12 @@
|
||||||
@include scrollbar-on-hover;
|
@include scrollbar-on-hover;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
>.scroller {
|
>.scroller {
|
||||||
@include flex(1);
|
@include flex(1);
|
||||||
@include display(flex);
|
|
||||||
@include align-items(stretch);
|
|
||||||
@include flex-direction(column);
|
|
||||||
@include justify-content(flex-start);
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
padding-top: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
|
@ -30,6 +27,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__icon-wrap {
|
||||||
|
@include display(flex);
|
||||||
|
}
|
||||||
|
|
||||||
&__buttons {
|
&__buttons {
|
||||||
@include display(flex);
|
@include display(flex);
|
||||||
@include flex-direction(row);
|
@include flex-direction(row);
|
||||||
|
|
|
@ -2,27 +2,52 @@
|
||||||
<div class="back-button">
|
<div class="back-button">
|
||||||
{{res 'retToApp'}} <i class="fa fa-external-link-square"></i>
|
{{res 'retToApp'}} <i class="fa fa-external-link-square"></i>
|
||||||
</div>
|
</div>
|
||||||
<h1>{{res 'genPsTitle'}}</h1>
|
<div class="scroller">
|
||||||
{{#if empty}}
|
<h1>{{res 'genPsTitle'}}</h1>
|
||||||
<div class="empty-block empty-block--flex muted-color">
|
<select class="gen-ps__list input-base">
|
||||||
<h1 class="empty-block__title">{{res 'genPsEmpty'}}</h1>
|
{{#each presets as |ps|}}
|
||||||
<div class="gen-ps__empty-text">{{res 'genPsEmptyDesc'}}</div>
|
<option value="{{ps.name}}" {{#ifeq ps ../selected}}selected{{/ifeq}}>{{#if ps.builtIn}}* {{/if}}{{ps.title}}</option>
|
||||||
<button class="gen-ps__btn-create">{{res 'genPsCreate'}}</button>
|
{{/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>
|
</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}}
|
{{/each}}
|
||||||
</select>
|
<div class="gen-ps__field">
|
||||||
<div class="gen-ps__field">
|
<label for="gen-ps__field-include">{{res 'genPsInclude'}}:</label>
|
||||||
<label for="gen-ps__field-name">{{Res 'name'}}:</label>
|
<input type="text" class="input-base" id="gen-ps__field-include" value="{{selected.include}}"
|
||||||
<input type="text" class="input-base" id="gen-ps__field-name" value="{{selected.name}}"
|
{{#if selected.builtIn}}readonly{{/if}} />
|
||||||
size="50" maxlength="64" required />
|
</div>
|
||||||
|
<div class="gen-ps__field">
|
||||||
|
<label>{{res 'genPsExample'}}:</label>
|
||||||
|
<div class="gen-ps__example"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="scroller__bar-wrapper"><div class="scroller__bar"></div></div>
|
||||||
<div class="gen-ps__buttons">
|
<div class="gen-ps__buttons">
|
||||||
<button class="gen-ps__btn-create">{{res 'genPsCreate'}}</button>
|
<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>
|
</div>
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
<label>{{Res 'icon'}}:</label>
|
<label>{{Res 'icon'}}:</label>
|
||||||
{{#if customIcon}}
|
<div class="grp__icon-wrap">
|
||||||
<img src="{{{customIcon}}}" class="grp__icon grp__icon--image" />
|
{{#if customIcon}}
|
||||||
{{else}}
|
<img src="{{{customIcon}}}" class="grp__icon grp__icon--image" />
|
||||||
<i class="fa fa-{{icon}} grp__icon"></i>
|
{{else}}
|
||||||
{{/if}}
|
<i class="fa fa-{{icon}} grp__icon"></i>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
<div class="grp__icons"></div>
|
<div class="grp__icons"></div>
|
||||||
{{#if canAutoType}}
|
{{#if canAutoType}}
|
||||||
{{#unless readonly}}
|
{{#unless readonly}}
|
||||||
|
|
|
@ -5,6 +5,7 @@ Release notes
|
||||||
`+` auto-type improvements
|
`+` auto-type improvements
|
||||||
`+` context menu
|
`+` context menu
|
||||||
`+` solarized themes
|
`+` solarized themes
|
||||||
|
`+` generator presets
|
||||||
`+` group reorder
|
`+` group reorder
|
||||||
`+` select field contents on search hotkey
|
`+` select field contents on search hotkey
|
||||||
`+` option to preload default config and file
|
`+` option to preload default config and file
|
||||||
|
|
Loading…
Reference in New Issue