mirror of https://github.com/keeweb/keeweb
import
commit
3fb3db9365
|
@ -0,0 +1,9 @@
|
|||
workspace.xml
|
||||
.DS_Store
|
||||
node_modules/
|
||||
bower_components/
|
||||
*.log
|
||||
dist/
|
||||
tmp/
|
||||
build/
|
||||
coverage/
|
|
@ -0,0 +1 @@
|
|||
keepass-webapp
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectCodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
</value>
|
||||
</option>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,3 @@
|
|||
<component name="CopyrightManager">
|
||||
<settings default="" />
|
||||
</component>
|
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
|
@ -0,0 +1,7 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4" />
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JSHintConfiguration" version="2.6.3" use-config-file="true">
|
||||
<option asi="false" />
|
||||
<option bitwise="true" />
|
||||
<option boss="false" />
|
||||
<option browser="true" />
|
||||
<option camelcase="false" />
|
||||
<option couch="false" />
|
||||
<option curly="true" />
|
||||
<option debug="false" />
|
||||
<option devel="false" />
|
||||
<option dojo="false" />
|
||||
<option eqeqeq="true" />
|
||||
<option eqnull="false" />
|
||||
<option es3="false" />
|
||||
<option esnext="false" />
|
||||
<option evil="false" />
|
||||
<option expr="false" />
|
||||
<option forin="true" />
|
||||
<option freeze="false" />
|
||||
<option funcscope="false" />
|
||||
<option gcl="false" />
|
||||
<option globalstrict="false" />
|
||||
<option immed="false" />
|
||||
<option iterator="false" />
|
||||
<option jquery="false" />
|
||||
<option lastsemic="false" />
|
||||
<option latedef="false" />
|
||||
<option laxbreak="false" />
|
||||
<option laxcomma="false" />
|
||||
<option loopfunc="false" />
|
||||
<option maxerr="50" />
|
||||
<option mootools="false" />
|
||||
<option moz="false" />
|
||||
<option multistr="false" />
|
||||
<option newcap="false" />
|
||||
<option noarg="true" />
|
||||
<option node="false" />
|
||||
<option noempty="true" />
|
||||
<option nomen="false" />
|
||||
<option nonbsp="false" />
|
||||
<option nonew="true" />
|
||||
<option nonstandard="false" />
|
||||
<option notypeof="false" />
|
||||
<option noyield="false" />
|
||||
<option onevar="false" />
|
||||
<option passfail="false" />
|
||||
<option phantom="false" />
|
||||
<option plusplus="false" />
|
||||
<option proto="false" />
|
||||
<option prototypejs="false" />
|
||||
<option quotmark="false" />
|
||||
<option rhino="false" />
|
||||
<option scripturl="false" />
|
||||
<option shadow="false" />
|
||||
<option smarttabs="false" />
|
||||
<option strict="true" />
|
||||
<option sub="false" />
|
||||
<option supernew="false" />
|
||||
<option trailing="false" />
|
||||
<option undef="true" />
|
||||
<option unused="false" />
|
||||
<option validthis="false" />
|
||||
<option white="false" />
|
||||
<option worker="false" />
|
||||
<option wsh="false" />
|
||||
<option yui="false" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,15 @@
|
|||
<component name="libraryTable">
|
||||
<library name="keepass-webapp node_modules" type="javaScript">
|
||||
<properties>
|
||||
<option name="frameworkName" value="node_modules" />
|
||||
<sourceFilesUrls>
|
||||
<item url="file://$PROJECT_DIR$/node_modules" />
|
||||
</sourceFilesUrls>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="file://$PROJECT_DIR$/node_modules" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
||||
<OptionsSetting value="true" id="Add" />
|
||||
<OptionsSetting value="true" id="Remove" />
|
||||
<OptionsSetting value="true" id="Checkout" />
|
||||
<OptionsSetting value="true" id="Update" />
|
||||
<OptionsSetting value="true" id="Status" />
|
||||
<OptionsSetting value="true" id="Edit" />
|
||||
<ConfirmationsSetting value="0" id="Add" />
|
||||
<ConfirmationsSetting value="0" id="Remove" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="true" assert-keyword="true" jdk-15="true">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/keepass-webapp.iml" filepath="$PROJECT_DIR$/keepass-webapp.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"maxerr" : 50, // {int} Maximum error before stopping
|
||||
|
||||
// Enforcing
|
||||
"bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||
"camelcase" : true, // true: Identifiers must be in camelCase
|
||||
"curly" : true, // true: Require {} for every new block or scope
|
||||
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
||||
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
||||
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
|
||||
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
|
||||
"indent" : 4, // {int} Number of spaces to use for indentation
|
||||
"latedef" : false, // true: Require variables/functions to be defined before being used
|
||||
"newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()`
|
||||
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
|
||||
"noempty" : true, // true: Prohibit use of empty blocks
|
||||
"nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
|
||||
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
|
||||
"plusplus" : false, // true: Prohibit use of `++` & `--`
|
||||
"quotmark" : "single", // Quotation mark consistency:
|
||||
// false : do nothing (default)
|
||||
// true : ensure whatever is used is consistent
|
||||
// "single" : require single quotes
|
||||
// "double" : require double quotes
|
||||
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
|
||||
"unused" : true, // Unused variables:
|
||||
// true : all variables, last function parameter
|
||||
// "vars" : all variables only
|
||||
// "strict" : all variables, all function parameters
|
||||
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
|
||||
"maxparams" : false, // {int} Max number of formal params allowed per function
|
||||
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
|
||||
"maxstatements" : false, // {int} Max number statements per function
|
||||
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
|
||||
"maxlen" : 160, // {int} Max number of characters per line
|
||||
"varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed.
|
||||
|
||||
// Relaxing
|
||||
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
|
||||
"boss" : false, // true: Tolerate assignments where comparisons would be expected
|
||||
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
|
||||
"eqnull" : false, // true: Tolerate use of `== null`
|
||||
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
|
||||
// (ex: `for each`, multiple try/catch, function expression…)
|
||||
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
|
||||
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
|
||||
"funcscope" : false, // true: Tolerate defining variables inside control statements
|
||||
"globalstrict" : true, // true: Allow global "use strict" (also enables 'strict')
|
||||
"iterator" : false, // true: Tolerate using the `__iterator__` property
|
||||
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
|
||||
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
|
||||
"laxcomma" : false, // true: Tolerate comma-first style coding
|
||||
"loopfunc" : false, // true: Tolerate functions being defined in loops
|
||||
"multistr" : false, // true: Tolerate multi-line strings
|
||||
"noyield" : false, // true: Tolerate generator functions with no yield statement in them.
|
||||
"notypeof" : false, // true: Tolerate invalid typeof operator values
|
||||
"proto" : false, // true: Tolerate using the `__proto__` property
|
||||
"scripturl" : false, // true: Tolerate script-targeted URLs
|
||||
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
|
||||
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
|
||||
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
|
||||
"validthis" : false, // true: Tolerate using this in a non-constructor function
|
||||
|
||||
// Environments
|
||||
"browser" : true, // Web Browser (window, document, etc)
|
||||
"browserify" : false, // Browserify (node.js code in the browser)
|
||||
"couch" : false, // CouchDB
|
||||
"devel" : false, // Development/debugging (alert, confirm, etc)
|
||||
"dojo" : false, // Dojo Toolkit
|
||||
"jasmine" : true, // Jasmine
|
||||
"jquery" : false, // jQuery
|
||||
"mocha" : true, // Mocha
|
||||
"mootools" : false, // MooTools
|
||||
"node" : false, // Node.js
|
||||
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
|
||||
"phantom" : false, // PhantomJS
|
||||
"prototypejs" : false, // Prototype and Scriptaculous
|
||||
"qunit" : false, // QUnit
|
||||
"rhino" : false, // Rhino
|
||||
"shelljs" : false, // ShellJS
|
||||
"typed" : false, // Globals for typed array constructions
|
||||
"worker" : false, // Web Workers
|
||||
"wsh" : false, // Windows Scripting Host
|
||||
"yui" : false, // Yahoo User Interface
|
||||
|
||||
// Custom Globals
|
||||
"globals" : {
|
||||
"require": true,
|
||||
"module": true,
|
||||
"$": true,
|
||||
"_": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
'use strict';
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
var StringReplacePlugin = require("string-replace-webpack-plugin");
|
||||
|
||||
module.exports = function(grunt) {
|
||||
require('time-grunt')(grunt);
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
var webpack = require('webpack');
|
||||
var pkg = require('./package');
|
||||
|
||||
function replaceFont(css) {
|
||||
css.walkAtRules('font-face', function (rule) {
|
||||
var fontFamily = rule.nodes.filter(function(n) { return n.prop === 'font-family'; })[0];
|
||||
if (!fontFamily) {
|
||||
throw 'Bad font rule: ' + rule.toString();
|
||||
}
|
||||
var value = fontFamily.value.replace(/["']/g, '');
|
||||
var fontFiles = {
|
||||
FontAwesome: 'fontawesome-webfont.woff'
|
||||
};
|
||||
var fontFile = fontFiles[value];
|
||||
if (!fontFile) {
|
||||
throw 'Unsupported font ' + value + ': ' + rule.toString();
|
||||
}
|
||||
var data = fs.readFileSync('tmp/fonts/' + fontFile, 'base64');
|
||||
var src = 'url(data:application/font-woff;charset=utf-8;base64,{data}) format(\'woff\')'
|
||||
.replace('{data}', data);
|
||||
//var src = 'url(\'../fonts/fontawesome-webfont.woff\') format(\'woff\')';
|
||||
rule.nodes = rule.nodes.filter(function(n) { return n.prop !== 'src'; });
|
||||
rule.append({ prop: 'src', value: src });
|
||||
});
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
'bower-install-simple': {
|
||||
install: {
|
||||
}
|
||||
},
|
||||
clean: {
|
||||
dist: ['dist', 'tmp']
|
||||
},
|
||||
copy: {
|
||||
html: {
|
||||
src: 'app/index.html',
|
||||
dest: 'tmp/index.html',
|
||||
nonull: true
|
||||
},
|
||||
favicon: {
|
||||
src: 'app/favicon.png',
|
||||
dest: 'tmp/favicon.png',
|
||||
nonull: true
|
||||
},
|
||||
fonts: {
|
||||
src: 'bower_components/font-awesome/fonts/fontawesome-webfont.*',
|
||||
dest: 'tmp/fonts/',
|
||||
nonull: true,
|
||||
expand: true,
|
||||
flatten: true
|
||||
}
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: true
|
||||
},
|
||||
all: ['app/scripts/**/*.js']
|
||||
},
|
||||
sass: {
|
||||
options: {
|
||||
sourceMap: false,
|
||||
includePaths: ['./bower_components']
|
||||
},
|
||||
dist: {
|
||||
files: {
|
||||
'tmp/css/main.css': 'app/styles/main.scss'
|
||||
}
|
||||
}
|
||||
},
|
||||
postcss: {
|
||||
options: {
|
||||
processors: [
|
||||
replaceFont,
|
||||
require('cssnano')({discardComments: {removeAll: true}})
|
||||
]
|
||||
},
|
||||
dist: {
|
||||
src: 'tmp/css/main.css',
|
||||
dest: 'tmp/css/main.css'
|
||||
}
|
||||
},
|
||||
inline: {
|
||||
app: {
|
||||
src: 'tmp/index.html',
|
||||
dest: 'tmp/app.html'
|
||||
}
|
||||
},
|
||||
htmlmin: {
|
||||
options: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true
|
||||
},
|
||||
app: {
|
||||
files: {
|
||||
'dist/app.html': 'tmp/app.html'
|
||||
}
|
||||
}
|
||||
},
|
||||
webpack: {
|
||||
js: {
|
||||
entry: {
|
||||
app: 'app',
|
||||
vendor: ['zepto', 'jquery', 'underscore', 'backbone', 'kdbxweb', 'baron', 'dropbox', 'pikaday', 'filesaver']
|
||||
},
|
||||
output: {
|
||||
path: 'tmp/js',
|
||||
filename: 'app.js'
|
||||
},
|
||||
stats: {
|
||||
colors: false,
|
||||
modules: true,
|
||||
reasons: true
|
||||
},
|
||||
progress: false,
|
||||
failOnError: true,
|
||||
resolve: {
|
||||
root: [path.join(__dirname, 'app/scripts'), path.join(__dirname, 'bower_components')],
|
||||
alias: {
|
||||
backbone: 'backbone/backbone-min.js',
|
||||
underscore: 'underscore/underscore-min.js',
|
||||
_: 'underscore/underscore-min.js',
|
||||
zepto: 'zepto/zepto.min.js',
|
||||
jquery: 'zepto/zepto.min.js',
|
||||
kdbxweb: 'kdbxweb/dist/kdbxweb.js',
|
||||
dropbox: 'dropbox/lib/dropbox.min.js',
|
||||
baron: 'baron/baron.min.js',
|
||||
pikaday: 'pikaday/pikaday.js',
|
||||
filesaver: 'FileSaver.js/FileSaver.min.js',
|
||||
templates: path.join(__dirname, 'app/templates')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.html$/, loader: StringReplacePlugin.replace('ejs', { replacements: [{
|
||||
pattern: /\r?\n\s*/g,
|
||||
replacement: function() { return '\n'; }
|
||||
}]})},
|
||||
{ test: /zepto(\.min)?\.js$/, loader: 'exports?Zepto; delete window.$; delete window.Zepto;' },
|
||||
{ test: /baron(\.min)?\.js$/, loader: 'exports?baron; delete window.baron;' },
|
||||
{ test: /pikadat\.js$/, loader: 'uglify' }
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
|
||||
new webpack.BannerPlugin('keepass-webapp v' + pkg.version + ', (c) 2015 ' + pkg.author +
|
||||
', opensource.org/licenses/' + pkg.license),
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.ProvidePlugin({ _: 'underscore', $: 'jquery' }),
|
||||
new webpack.IgnorePlugin(/^(moment)$/),
|
||||
new StringReplacePlugin()
|
||||
],
|
||||
node: {
|
||||
console: false,
|
||||
process: false,
|
||||
Buffer: false,
|
||||
__filename: false,
|
||||
__dirname: false
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
options: {
|
||||
preserveComments: false
|
||||
},
|
||||
app: {
|
||||
files: { 'tmp/js/app.js': ['tmp/js/app.js'] }
|
||||
},
|
||||
vendor: {
|
||||
options: {
|
||||
mangle: false,
|
||||
compress: false
|
||||
},
|
||||
files: { 'tmp/js/vendor.js': ['tmp/js/vendor.js'] }
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
interrupt: true,
|
||||
debounceDelay: 500
|
||||
},
|
||||
scripts: {
|
||||
files: ['app/scripts/**/*.js', 'app/templates/**/*.html'],
|
||||
tasks: ['webpack']
|
||||
},
|
||||
styles: {
|
||||
files: 'app/styles/**/*.scss',
|
||||
tasks: ['sass']
|
||||
},
|
||||
indexhtml: {
|
||||
files: 'app/index.html',
|
||||
tasks: ['copy:html']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'bower-install-simple',
|
||||
'clean',
|
||||
'jshint',
|
||||
'copy:html',
|
||||
'copy:favicon',
|
||||
'copy:fonts',
|
||||
'webpack',
|
||||
'uglify',
|
||||
'sass',
|
||||
'postcss',
|
||||
'inline',
|
||||
'htmlmin'
|
||||
]);
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
# KeePass web app (unofficial)
|
||||
|
||||
This webapp can read KeePass databases. It doesn't require any server or additional resources.
|
||||
|
||||
# Status
|
||||
|
||||
Deep under construction, far from usable product; see TODO.
|
|
@ -0,0 +1,21 @@
|
|||
# MVP
|
||||
|
||||
[ ] add/edit groups
|
||||
[ ] dropbox
|
||||
[ ] save+xml
|
||||
[ ] generate
|
||||
[ ] tags autocomplete
|
||||
[ ] file settings
|
||||
|
||||
# FUTURE
|
||||
|
||||
[ ] custom icons
|
||||
[ ] advanced search
|
||||
[ ] merge
|
||||
[ ] move groups/entries
|
||||
[ ] switch view
|
||||
[ ] mobile
|
||||
[ ] help/tips
|
||||
[ ] secure fields
|
||||
[ ] app settings
|
||||
[ ] trash: groups/empty/untrash
|
Binary file not shown.
After Width: | Height: | Size: 490 B |
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title>KeePass Web App</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link rel="shortcut icon" href="favicon.png?__inline=true" />
|
||||
<link rel="stylesheet" href="css/main.css?__inline=true" />
|
||||
<script src="js/vendor.js?__inline=true"></script>
|
||||
<script src="js/app.js?__inline=true"></script>
|
||||
</head>
|
||||
<body class="th-d">
|
||||
<noscript>
|
||||
<h1>KeePass Web App</h1>
|
||||
<p>This app is written entirely in JavaScript. Please, enable JavaScript to run it.</p>
|
||||
</noscript>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
var AppModel = require('./models/app-model'),
|
||||
AppView = require('./views/app-view'),
|
||||
KeyHandler = require('./util/key-handler'),
|
||||
Alerts = require('./util/alerts');
|
||||
|
||||
$(function() {
|
||||
require('./util/view');
|
||||
KeyHandler.init();
|
||||
if (['https:', 'file:', 'app:'].indexOf(location.protocol) < 0) {
|
||||
Alerts.error({ header: 'Not Secure!', icon: 'user-secret', esc: false, enter: false, click: false,
|
||||
body: 'You have loaded this app with insecure connection. ' +
|
||||
'Someone may be watching you and stealing your passwords. ' +
|
||||
'We strongly advice you to stop, unless you clearly understand what you\'re doing.' +
|
||||
'<br/><br/>' +
|
||||
'Yes, your database is encrypted but no one can guarantee that the app has not been modified on the way to you.',
|
||||
buttons: [
|
||||
{ result: '', title: 'I understand the risks, continue', error: true }
|
||||
],
|
||||
complete: showApp
|
||||
});
|
||||
} else {
|
||||
showApp();
|
||||
}
|
||||
|
||||
function showApp() {
|
||||
new AppView({ model: new AppModel() }).render().showOpenFile();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
EntryModel = require('../models/entry-model'),
|
||||
Comparators = require('../util/comparators');
|
||||
|
||||
var EntryCollection = Backbone.Collection.extend({
|
||||
model: EntryModel,
|
||||
|
||||
comparator: function() {},
|
||||
|
||||
comparators: {
|
||||
'title': Comparators.stringComparator('title', true),
|
||||
'-title': Comparators.stringComparator('title', false),
|
||||
'website': Comparators.stringComparator('url', true),
|
||||
'-website': Comparators.stringComparator('url', false),
|
||||
'user': Comparators.stringComparator('user', true),
|
||||
'-user': Comparators.stringComparator('user', false),
|
||||
'created': Comparators.dateComparator('created', true),
|
||||
'-created': Comparators.dateComparator('created', false),
|
||||
'updated': Comparators.dateComparator('updated', true),
|
||||
'-updated': Comparators.dateComparator('updated', false),
|
||||
'-attachments': function(x, y) { return this.attachmentSortVal(x).localeCompare(this.attachmentSortVal(y)); }
|
||||
},
|
||||
|
||||
defaultComparator: 'title',
|
||||
|
||||
activeEntry: null,
|
||||
|
||||
initialize: function() {
|
||||
this.comparator = this.comparators[this.defaultComparator];
|
||||
},
|
||||
|
||||
setActive: function(entry) {
|
||||
if (!(entry instanceof EntryModel)) {
|
||||
entry = this.get(entry);
|
||||
}
|
||||
this.forEach(function(entry) { entry.active = false; });
|
||||
if (entry) {
|
||||
entry.active = true;
|
||||
}
|
||||
this.activeEntry = entry;
|
||||
},
|
||||
|
||||
getActive: function() {
|
||||
return this.activeEntry;
|
||||
},
|
||||
|
||||
sortEntries: function(comparator) {
|
||||
this.comparator = this.comparators[comparator] || this.comparators[this.defaultComparator];
|
||||
this.sort();
|
||||
},
|
||||
|
||||
attachmentSortVal: function(entry) {
|
||||
var att = entry.attachments;
|
||||
var str = att.length ? String.fromCharCode(64 + att.length) : 'Z';
|
||||
if (att[0]) {
|
||||
str += att[0].title;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = EntryCollection;
|
|
@ -0,0 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
FileModel = require('../models/file-model');
|
||||
|
||||
var FileCollection = Backbone.Collection.extend({
|
||||
model: FileModel,
|
||||
|
||||
hasOpenFiles: function() {
|
||||
return this.some(function(file) { return file.get('open'); });
|
||||
},
|
||||
|
||||
hasUnsavedFiles: function() {
|
||||
return this.some(function(file) { return file.get('modified'); });
|
||||
},
|
||||
|
||||
getByName: function(name) {
|
||||
return this.find(function(file) { return file.get('name') === name; });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FileCollection;
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
GroupModel = require('../models/group-model');
|
||||
|
||||
var GroupCollection = Backbone.Collection.extend({
|
||||
model: GroupModel,
|
||||
|
||||
removeByAttr: function(attr, val) {
|
||||
var items = this.get('items');
|
||||
items.forEach(function(item) {
|
||||
if (item[attr] === val) {
|
||||
items.remove(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = GroupCollection;
|
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
MenuItemModel = require('../../models/menu/menu-item-model');
|
||||
|
||||
var MenuItemCollection = Backbone.Collection.extend({
|
||||
model: MenuItemModel
|
||||
});
|
||||
|
||||
module.exports = MenuItemCollection;
|
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
MenuOptionModel = require('../../models/menu/menu-option-model');
|
||||
|
||||
var MenuOptionCollection = Backbone.Collection.extend({
|
||||
model: MenuOptionModel
|
||||
});
|
||||
|
||||
module.exports = MenuOptionCollection;
|
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
MenuSectionModel = require('../../models/menu/menu-section-model');
|
||||
|
||||
var MenuSectionCollection = Backbone.Collection.extend({
|
||||
model: MenuSectionModel
|
||||
});
|
||||
|
||||
module.exports = MenuSectionCollection;
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
var Colors = {
|
||||
AllColors: ['yellow', 'green', 'red', 'orange', 'blue', 'violet'],
|
||||
|
||||
ColorsValues: {
|
||||
yellow: 'ff0',
|
||||
green: '0f0',
|
||||
red: 'f00',
|
||||
orange: 'f80',
|
||||
blue: '00f',
|
||||
violet: 'f0f'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Colors;
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
var IconMap = [
|
||||
'key', 'globe', 'exclamation-triangle', 'server', 'thumb-tack',
|
||||
'comments-o', 'puzzle-piece', 'pencil-square-o', 'plug', 'newspaper-o',
|
||||
'paperclip', 'camera', 'wifi', 'link', 'battery-three-quarters',
|
||||
'barcode', 'certificate', 'bullseye', 'desktop', 'envelope-o',
|
||||
'cog', 'clipboard', 'paper-plane-o', 'television', 'bolt',
|
||||
'inbox', 'floppy-o', 'hdd-o', 'dot-circle-o', 'expeditedssl',
|
||||
'terminal', 'print', 'map-signs', 'flag-checkered', 'wrench',
|
||||
'laptop', 'archive', 'credit-card', 'windows', 'clock-o',
|
||||
'search', 'flask', 'gamepad', 'trash-o', 'sticky-note-o',
|
||||
'ban', 'question-circle', 'cube', 'folder-o', 'folder-open-o',
|
||||
'database', 'unlock-alt', 'lock', 'check', 'pencil',
|
||||
'picture-o', 'book', 'list-alt', 'user-secret', 'cutlery',
|
||||
'home', 'star-o', 'linux', 'map-pin', 'apple',
|
||||
'wikipedia-w', 'usd', 'calendar', 'mobile'
|
||||
];
|
||||
|
||||
module.exports = IconMap;
|
|
@ -0,0 +1,121 @@
|
|||
'use strict';
|
||||
|
||||
var Keys = {
|
||||
DOM_VK_CANCEL: 3,
|
||||
DOM_VK_HELP: 6,
|
||||
DOM_VK_BACK_SPACE: 8,
|
||||
DOM_VK_TAB: 9,
|
||||
DOM_VK_CLEAR: 12,
|
||||
DOM_VK_RETURN: 13,
|
||||
DOM_VK_ENTER: 14,
|
||||
DOM_VK_SHIFT: 16,
|
||||
DOM_VK_CONTROL: 17,
|
||||
DOM_VK_ALT: 18,
|
||||
DOM_VK_PAUSE: 19,
|
||||
DOM_VK_CAPS_LOCK: 20,
|
||||
DOM_VK_ESCAPE: 27,
|
||||
DOM_VK_SPACE: 32,
|
||||
DOM_VK_PAGE_UP: 33,
|
||||
DOM_VK_PAGE_DOWN: 34,
|
||||
DOM_VK_END: 35,
|
||||
DOM_VK_HOME: 36,
|
||||
DOM_VK_LEFT: 37,
|
||||
DOM_VK_UP: 38,
|
||||
DOM_VK_RIGHT: 39,
|
||||
DOM_VK_DOWN: 40,
|
||||
DOM_VK_PRINTSCREEN: 44,
|
||||
DOM_VK_INSERT: 45,
|
||||
DOM_VK_DELETE: 46,
|
||||
DOM_VK_0: 48,
|
||||
DOM_VK_1: 49,
|
||||
DOM_VK_2: 50,
|
||||
DOM_VK_3: 51,
|
||||
DOM_VK_4: 52,
|
||||
DOM_VK_5: 53,
|
||||
DOM_VK_6: 54,
|
||||
DOM_VK_7: 55,
|
||||
DOM_VK_8: 56,
|
||||
DOM_VK_9: 57,
|
||||
DOM_VK_SEMICOLON: 59,
|
||||
DOM_VK_EQUALS: 61,
|
||||
DOM_VK_A: 65,
|
||||
DOM_VK_B: 66,
|
||||
DOM_VK_C: 67,
|
||||
DOM_VK_D: 68,
|
||||
DOM_VK_E: 69,
|
||||
DOM_VK_F: 70,
|
||||
DOM_VK_G: 71,
|
||||
DOM_VK_H: 72,
|
||||
DOM_VK_I: 73,
|
||||
DOM_VK_J: 74,
|
||||
DOM_VK_K: 75,
|
||||
DOM_VK_L: 76,
|
||||
DOM_VK_M: 77,
|
||||
DOM_VK_N: 78,
|
||||
DOM_VK_O: 79,
|
||||
DOM_VK_P: 80,
|
||||
DOM_VK_Q: 81,
|
||||
DOM_VK_R: 82,
|
||||
DOM_VK_S: 83,
|
||||
DOM_VK_T: 84,
|
||||
DOM_VK_U: 85,
|
||||
DOM_VK_V: 86,
|
||||
DOM_VK_W: 87,
|
||||
DOM_VK_X: 88,
|
||||
DOM_VK_Y: 89,
|
||||
DOM_VK_Z: 90,
|
||||
DOM_VK_CONTEXT_MENU: 93,
|
||||
DOM_VK_NUMPAD0: 96,
|
||||
DOM_VK_NUMPAD1: 97,
|
||||
DOM_VK_NUMPAD2: 98,
|
||||
DOM_VK_NUMPAD3: 99,
|
||||
DOM_VK_NUMPAD4: 100,
|
||||
DOM_VK_NUMPAD5: 101,
|
||||
DOM_VK_NUMPAD6: 102,
|
||||
DOM_VK_NUMPAD7: 103,
|
||||
DOM_VK_NUMPAD8: 104,
|
||||
DOM_VK_NUMPAD9: 105,
|
||||
DOM_VK_MULTIPLY: 106,
|
||||
DOM_VK_ADD: 107,
|
||||
DOM_VK_SEPARATOR: 108,
|
||||
DOM_VK_SUBTRACT: 109,
|
||||
DOM_VK_DECIMAL: 110,
|
||||
DOM_VK_DIVIDE: 111,
|
||||
DOM_VK_F1: 112,
|
||||
DOM_VK_F2: 113,
|
||||
DOM_VK_F3: 114,
|
||||
DOM_VK_F4: 115,
|
||||
DOM_VK_F5: 116,
|
||||
DOM_VK_F6: 117,
|
||||
DOM_VK_F7: 118,
|
||||
DOM_VK_F8: 119,
|
||||
DOM_VK_F9: 120,
|
||||
DOM_VK_F10: 121,
|
||||
DOM_VK_F11: 122,
|
||||
DOM_VK_F12: 123,
|
||||
DOM_VK_F13: 124,
|
||||
DOM_VK_F14: 125,
|
||||
DOM_VK_F15: 126,
|
||||
DOM_VK_F16: 127,
|
||||
DOM_VK_F17: 128,
|
||||
DOM_VK_F18: 129,
|
||||
DOM_VK_F19: 130,
|
||||
DOM_VK_F20: 131,
|
||||
DOM_VK_F21: 132,
|
||||
DOM_VK_F22: 133,
|
||||
DOM_VK_F23: 134,
|
||||
DOM_VK_F24: 135,
|
||||
DOM_VK_NUM_LOCK: 144,
|
||||
DOM_VK_SCROLL_LOCK: 145,
|
||||
DOM_VK_COMMA: 188,
|
||||
DOM_VK_PERIOD: 190,
|
||||
DOM_VK_SLASH: 191,
|
||||
DOM_VK_BACK_QUOTE: 192,
|
||||
DOM_VK_OPEN_BRACKET: 219,
|
||||
DOM_VK_BACK_SLASH: 220,
|
||||
DOM_VK_CLOSE_BRACKET: 221,
|
||||
DOM_VK_QUOTE: 222,
|
||||
DOM_VK_META: 224
|
||||
};
|
||||
|
||||
module.exports = Keys;
|
|
@ -0,0 +1,161 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
AppSettingsModel = require('./app-settings-model'),
|
||||
MenuModel = require('./menu/menu-model'),
|
||||
EntryModel = require('./entry-model'),
|
||||
FileCollection = require('../collections/file-collection'),
|
||||
EntryCollection = require('../collections/entry-collection');
|
||||
|
||||
var AppModel = Backbone.Model.extend({
|
||||
defaults: {},
|
||||
|
||||
initialize: function() {
|
||||
this.tags = [];
|
||||
this.files = new FileCollection();
|
||||
this.menu = new MenuModel();
|
||||
this.filter = {};
|
||||
this.sort = 'title';
|
||||
this.settings = AppSettingsModel.instance;
|
||||
|
||||
this.listenTo(Backbone, 'refresh', this.refresh);
|
||||
this.listenTo(Backbone, 'set-filter', this.setFilter);
|
||||
this.listenTo(Backbone, 'add-filter', this.addFilter);
|
||||
this.listenTo(Backbone, 'set-sort', this.setSort);
|
||||
},
|
||||
|
||||
addFile: function(file) {
|
||||
this.files.add(file);
|
||||
file.get('groups').forEach(function(group) { this.menu.groupsSection.addItem(group); }, this);
|
||||
this._addTags(file.db);
|
||||
this._tagsChanged();
|
||||
this.menu.filesSection.addItem({
|
||||
icon: 'lock',
|
||||
title: file.get('name'),
|
||||
page: 'file',
|
||||
file: file
|
||||
});
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
_addTags: function(group) {
|
||||
var tagsHash = {};
|
||||
this.tags.forEach(function(tag) {
|
||||
tagsHash[tag.toLowerCase()] = true;
|
||||
});
|
||||
_.forEach(group.entries, function(entry) {
|
||||
_.forEach(entry.tags, function(tag) {
|
||||
if (!tagsHash[tag.toLowerCase()]) {
|
||||
tagsHash[tag.toLowerCase()] = true;
|
||||
this.tags.push(tag);
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
_.forEach(group.groups, function(subGroup) {
|
||||
this._addTags(subGroup);
|
||||
}, this);
|
||||
this.tags.sort();
|
||||
},
|
||||
|
||||
_tagsChanged: function() {
|
||||
if (this.tags.length) {
|
||||
this.menu.tagsSection.set('scrollable', true);
|
||||
this.menu.tagsSection.setItems(this.tags.map(function (tag) {
|
||||
return {title: tag, icon: 'tag', filterKey: 'tag', filterValue: tag};
|
||||
}));
|
||||
} else {
|
||||
this.menu.tagsSection.set('scrollable', false);
|
||||
this.menu.tagsSection.removeAllItems();
|
||||
}
|
||||
},
|
||||
|
||||
updateTags: function() {
|
||||
var oldTags = this.tags.slice();
|
||||
this.tags.splice(0, this.tags.length);
|
||||
this.files.forEach(function(file) {
|
||||
this._addTags(file.db);
|
||||
}, this);
|
||||
if (!_.isEqual(oldTags, this.tags)) {
|
||||
this._tagsChanged();
|
||||
}
|
||||
},
|
||||
|
||||
closeAllFiles: function() {
|
||||
this.files.reset();
|
||||
this.menu.groupsSection.removeAllItems();
|
||||
this.menu.tagsSection.set('scrollable', false);
|
||||
this.menu.tagsSection.removeAllItems();
|
||||
this.menu.filesSection.removeAllItems();
|
||||
this.tags.splice(0, this.tags.length);
|
||||
this.setFilter({});
|
||||
},
|
||||
|
||||
setFilter: function(filter) {
|
||||
this.filter = filter;
|
||||
var entries = this.getEntries();
|
||||
Backbone.trigger('filter', { filter: this.filter, sort: this.sort, entries: entries });
|
||||
Backbone.trigger('select-entry', entries.length ? entries.first() : null);
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.setFilter(this.filter);
|
||||
},
|
||||
|
||||
addFilter: function(filter) {
|
||||
this.setFilter(_.extend(this.filter, filter));
|
||||
},
|
||||
|
||||
setSort: function(sort) {
|
||||
this.sort = sort;
|
||||
this.setFilter(this.filter);
|
||||
},
|
||||
|
||||
getEntries: function() {
|
||||
var entries = new EntryCollection();
|
||||
var filter = this.prepareFilter();
|
||||
this.files.forEach(function(file) {
|
||||
file.forEachEntry(filter, function(entry) {
|
||||
entries.push(entry);
|
||||
});
|
||||
});
|
||||
entries.sortEntries(this.sort);
|
||||
if (entries.length) {
|
||||
entries.setActive(entries.first());
|
||||
}
|
||||
return entries;
|
||||
},
|
||||
|
||||
prepareFilter: function() {
|
||||
var filter = _.clone(this.filter);
|
||||
if (filter.text) {
|
||||
filter.textLower = filter.text.toLowerCase();
|
||||
}
|
||||
if (filter.tag) {
|
||||
filter.tagLower = filter.tag.toLowerCase();
|
||||
}
|
||||
return filter;
|
||||
},
|
||||
|
||||
createNewEntry: function() {
|
||||
var selGroupId = this.filter.group;
|
||||
var file, group;
|
||||
if (selGroupId) {
|
||||
this.files.forEach(function(f) {
|
||||
group = f.getGroup(selGroupId);
|
||||
if (group) {
|
||||
file = f;
|
||||
return false;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
if (!group) {
|
||||
file = this.files.first();
|
||||
group = file.get('groups').first();
|
||||
}
|
||||
var entry = EntryModel.newEntry(group, file);
|
||||
group.addEntry(entry);
|
||||
return entry;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = AppModel;
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone');
|
||||
|
||||
var AppSettingsModel = Backbone.Model.extend({
|
||||
defaults: {
|
||||
theme: 'd'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
},
|
||||
|
||||
load: function() {
|
||||
if (typeof localStorage !== 'undefined' && localStorage.appSettings) {
|
||||
try {
|
||||
var data = JSON.parse(localStorage.appSettings);
|
||||
this.set(data);
|
||||
} catch (e) { /* failed to load settings */ }
|
||||
}
|
||||
},
|
||||
|
||||
save: function() {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.appSettings = JSON.stringify(this.attributes);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AppSettingsModel.instance = new AppSettingsModel();
|
||||
AppSettingsModel.instance.load();
|
||||
|
||||
module.exports = AppSettingsModel;
|
|
@ -0,0 +1,90 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone');
|
||||
|
||||
var AttachmentModel = Backbone.Model.extend({
|
||||
defaults: {},
|
||||
|
||||
initialize: function() {
|
||||
},
|
||||
|
||||
setAttachment: function(att) {
|
||||
this.title = att.title;
|
||||
this.data = att.data;
|
||||
this.ext = this._getExtension(this.title);
|
||||
this.icon = this._getIcon(this.ext);
|
||||
this.mimeType = this._getMimeType(this.ext);
|
||||
},
|
||||
|
||||
_getExtension: function(fileName) {
|
||||
var ext = fileName ? fileName.split('.').pop() : undefined;
|
||||
return ext ? ext.toLowerCase() : undefined;
|
||||
},
|
||||
|
||||
_getIcon: function(ext) {
|
||||
switch (ext) {
|
||||
case 'txt': case 'log': case 'rtf':
|
||||
return 'file-text-o';
|
||||
case 'html': case 'htm': case 'js': case 'css': case 'xml': case 'config': case 'json': case 'yaml':
|
||||
case 'cpp': case 'c': case 'h': case 'cc': case 'hpp': case 'mm': case 'cs': case 'php': case 'sh':
|
||||
case 'py': case 'java': case 'rb': case 'cfg': case 'properties': case 'yml': case 'asm': case 'bat':
|
||||
return 'file-code-o';
|
||||
case 'pdf':
|
||||
return 'file-pdf-o';
|
||||
case 'zip': case 'rar': case 'bz': case 'bz2': case '7z': case 'gzip': case 'gz': case 'tar':
|
||||
case 'cab': case 'ace': case 'dmg': case 'jar':
|
||||
return 'file-archive-o';
|
||||
case 'doc': case 'docx':
|
||||
return 'file-word-o';
|
||||
case 'xls': case 'xlsx':
|
||||
return 'file-excel-o';
|
||||
case 'ppt': case 'pptx':
|
||||
return 'file-powerpoint-o';
|
||||
case 'jpeg': case 'jpg': case 'png': case 'gif': case 'bmp': case 'tiff': case 'svg': case 'ico': case 'psd':
|
||||
return 'file-image-o';
|
||||
case 'avi': case 'mp4': case '3gp': case 'm4v': case 'mov': case 'mpeg': case 'mpg': case 'mpe':
|
||||
return 'file-video-o';
|
||||
case 'mp3': case 'wav': case 'flac':
|
||||
return 'file-audio-o';
|
||||
}
|
||||
return 'file-o';
|
||||
},
|
||||
|
||||
_getMimeType: function(ext) {
|
||||
switch (ext) {
|
||||
case 'txt': case 'log':
|
||||
case 'html': case 'htm': case 'js': case 'css': case 'xml': case 'config': case 'json': case 'yaml':
|
||||
case 'cpp': case 'c': case 'h': case 'cc': case 'hpp': case 'mm': case 'cs': case 'php': case 'sh':
|
||||
case 'py': case 'java': case 'rb': case 'cfg': case 'properties': case 'yml': case 'asm':
|
||||
return 'text/plain';
|
||||
case 'pdf':
|
||||
return 'application/pdf';
|
||||
case 'jpeg': case 'jpg': case 'png': case 'gif': case 'bmp': case 'tiff': case 'svg':
|
||||
return 'image/' + ext;
|
||||
}
|
||||
},
|
||||
|
||||
getBinary: function() {
|
||||
var data = this.data;
|
||||
if (data && data.ref) {
|
||||
data = data.value;
|
||||
}
|
||||
if (data && data.getBinary) {
|
||||
data = data.getBinary();
|
||||
}
|
||||
if (data instanceof ArrayBuffer && data.byteLength) {
|
||||
data = new Uint8Array(data);
|
||||
}
|
||||
if (data instanceof Uint8Array) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AttachmentModel.fromAttachment = function(att) {
|
||||
var model = new AttachmentModel();
|
||||
model.setAttachment(att);
|
||||
return model;
|
||||
};
|
||||
|
||||
module.exports = AttachmentModel;
|
|
@ -0,0 +1,244 @@
|
|||
'use strict';
|
||||
|
||||
var Backbone = require('backbone'),
|
||||
AttachmentModel = require('./attachment-model'),
|
||||
IconMap = require('../const/icon-map'),
|
||||
Color = require('../util/color'),
|
||||
kdbxweb = require('kdbxweb');
|
||||
|
||||
var EntryModel = Backbone.Model.extend({
|
||||
defaults: {},
|
||||
|
||||
buildInFields: ['Title', 'Password', 'Notes', 'URL', 'UserName'],
|
||||
|
||||
initialize: function() {
|
||||
},
|
||||
|
||||
setEntry: function(entry, group, file) {
|
||||
this.set({ id: entry.uuid.id }, {silent: true});
|
||||
this.entry = entry;
|
||||
this.group = group;
|
||||
this.file = file;
|
||||
this._fillByEntry();
|
||||
this._fillInTrash();
|
||||
},
|
||||
|
||||
_fillByEntry: function() {
|
||||
var entry = this.entry;
|
||||
this.fileName = this.file.db.meta.name;
|
||||
this.title = entry.fields.Title || '';
|
||||
this.password = entry.fields.Password;
|
||||
this.notes = entry.fields.Notes || '';
|
||||
this.url = entry.fields.URL || '';
|
||||
this.user = entry.fields.UserName || '';
|
||||
this.iconId = entry.icon;
|
||||
this.icon = this._iconFromId(entry.icon);
|
||||
this.tags = entry.tags;
|
||||
this.color = this._colorToModel(entry.bgColor) || this._colorToModel(entry.fgColor);
|
||||
this.fields = this._fieldsToModel(entry.fields);
|
||||
this.attachments = this._attachmentsToModel(entry.binaries);
|
||||
this.created = entry.times.creationTime;
|
||||
this.updated = entry.times.lastModTime;
|
||||
this.expires = entry.times.expires ? entry.times.expiryTime : undefined;
|
||||
this.expired = entry.times.expires && entry.times.expiryTime <= new Date();
|
||||
this.historyLength = entry.history.length;
|
||||
this._buildSearchText();
|
||||
this._buildSearchTags();
|
||||
this._buildSearchColor();
|
||||
},
|
||||
|
||||
_buildSearchText: function() {
|
||||
var text = '';
|
||||
_.forEach(this.entry.fields, function(value) {
|
||||
if (typeof value === 'string') {
|
||||
text += value.toLowerCase() + '\n';
|
||||
}
|
||||
});
|
||||
this.entry.tags.forEach(function(tag) {
|
||||
text += tag.toLowerCase() + '\n';
|
||||
});
|
||||
this.attachments.forEach(function(att) {
|
||||
text += att.title.toLowerCase() + '\n';
|
||||
});
|
||||
this.searchText = text;
|
||||
},
|
||||
|
||||
_buildSearchTags: function() {
|
||||
this.searchTags = this.entry.tags.map(function(tag) { return tag.toLowerCase(); });
|
||||
},
|
||||
|
||||
_buildSearchColor: function() {
|
||||
this.searchColor = this.color;
|
||||
},
|
||||
|
||||
_iconFromId: function(id) {
|
||||
return IconMap[id];
|
||||
},
|
||||
|
||||
_colorToModel: function(color) {
|
||||
return color ? Color.getNearest(color) : null;
|
||||
},
|
||||
|
||||
_fieldsToModel: function(fields) {
|
||||
return _.omit(fields, this.buildInFields);
|
||||
},
|
||||
|
||||
_attachmentsToModel: function(binaries) {
|
||||
var att = [];
|
||||
_.forEach(binaries, function(data, title) {
|
||||
att.push(AttachmentModel.fromAttachment({ data: data, title: title }));
|
||||
}, this);
|
||||
return att;
|
||||
},
|
||||
|
||||
_fillInTrash: function() {
|
||||
this.deleted = false;
|
||||
if (this.file.db.meta.recycleBinEnabled) {
|
||||
var trashGroupId = this.file.db.meta.recycleBinUuid.id;
|
||||
for (var group = this.group; group; group = group.group) {
|
||||
if (group.id === trashGroupId) {
|
||||
this.deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_entryModified: function() {
|
||||
if (!this.unsaved) {
|
||||
this.unsaved = true;
|
||||
this.entry.pushHistory();
|
||||
this.file.setModified();
|
||||
}
|
||||
this.entry.times.update();
|
||||
},
|
||||
|
||||
matches: function(filter) {
|
||||
return (!filter.tagLower || this.searchTags.indexOf(filter.tagLower) >= 0) &&
|
||||
(!filter.textLower || this.searchText.indexOf(filter.textLower) >= 0) &&
|
||||
(!filter.color || filter.color === true && this.searchColor || this.searchColor === filter.color);
|
||||
},
|
||||
|
||||
setColor: function(color) {
|
||||
this._entryModified();
|
||||
this.entry.bgColor = Color.getKnownBgColor(color);
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
setIcon: function(iconId) {
|
||||
this._entryModified();
|
||||
this.entry.icon = iconId;
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
setExpires: function(dt) {
|
||||
this._entryModified();
|
||||
this.entry.times.expiryTime = dt instanceof Date ? dt : undefined;
|
||||
this.entry.times.expires = !!dt;
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
setTags: function(tags) {
|
||||
this._entryModified();
|
||||
this.entry.tags = tags;
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
setField: function(field, val) {
|
||||
this._entryModified();
|
||||
if (val || this.buildInFields.indexOf(field) >= 0) {
|
||||
this.entry.fields[field] = val;
|
||||
} else {
|
||||
delete this.entry.fields[field];
|
||||
}
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
hasField: function(field) {
|
||||
return this.entry.fields.hasOwnProperty(field);
|
||||
},
|
||||
|
||||
addAttachment: function(name, data) {
|
||||
this._entryModified();
|
||||
this.entry.binaries[name] = kdbxweb.ProtectedValue.fromBinary(data);
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
removeAttachment: function(name) {
|
||||
this._entryModified();
|
||||
delete this.entry.binaries[name];
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
getHistory: function() {
|
||||
var history = this.entry.history.map(function(rec) {
|
||||
return EntryModel.fromEntry(rec, this.group, this.file);
|
||||
}, this);
|
||||
history.push(this);
|
||||
history.sort(function(x, y) { return x.updated - y.updated; });
|
||||
return history;
|
||||
},
|
||||
|
||||
deleteHistory: function(historyEntry) {
|
||||
var ix = this.entry.history.indexOf(historyEntry);
|
||||
if (ix >= 0) {
|
||||
this.entry.history.splice(ix, 1);
|
||||
}
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
revertToHistoryState: function(historyEntry) {
|
||||
var ix = this.entry.history.indexOf(historyEntry);
|
||||
if (ix < 0) {
|
||||
return;
|
||||
}
|
||||
this.entry.pushHistory();
|
||||
this.unsaved = true;
|
||||
this.file.setModified();
|
||||
this.entry.fields = {};
|
||||
this.entry.binaries = {};
|
||||
this.entry.copyFrom(historyEntry);
|
||||
this._entryModified();
|
||||
this._fillByEntry();
|
||||
},
|
||||
|
||||
discardUnsaved: function() {
|
||||
if (this.unsaved) {
|
||||
this.unsaved = false;
|
||||
var historyEntry = this.entry.history.pop();
|
||||